redis 数据读取方式性能对比

redis 数据读取方式性能对比

 

以下是快速获取 Redis 3 号数据库所有键值对的 4 种高效方案,根据数据量级和性能需求选择最适合的方式:


方案 1:SCAN + Pipeline 批量获取(推荐)

适用场景:大数据量(万级以上),不阻塞 Redis

java
 
/**
 * 获取指定数据库的所有键值对(SCAN+Pipeline 安全迭代)
 * @param dbIndex 数据库索引(如3)
 * @return Map<key, value>(值不存在的key不会包含在结果中)
 */
public static Map<String, String> getAllValues(int dbIndex) {
    Map<String, String> result = new HashMap<>();
    try (Jedis jedis = selectDb(getJedis(), dbIndex)) {
        String cursor = "0";
        ScanParams scanParams = new ScanParams().count(500); // 每批扫描500个key
        
        do {
            // 1. SCAN获取一批key
            ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
            List<String> keys = scanResult.getResult();
            
            // 2. Pipeline批量获取值
            Pipeline pipeline = jedis.pipelined();
            Map<String, Response<String>> responses = new HashMap<>();
            for (String key : keys) {
                responses.put(key, pipeline.get(key));
            }
            pipeline.sync();
            
            // 3. 解析结果
            for (Map.Entry<String, Response<String>> entry : responses.entrySet()) {
                String value = entry.getValue().get();
                if (value != null) {
                    result.put(entry.getKey(), value);
                }
            }
            
            cursor = scanResult.getCursor();
        } while (!cursor.equals("0"));
    }
    return result;
}

优点

  • 无阻塞,适合生产环境

  • 内存友好(分批处理)
    调用方式

java
 
Map<String, String> allData = JedisUtil.getAllValues(3);

方案 2:KEYS + MGET(慎用!)

适用场景:开发测试环境,数据量小(<1万)

java
 
public static Map<String, String> getAllValuesUnsafe(int dbIndex) {
    try (Jedis jedis = selectDb(getJedis(), dbIndex)) {
        // 1. 获取所有key(可能阻塞Redis!)
        Set<String> keys = jedis.keys("*");
        
        // 2. 批量获取值
        List<String> values = jedis.mget(keys.toArray(new String[0]));
        
        // 3. 构建键值对
        Map<String, String> result = new HashMap<>();
        Iterator<String> keyIter = keys.iterator();
        Iterator<String> valueIter = values.iterator();
        while (keyIter.hasNext() && valueIter.hasNext()) {
            String value = valueIter.next();
            if (value != null) {
                result.put(keyIter.next(), value);
            }
        }
        return result;
    }
}

警告

  • KEYS * 会阻塞 Redis,生产环境禁用!


方案 3:Lua脚本(原子性操作)

适用场景:需要原子性获取全量数据

java
 
public static Map<String, String> getAllByLua(int dbIndex) {
    String luaScript = ""
        + "local keys = redis.call('KEYS', '*') "
        + "local result = {} "
        + "for i, key in ipairs(keys) do "
        + "    result[i] = {key, redis.call('GET', key)} "
        + "end "
        + "return result";
    
    try (Jedis jedis = selectDb(getJedis(), dbIndex)) {
        List<?> rawResult = (List<?>) jedis.eval(luaScript);
        Map<String, String> result = new HashMap<>();
        for (Object item : rawResult) {
            List<?> pair = (List<?>) item;
            if (pair.get(1) != null) {
                result.put((String) pair.get(0), (String) pair.get(1));
            }
        }
        return result;
    }
}

特点

  • 原子性操作

  • 仍会阻塞 Redis,仅适合特殊场景


方案 4:异步处理(超大数据量)

java
 
public static CompletableFuture<Map<String, String>> getAllValuesAsync(int dbIndex) {
    return CompletableFuture.supplyAsync(() -> {
        Map<String, String> result = new ConcurrentHashMap<>();
        try (Jedis jedis = selectDb(getJedis(), dbIndex)) {
            String cursor = "0";
            ScanParams scanParams = new ScanParams().count(200);
            
            do {
                ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
                List<String> keys = scanResult.getResult();
                
                // 并行处理每批key
                keys.parallelStream().forEach(key -> {
                    String value = jedis.get(key);
                    if (value != null) {
                        result.put(key, value);
                    }
                });
                
                cursor = scanResult.getCursor();
            } while (!cursor.equals("0"));
        }
        return result;
    }, ForkJoinPool.commonPool()); // 使用公共线程池
}

适用场景

  • 超大数据量(百万级)

  • 允许最终一致性

posted @ 2025-04-22 09:13  锐洋智能  阅读(68)  评论(0)    收藏  举报