代わりにRedisTemplateでスキャン指示キーを使用します

keys *本番環境でこのコマンドを台無しにしないでください。特に、膨大なデータの状況。そのための鍵は、Redisのロックをリードし、Redisの上のCPU使用率が増加します。多くの企業は、このコマンドの操作や保守を禁止されています

あなたは彼らが必要とするキーと一致、キーをスキャンする必要がある場合は、使用することができますscanコマンドを

scan達成するためのヘルパー事業

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Component
public class RedisHelper {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    /**
     * scan 实现
     * @param pattern   表达式
     * @param consumer  对迭代到的key进行操作
     */
    public void scan(String pattern, Consumer<byte[]> consumer) {
        this.stringRedisTemplate.execute((RedisConnection connection) -> {
            try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
                cursor.forEachRemaining(consumer);
                return null;
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 获取符合条件的key
     * @param pattern   表达式
     * @return
     */
    public List<String> keys(String pattern) {
        List<String> keys = new ArrayList<>();
        this.scan(pattern, item -> {
            //符合条件的key
            String key = new String(item,StandardCharsets.UTF_8);
            keys.add(key);
        });
        return keys;
    }
}

しかし、問題があります:、カーソルを移動することはできません一度だけスキャンすることができ、そしてRedisのリンクを、エラーを起こしやすいです

まず、次のスキャンを理解HSCAN、SSCAN、zscan

http://doc.redisfans.com/key/scan.html

なぜキー危険な?

  • 操作のキー一時データベースがロックされている原因となります、他の要求がブロックされます。事業の大ボリュームは間違って行くだろう

春RedisTemplate实现スキャン

1. HSCAN SSCAN zscan

  • 「フィールド」の一例では、ルックアップ即ちからハッシュキーの「フィールド」を、Redisのキーの値であります
  • redisTemplate opsForHash、opsForSet、opsForZSet SSCAN対応することができる、HSCAN、zscan
  • カーソルを保持トラバーサルがないので、このオンラインコースの例としては、ちょうどチェックをスキャンし、実際には適切ではありません
  • 使用して、怠惰することができ.count(Integer.MAX_VALUE)、すべてを一度に確認してください。しかし、この子とキーので、それはどのような違いを作るのですか?変な顔&顔を疑います
  • あなたが使用されてもよい(JedisCommands) connection.getNativeConnection()以下のセクション2.2を参照して、SSCAN、zscanカーソルトラバーサル実装方法のHSCANを
try {
    Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan("field",
    ScanOptions.scanOptions().match("*").count(1000).build());
    while (cursor.hasNext()) {
        Object key = cursor.next().getKey();
        Object valueSet = cursor.next().getValue();
    }
    //关闭cursor
    cursor.close();
} catch (IOException e) {
    e.printStackTrace();
}
  • cursor.close();カーソルは、そうでない場合は、接続が成長する、閉じていなければなりません。あなたが使用することができclient lists``info clients``info stats、クライアントの接続ステータスを表示するコマンドを、あなたはスキャン操作があったでしょう
  • 我々は通常使用redisTemplate.executeは、接続を解放するためのイニシアチブを取るでしょう、あなたは、ソースコードの確認を表示することができます
client list
......
id=1531156 addr=xxx:55845 fd=8 name= age=80 idle=11 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=scan
......
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>, boolean, boolean)

finally {
    RedisConnectionUtils.releaseConnection(conn, factory);
}

2.スキャン

この例のほとんどは2.1インターネット

  • このconnection.scanは、カーソルを移動できませんでした、あなたは一度だけスキャンすることができます
public Set<String> scan(String matchKey) {
    Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
        Set<String> keysTmp = new HashSet<>();
        Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match("*" + matchKey + "*").count(1000).build());
        while (cursor.hasNext()) {
            keysTmp.add(new String(cursor.next()));
        }
        return keysTmp;
    });

    return keys;
}

2.2 MultiKeyCommands

  • 得るconnection.getNativeConnection; connection.getNativeConnection()実際のオブジェクトがJedis(デバッグを見ることができる)であり、Jedisは、多くのインターフェイスを実装します
public class Jedis extends BinaryJedis implements JedisCommands, MultiKeyCommands, AdvancedJedisCommands, ScriptingCommands, BasicCommands, ClusterCommands, SentinelCommands 
  • scan.getStringCursor()が存在し、0でない場合、取得は、カーソルを移動させるようになっています
