• redis scan & kyes p*


    场景:

    一次性取出redis一个field中的所有key,并遍历。

    方案一:(生产环境禁用keys)

    使用redisTemplate.opsForHash().keys("filed")

    前期数据量少,感j觉不到性能问题。数据量上去后keys方法严重消耗CPU,导致服务"假死", 其他连接阻塞   ,    一般在生产环境禁用keys方法。

    方案二:

    使用redisTemplate游标分批次获取

    使用scan主要两个参数:match和count。

    match:key的正则表达式

    count:每次扫描的记录数。值越小,扫描次数越过、越耗时。建议设置在1000-10000

    Spring RedisTemplate实现scan

    1. hscan sscan zscan

    • 例子中的"field"是值redis的key,即从key为"field"中的hash中查找
    • redisTemplate的opsForHash,opsForSet,opsForZSet 可以 分别对应 sscan、hscan、zscan
    • 也可以使用 (JedisCommands) connection.getNativeConnection() 的 hscan、sscan、zscan 方法实现cursor遍历,
    try {
        Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan("field",
        ScanOptions.scanOptions().match("*").count(1000).build());
        while (cursor.hasNext()) {
            Map.Entry<Object,Object> entry = cursor.next();
            Object key = entry.getKey();
            Object valueSet = entry.getValue();
        }
        //关闭cursor
        cursor.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    • cursor.close(); 游标一定要关闭,不然连接会一直增长;
    • 可以使用client lists info clients info stats 命令查看客户端连接状态,会发现scan操作一直存在
     
     

    2. scan

    2.1 使用spring-data-redis封装好的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 使用redis.clients.jedis的MultiKeyCommands,自己循环scan

    • 获取 connection.getNativeConnectionconnection.getNativeConnection() 实际对象是Jedis(debug可以看出) ,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); // 这个不是返回结果的数量,应该是每次scan的数量
                ScanResult<String> scan = multiKeyCommands.scan("0", scanParams);
                while (null != scan.getStringCursor()) {
                    keys.addAll(scan.getResult()); // 这一次scan match到的结果
                    if (!StringUtils.equals("0", scan.getStringCursor())) { // 不断拿着新的cursor scan,最终会拿到所有匹配的值
                        scan = multiKeyCommands.scan(scan.getStringCursor(), scanParams);
                        continue;
                    } else {
                        break;
                    }
                }
    
                return keys;
            });
        }
    作者:南岩飞雪
    链接:https://www.jianshu.com/p/4c842c41ba41
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    多线程的同步锁和死锁
    多线程同步
    oracle11g导出表时会发现少表,空表导不出解决方案
    GET和POST两种基本请求方法的区别
    数据库优化
    JavaScript中的基本数据类型
    Spring Data Jpa简单了解
    单例和多例详解
    jsp九大内置对象
    JavaEE 前后端分离以及优缺点
  • 原文地址:https://www.cnblogs.com/lshan/p/15309080.html
Copyright © 2020-2023  润新知