Redis如何使用Pipeline实现批处理操作

 更新时间:2025年02月17日 10:03:55   作者:记得开心一点嘛  
Redis Pipeline 是一种优化 Redis 操作的机制,通过将多个命令打包发送到 Redis 服务器,减少客户端与服务器之间的网络往返时间,本文主要来聊聊Redis如何使用Pipeline实现批处理操作,需要的可以了解下

在正常情况下,我们每次发送 Redis 命令时,客户端会等待 Redis 服务器的响应,直到接收到结果后,才会发送下一个命令。这种方式虽然保证了操作的顺序性,但在执行大量命令时会产生很大的网络延迟。

通过 Pipeline 技术,我们的客户端可以将多个命令同时发送给 Redis 服务器,并且不需要等待每个命令的返回结果,直到所有命令都被执行完毕,客户端再一起获取返回值。这样能减少每个命令的等待时间,大幅提高执行效率。

Redis Pipeline 是一种优化 Redis 操作的机制,通过将多个命令打包发送到 Redis 服务器,减少客户端与服务器之间的网络往返时间(RTT),从而显著提升性能。 

在默认情况下,Redis 客户端与服务器之间的通信是请求-响应模式,即:

1客户端发送一个命令到服务器。

2.服务器执行命令并返回结果。

3.客户端等待响应后再发送下一个命令。

这种模式在命令数量较少时没有问题,但在需要执行大量命令时,网络往返时间(RTT)会成为性能瓶颈。所以我们需要实现下面目的:

1.将多个命令打包发送到服务器。

2.服务器依次执行这些命令,并将结果一次性返回给客户端。

3.减少网络开销,提升性能。

以下是一个简单的 Java 示例,展示了如何使用 Jedis(Redis 的一个 Java 客户端)执行 Pipeline:

注意:批处理时不建议一次携带太多命令,并且Pipeline的多个命令之间不具备原子性。

// 创建 Jedis 实例
Jedis jedis = new Jedis("localhost", 6379);
 
// 使用 pipelining 方式批量执行命令
Pipeline pipeline = jedis.pipelined();
 
// 批量操作:使用 pipeline 来缓存命令
for (int i = 0; i < 1000; i++) {
    pipeline.set("key" + i, "value" + i);
}
 
// 同步执行所有命令
pipeline.sync();

pipelined() 方法: 创建一个 Pipeline 对象,它缓存所有要执行的命令。

批量设置命令: 通过 pipeline.set() 将多个 SET 命令放入管道中,但命令并不会立即执行。

sync() 方法: 通过调用 sync() 方法,客户端将会把所有缓存的命令一次性发送给 Redis,并等待它们完成执行。

但是这些都是在单机模式下的批处理,那对于集群来说该如何使用呢?

向MSet或Pipeline这样的批处理需要在一次请求中携带多条命令,而此时如何Redis是一个集群,那批处理命令的多个key必须落在同一个插槽中,否则就会导致执行失败。

一般推荐使用并行插槽来解决,如果使用hash_tag,可能会出现大量的key分配同一插槽导致数据倾斜,而并行插槽不会。

那么这里我们模拟一下并行插槽实现:

将多个键值对按照Redis集群的槽位进行分组,然后分别使用jedisCluster.mset()方法按组设置键值对。

public class JedisClusterTest {
 
    // 声明一个JedisCluster对象,用于与Redis集群进行交互
    private JedisCluster jedisCluster;
 
    // 在每个测试方法执行之前,初始化JedisCluster连接
    @BeforeEach
    void setUp() {
        // 配置Jedis连接池
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(8);
        poolConfig.setMaxIdle(8);
        poolConfig.setMinIdle(0);
        poolConfig.setMaxWaitMillis(1000);
 
        // 创建一个HashSet,用于存储Redis集群的节点信息
        HashSet<HostAndPort> nodes = new HashSet<>();
        // 添加Redis集群的节点信息(IP和端口)
        nodes.add(new HostAndPort("192.168.150.101", 7001));
        nodes.add(new HostAndPort("192.168.150.101", 7002));
        nodes.add(new HostAndPort("192.168.150.101", 7003));
        nodes.add(new HostAndPort("192.168.150.101", 8001));
        nodes.add(new HostAndPort("192.168.150.101", 8002));
        nodes.add(new HostAndPort("192.168.150.101", 8003));
 
        // 使用配置的连接池和节点信息初始化JedisCluster对象
        jedisCluster = new JedisCluster(nodes, poolConfig);
    }
 
    // 测试方法:使用mset命令一次性设置多个键值对
    @Test
    void testMSet() {
        // 使用JedisCluster的mset方法,一次性设置多个键值对
        // 但是jedisCluster默认是无法解决批处理问题的,需要我们手动解决
        jedisCluster.mset("name", "Jack", "age", "21", "sex", "male");
    }
 