public Set<String> scan(String key) {
    return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
        Set<String> keys = Sets.newHashSet();

        JedisCommands commands = (JedisCommands) connection.getNativeConnection();
        MultiKeyCommands multiKeyCommands = (MultiKeyCommands) commands;

        ScanParams scanParams = new ScanParams();
        scanParams.match("*" + key + "*");
        scanParams.count(1000);
        ScanResult<String> scan = multiKeyCommands.scan("0", scanParams);
        while (null != scan.getStringCursor()) {
            keys.addAll(scan.getResult());
            if (!StringUtils.equals("0", scan.getStringCursor())) {
                scan = multiKeyCommands.scan(scan.getStringCursor(), scanParams);
                continue;
            } else {
                break;
            }
        }

        return keys;
    });
}

発散的思考

カーソルが、それはRedisのある、ブロックされている最後に、閉じません

  • 試験中、私は基本的には、カーソルをクローズしませんでした、ダーススキャン操作を開始した、後続の要求が立ち往生しています

Redisのサイド分析

  • client lists``info clients``info statsビューは、
    ブロッキングが存在しない、唯一のダースに接続の数を発見し、接続を拒否しました
  • config get maxclients最大接続数は10000 Redisのクエリを許可しました
1) "maxclients"
2) "10000"`
  • redis-cli他のマシンでも動作直接ログインすることができます

要約すると、自身のRedisは滞留していません

アプリケーション側

  • netstat接続があった;、6333はRedisのポートで接続し、Redisのを確認してください
➜  ~ netstat -an | grep 6333
netstat -an | grep 6333
tcp4       0      0  xx.xx.xx.aa.52981      xx.xx.xx.bb.6333     ESTABLISHED
tcp4       0      0  xx.xx.xx.aa.52979      xx.xx.xx.bb.6333     ESTABLISHED
tcp4       0      0  xx.xx.xx.aa.52976      xx.xx.xx.bb.6333     ESTABLISHED
tcp4       0      0  xx.xx.xx.aa.52971      xx.xx.xx.bb.6333     ESTABLISHED
tcp4       0      0  xx.xx.xx.aa.52969      xx.xx.xx.bb.6333     ESTABLISHED
tcp4       0      0  xx.xx.xx.aa.52967      xx.xx.xx.bb.6333     ESTABLISHED
tcp4       0      0  xx.xx.xx.aa.52964      xx.xx.xx.bb.6333     ESTABLISHED
tcp4       0      0  xx.xx.xx.aa.52961      xx.xx.xx.bb.6333     ESTABLISHED
  • jstackビューアプリケーションスタック情報
    多くのスレッドを待ち見つけ、接続のRedisで取得したすべてが
    そう基本的にはRedisのスレッドプールのアプリケーションがいっぱいであると結論付けることができます
"http-nio-7007-exec-2" #139 daemon prio=5 os_prio=31 tid=0x00007fda36c1c000 nid=0xdd03 waiting on condition [0x00007000171ff000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006c26ef560> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at org.apache.commons.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:590)
        at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:441)
        at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:362)
        at redis.clients.util.Pool.getResource(Pool.java:49)
        at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226)
        at redis.clients.jedis.JedisPool.getResource(JedisPool.java:16)
        at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:276)
        at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:469)
        at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:132)
        at org.springframework.data.redis.core.RedisTemplate.executeWithStickyConnection(RedisTemplate.java:371)
        at org.springframework.data.redis.core.DefaultHashOperations.scan(DefaultHashOperations.java:244)

要約すると、アプリケーション側が貼り付けられています

フォロー

  • 正午の後、Redisのは、client listsアプリケーションのスレッドが静止状態でスタックしている、スキャン接続がまだリリースされていない表示します
  • 検査はconfig get timeout、Redisのタイムアウトが提供されていない、することができるconfig set timeout xxx、しかし、Redisのタイムアウトは、Redisの接続が解除され、同じアプリケーションをか立ち往生;秒で、提供します
1) "timeout"
2) "0"
  • netstatビューとRedisの接続、6333はRedisのポートです。ESTABLISHEDになっCLOSE_WAITからの接続。
  • jstackそして、元のカードのようなパフォーマンスJedisConnectionFactory.getConnection
➜  ~ netstat -an | grep 6333
netstat -an | grep 6333
tcp4       0      0  xx.xx.xx.aa.52981      xx.xx.xx.bb.6333     CLOSE_WAIT
tcp4       0      0  xx.xx.xx.aa.52979      xx.xx.xx.bb.6333     CLOSE_WAIT
tcp4       0      0  xx.xx.xx.aa.52976      xx.xx.xx.bb.6333     CLOSE_WAIT
tcp4       0      0  xx.xx.xx.aa.52971      xx.xx.xx.bb.6333     CLOSE_WAIT
tcp4       0      0  xx.xx.xx.aa.52969      xx.xx.xx.bb.6333     CLOSE_WAIT
tcp4       0      0  xx.xx.xx.aa.52967      xx.xx.xx.bb.6333     CLOSE_WAIT
tcp4       0      0  xx.xx.xx.aa.52964      xx.xx.xx.bb.6333     CLOSE_WAIT
tcp4       0      0  xx.xx.xx.aa.52961      xx.xx.xx.bb.6333     CLOSE_WAIT
  • リコールTCP 4は波打ち
    、確立された接続が確立されたことを示し
    CLOSE_WAITは、リモート電卓は、接続を閉じるソケット接続が待っている閉じ表し
    およびに沿った現象
  • Redisの接続プールの構成
    によれば、上記netstat -an基本的に、接続プールのサイズをRedisの8れているかを決定することができ、指定されていない場合、ように構成されたコードを結合、デフォルトは確か8
redis.clients.jedis.JedisPoolConfig
private int maxTotal = 8;
private int maxIdle = 8;
private int minIdle = 0;
  • 接続それの大きなプールを構成する方法?
    A.元の構成
@Bean
public RedisConnectionFactory redisConnectionFactory() {
    RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
    redisStandaloneConfiguration.setHostName(redisHost);
    redisStandaloneConfiguration.setPort(redisPort);
    redisStandaloneConfiguration.setPassword(RedisPassword.of(redisPasswd));
    JedisConnectionFactory cf = new JedisConnectionFactory(redisStandaloneConfiguration);
    cf.afterPropertiesSet();
    return cf;
}

readTimeout、のconnecttimeoutが指定されていない、デフォルト値2000ミリ秒があります

org.springframework.data.redis.connection.jedis.JedisConnectionFactory.MutableJedisClientConfiguration
private Duration readTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT);
private Duration connectTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT); 

構成Bを変更した後

    1. 構成:インターフェイスの一部は非推奨しています
@Bean
public RedisConnectionFactory redisConnectionFactory() {
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxTotal(16); // --最多可以建立16个连接了
    jedisPoolConfig.setMaxWaitMillis(10000); // --10s获取不到连接池的连接,
                                             // --直接报错Could not get a resource from the pool

    jedisPoolConfig.setMaxIdle(16);
    jedisPoolConfig.setMinIdle(0);

    JedisConnectionFactory cf = new JedisConnectionFactory(jedisPoolConfig);
    cf.setHostName(redisHost); // -- @Deprecated 
    cf.setPort(redisPort); // -- @Deprecated 
    cf.setPassword(redisPasswd); // -- @Deprecated 
    cf.setTimeout(30000); // -- @Deprecated 貌似没生效,30s超时,没有关闭连接池的连接;
                          // --redis没有设置超时,会一直ESTABLISHED;redis设置了超时,且超时之后,会一直CLOSE_WAIT

    cf.afterPropertiesSet();
    return cf;
}
    1. これは、友人を見つけるためにグループを設定するための新しい方法である、同じ効果:方法の設定
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(redisHost);
redisStandaloneConfiguration.setPort(redisPort);
redisStandaloneConfiguration.setPassword(RedisPassword.of(redisPasswd));

JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(16);
jedisPoolConfig.setMaxWaitMillis(10000);
jedisPoolConfig.setMaxIdle(16);
jedisPoolConfig.setMinIdle(0);

cf = new JedisConnectionFactory(redisStandaloneConfiguration, JedisClientConfiguration.builder()
        .readTimeout(Duration.ofSeconds(30))
        .connectTimeout(Duration.ofSeconds(30))
        .usePooling().poolConfig(jedisPoolConfig).build());

参照

redistemplate-カーソルスキャン注意事項

RedisTemplateアクセスRedisのデータ構造を使用する方法

Redisのは、キーとスキャンで使用されています

スキャンコマンドのRedisの深い理解

春・ブート・スターター・Redisの詳細構成

オンライン調査の多数CLOSE_WAIT理由

jedisPoolのスタンドアロンRedisのバージョンを構成する方法

JedisはRedisのクライアントCLOSE_WAITのバグが大幅に増加につながる、非標準を使用します

おすすめ

転載: www.cnblogs.com/alterem/p/11433340.html