当前位置: 首页 > article >正文

Redis批量获取缓存的方法

使用multiGet方法

优点:简单易用,适用于获取少量键的场景。

缺点:当获取的键数量较多时,可能会因为网络延迟导致性能下降。此外,如果某个键不存在,对应的返回值会是null,需要额外处理。

其他说明:

multiGet方法适合处理的键的数量并没有一个严格的限制,但是它的性能会随着键的数量增加而逐渐下降。这是因为multiGet方法会将所有的键在一次请求中发送到Redis服务器,如果键的数量非常多,可能会导致以下几个问题:

  1. 网络延迟:如果键的数量很多,那么构建请求和接收响应所需的时间会增加,这可能导致网络延迟成为性能瓶颈。
  2. 响应体大小:如果这些键对应的值都比较大,那么一次multiGet请求的响应体可能会非常大,这不仅会占用大量的网络带宽,还可能对客户端和服务器的内存造成压力。
  3. Redis服务器压力:如果大量的multiGet请求同时发送到Redis服务器,尤其是在键的数量很多的情况下,可能会对服务器造成较大的压力,影响其他操作的性能。
  4. 超时风险:在高负载或者网络条件不佳的情况下,大量的键可能会导致multiGet请求超时。

在实践中,如果需要获取的键的数量在几十个到几百个,通常使用multiGet方法不会出现性能问题。但是,如果键的数量达到几千个或者更多,可能就需要考虑其他策略,比如:

  1. 分批获取:将大量的键分成多个批次,每批次使用multiGet获取一部分键的值。
  2. 使用scan命令:对于需要匹配模式的大量键,使用scan命令进行迭代获取。
  3. 使用管道:对于写入操作,可以使用管道(pipeline)来减少网络往返次数,提高效率。

总的来说,是否使用multiGet方法以及适合处理多少键,需要根据实际的应用场景和性能测试来决定。在不确定的情况下,建议进行性能测试,以确定在特定的环境下最佳的操作方式。

/**
 * 测试StringRedisTemplate.opsForValue().multiGet方法
 * 
 * @author 付聪
 * @time 2024-10-11 15:36:26
 */
@Test
public void testMultiGet() {
    List<String> keys = Arrays.asList("test-multiGet:key1", "test-multiGet:key2", "test-multiGet:key3");
    Integer i = 1;
    for (String key : keys) {
        stringRedisTemplate.opsForValue().set(key, "test-multiGet:value" + i++, 1, TimeUnit.HOURS);
    }
    List<String> values = stringRedisTemplate.opsForValue().multiGet(keys);
    PrintUtil.println(StrUtil.format("values:{}", values));
}

使用executePipelined方法

优点:可以显著减少网络往返次数,提高批量操作的性能。适用于执行大量独立的写入或读取操作。

缺点:编程模型相对复杂,需要手动管理命令的发送和结果的收集。此外,如果操作非常频繁,可能会对Redis服务器造成压力。

/**
 * 测试StringRedisTemplate.executePipelined方法
 *
 * @author 付聪
 * @time 2024-10-11 15:36:26
 */
@Test
public void testExecutePipelined() {
    List<String> keys = Arrays.asList("test-executePipelined:key1", "test-executePipelined:key2", "test-executePipelined:key3");
    Integer i = 1;
    for (String key : keys) {
        stringRedisTemplate.opsForValue().set(key, "test-executePipelined:value" + i++, 1, TimeUnit.HOURS);
    }
    /**
     *
     * 该函数使用stringRedisTemplate执行管道操作,批量获取多个键对应的值。具体步骤如下:
     * 使用executePipelined方法执行管道操作。
     * 定义一个RedisCallback匿名类,重写doInRedis方法。
     * RedisCallback#doInRedis是Spring Data Redis中的一个接口方法,其作用是在Redis连接上执行自定义的Redis操作。具体来说:
     * 参数:RedisConnection connection,表示当前的Redis连接。
     * 返回值:可以是任意类型,通常用于返回操作的结果。
     * 用途:
     * 提供对底层Redis连接的直接访问,允许执行更复杂的或自定义的Redis命令。
     * 可以在事务或管道中使用,提高性能和效率。
     * 通过实现doInRedis方法,开发者可以在Redis连接上执行任意的Redis命令,从而实现更灵活的Redis操作。
     * 在doInRedis方法中,遍历keys列表,对每个键调用connection.get(key.getBytes())获取其值。
     * 返回null,因为doInRedis方法的返回值不会影响最终结果。
     * executePipelined方法返回一个包含所有获取到的值的列表values。
     *
     */
    List<Object> values = stringRedisTemplate.executePipelined(new RedisCallback<String>() {
        @Override
        public String doInRedis(RedisConnection connection) throws DataAccessException {
            for (String key : keys) {
                connection.get(key.getBytes());
            }
            // 返回null,因为结果已经通过executePipelined返回。
            return null;
        }
    });
    PrintUtil.println(StrUtil.format("values:{}", values));
}