    // 测试方法:使用mset命令按槽位分组设置多个键值对
    @Test
    void testMSet2() {
        // 创建一个HashMap,用于存储多个键值对
        Map<String, String> map = new HashMap<>(3);
        map.put("name", "Jack");
        map.put("age", "21");
        map.put("sex", "Male");
 
        // 将map中的键值对按照Redis集群的槽位进行分组
        Map<Integer, List<Map.Entry<String, String>>> result = map.entrySet()
                .stream()
                .collect(Collectors.groupingBy(
                        // 使用ClusterSlotHashUtil计算每个键对应的槽位
                        entry -> ClusterSlotHashUtil.calculateSlot(entry.getKey()))
                );
 
        // 遍历按哈希槽分组后的结果
        for (List<Map.Entry<String, String>> list : result.values()) {
            // 创建一个数组用于批量设置Redis的键值对
            String[] arr = new String[list.size() * 2];  // 每个键值对包含两个元素
            int j = 0;  // 索引变量,用于在数组中定位位置
            for (int i = 0; i < list.size(); i++) {
                j = i << 1;  // 通过位移计算数组中的位置
                Map.Entry<String, String> e = list.get(i);  // 获取当前的键值对
                arr[j] = e.getKey();  // 将键放入数组中
                arr[j + 1] = e.getValue();  // 将值放入数组中
            }
            // 批量设置Redis集群中的键值对
            jedisCluster.mset(arr);
        }
    }
 
    // 在每个测试方法执行之后,关闭JedisCluster连接
    @AfterEach
    void tearDown() {
        // 如果JedisCluster对象不为空,则关闭连接
        if (jedisCluster != null) {
            jedisCluster.close();
        }
    }
}

而在Redis集群环境下,如果需要批量获取多个键的值,可以使用multiGet方法。multiGet是RedisTemplate提供的一个方法,用于一次性获取多个键的值。然而,需要注意的是,multiGet在集群环境下要求所有键必须位于同一个槽位(slot),否则会抛出异常。

@Service
public class RedisService {
 
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
 
    /**
     * 跨槽位批量获取多个键的值
     */
    public Map<String, Object> batchGetCrossSlot(List<String> keys) {
        // 按槽位分组
        Map<Integer, List<String>> slotKeyMap = keys.stream()
                .collect(Collectors.groupingBy(ClusterSlotHashUtil::calculateSlot));
 
        // 存储最终结果
        Map<String, Object> result = new HashMap<>();
 
        // 对每个槽位的键分别调用multiGet
        for (Map.Entry<Integer, List<String>> entry : slotKeyMap.entrySet()) {
            List<String> slotKeys = entry.getValue();
            List<Object> slotValues = redisTemplate.opsForValue().multiGet(slotKeys);
 
            // 将结果存入Map
            for (int i = 0; i < slotKeys.size(); i++) {
                result.put(slotKeys.get(i), slotValues.get(i));
            }
        }
 
        return result;
    }
 
    /**
     * 测试跨槽位批量获取方法
     */
    public void testBatchGetCrossSlot() {
        List<String> keys = Arrays.asList("name", "age", "sex");
        Map<String, Object> values = batchGetCrossSlot(keys);
 
        // 打印结果
        values.forEach((key, value) -> {
            System.out.println("Key: " + key + ", Value: " + value);
        });
    }
}

到此这篇关于Redis如何使用Pipeline实现批处理操作的文章就介绍到这了,更多相关Redis Pipeline批处理操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Redis内存碎片率调优处理方式

    Redis内存碎片率调优处理方式

    Redis集群因内存碎片率超过1.5触发告警,分析发现内因与外因导致内存碎片,内因为操作系统内存分配机制,外因为Redis操作特性,使用Redis内置内存碎片清理机制可有效降低碎片率,但需注意可能影响性能,建议使用MEMORY命令诊断内存使用情况,合理配置参数以优化性能
    2024-09-09
  • Centos 7 如何安装Redis(推荐)

    Centos 7 如何安装Redis(推荐)

    这篇文章主要介绍了Centos 7 如何安装Redis,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12
  • Redis缓存三大异常的处理方案梳理总结

    Redis缓存三大异常的处理方案梳理总结

    这篇文章主要介绍了Redis缓存三大异常的处理方案梳理总结,缓存方式,在提高数据查询效率、保护数据库等方面起到了不可磨灭的作用,但实际应用中,可能会出现一些Redis缓存异常的情况,下文对其方案总结需要的朋友可以参考一下
    2022-06-06
  • Redis进行相关优化详解

    Redis进行相关优化详解

    这篇文章主要介绍了Redis进行相关优化,Redis在项目中进行广泛使用,那么在日常的开发过程中,我们在使用Redis的过程中需要注意那些呢?本文将从三个维度来讲解如何进行Redis的优化
    2022-08-08
  • Redis排序命令Sort深入解析

    Redis排序命令Sort深入解析

    这篇文章主要为大家介绍了Redis排序命令Sort深入解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 浅谈redis内存数据的持久化方式

    浅谈redis内存数据的持久化方式

    这篇文章主要介绍了浅谈redis内存数据的持久化方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • 深入理解redis_memcached失效原理(小结)

    深入理解redis_memcached失效原理(小结)

    这篇文章主要介绍了深入理解redis_memcached失效原理(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • 基于Redis验证码发送及校验方案实现

    基于Redis验证码发送及校验方案实现

    本文主要介绍了基于Redis验证码发送及校验方案实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • Windows环境下安装Redis并设置Redis开机自启实践

    Windows环境下安装Redis并设置Redis开机自启实践

    文章介绍了如何在Windows系统上安装和配置Redis,包括下载Windows版本的Redis、设置连接密码、启动Redis、设置开机自启等步骤
    2025-12-12
  • 图解Redis主从复制与Redis哨兵机制

    图解Redis主从复制与Redis哨兵机制

    这篇文章主要介绍了图解Redis主从复制与Redis哨兵机制,今天分享一下Redis的持久化、事务、管道相关的知识点,需要的朋友可以参考下
    2023-03-03

最新评论