使用管道可以减少网络延迟,提高Redis的操作效率。但是,需要注意的是,管道中的命令不能包含事务性的命令,如MULTIEXEC,否则会抛出异常 。

使用scan方法

优点:可以迭代地获取匹配特定模式的键,适用于需要匹配大量键的场景,且不会阻塞Redis服务器。

缺点scan命令返回的是游标,需要多次迭代才能获取所有匹配的键,且每次迭代返回的键数量有限,可能需要多次迭代才能获取全部数据。此外,scan命令在处理大量数据时效率较低。

/**
 * 测试scan方法
 *
 * @author 付聪
 * @time 2024-10-18 10:09:38
 */
@Test
public void testScan() {
    List<String> keys = Arrays.asList("test-scan:key1", "test-scan:key2", "test-scan:key3");
    Integer i = 1;
    for (String key : keys) {
        stringRedisTemplate.opsForValue().set(key, "test-scan:value" + i++, 1, TimeUnit.HOURS);
    }
    /**
     *
     * 该函数通过stringRedisTemplate执行一个Redis操作,具体功能如下:
     * (RedisCallback<Set<String>>) 是一个类型转换操作,作用如下:
     * 将lambda表达式转换为RedisCallback<Set<String>>类型。
     * RedisCallback是Spring Data Redis提供的一个接口,用于执行自定义的Redis操作。
     * 通过这个转换,可以将lambda表达式传递给stringRedisTemplate.execute方法,从而在Redis连接上下文中执行自定义的逻辑。
     * 使用connection.scan方法扫描Redis中匹配模式test-scan:*的所有键。
     * count(100) 的作用是:
     * 设置每次扫描操作返回的最大元素数量为100。
     * 这个参数可以控制每次scan操作返回的键的数量,减少单次操作的数据量,提高性能和响应速度。
     * 如果不设置count,默认值通常为10。设置为100可以在一次扫描中返回更多的键,减少总的扫描次数。
     * 将扫描结果中的每个键转换为字符串并添加到result集合中。
     * 最终返回包含所有匹配键的字符串集合。
     *
     */
    Set<String> values = stringRedisTemplate.execute((RedisCallback<Set<String>>) connection -> {
        Set<String> result = new HashSet<>();
        Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match("test-scan:*").count(100).build());
        while (cursor.hasNext()) {
            result.add(new String(cursor.next()));
        }
        return result;
    });
    PrintUtil.println(StrUtil.format("values:{}", values));
}

使用scan方法可以避免keys命令可能带来的性能问题,但是需要注意调节count的值,避免产生和keys命令类似的效果,可能会阻塞Redis 。

总结

每种方法都有其适用场景,可以根据实际需求和性能测试结果选择最合适的方法。


http://www.kler.cn/news/364396.html

相关文章:

  • 戴维南,叠加,稳态笔记
  • ActivationType, Pool, ModelType(helpers文件中的classes.py)
  • 全面指南:在 Vue 中优雅封装 HTML <dialog> 组件
  • WISE:重新思考大语言模型的终身模型编辑与知识记忆机制
  • JAVA面试八股文(五)
  • STM32之基本定时器TIM6和TIM7
  • 【加密系统】华企盾DSC服务台提示:请升级服务器,否则可能导致客户端退回到旧服务器的版本
  • js基础入门篇
  • 【计算机网络 - 基础问题】每日 3 题(五十六)
  • 双十一母婴用品排行榜推荐出炉!建议收藏!看宝妈要买哪些东西
  • NewStarCTF 2023 公开赛道 Web week1-week2
  • 安全见闻(3)
  • 51单片机快速入门之 IIC I2C通信
  • 昇思MindSpore进阶教程--安装常见问题(中)
  • Modbus转IEC61850网关iGate-850实现电力系统采集电力仪表
  • Facebook区块链生态系统:去中心化社交平台的未来
  • docker XML详解
  • 深度学习 简易环境安装(不含Anaconda)
  • Zypher Network Layer3 主网上线,“宝藏方舟”活动是亮点
  • React综合指南(一)
  • 微服务的一些基本概念
  • 【Java】ArrayList相关操作及其案例
  • 安全见闻(4)
  • 什么是恶意爬虫,有什么应对措施
  • iOS 18.2开发者预览版 Beta 1版本发布,欧盟允许卸载应用商店
  • 字符串使用方法: