Redis Cluster基于客户端对mget的性能优化

本文分析了RedisCluster与Codis在mget性能上的差距,揭示了RedisCluster架构和Lettuce实现方式的问题。通过使用hashtag和客户端改造(包括JedisCluster的pipeline优化),显著提高了mget性能。然而,改造后的JedisCluster依赖于连接池配置,而Lettuce更适用于并发请求,各有优劣。

1 背景

Redis是知名的、应用广泛的NoSQL数据库,在转转也是作为主要的非关系型数据库使用。我们主要使用Codis来管理Redis分布式集群,但随着Codis官方停止更新和Redis Cluster的日益完善,转转也开始尝试使用Redis Cluster,并选择Lettuce作为客户端使用。但是在业务接入过程中发现,使用Lettuce访问Redis Cluster的mget、mset等Multi-Key命令时,性能表现不佳。

2 分析原因

2.1 现象

业务在从Codis迁移到Redis Cluster的过程中,在Redis Cluster和Codis双写了相同的数据。结果Codis在比Redis Cluster多一次连接proxy节点的耗时下,同样是mget获取相同的数据,使用Lettuce访问Redis Cluster还是比使用Jeds访问Codis耗时要高,于是我们开始定位性能差异的原因。

2.2 定位问题

2.2.1 Redis Cluster的架构设计

导致Redis Cluster的mget性能不佳的根本原因,是Redis Cluster在架构上的设计导致的。Redis Cluster基于smart client和无中心的设计,按照槽位将数据存储在不同的节点上

如上图所示,每个主节点管理不同部分的槽位,并且下面挂了多个从节点。槽位是Redis Cluster管理数据的基本单位,集群的伸缩就是槽和数据在节点之间的移动。

通过CRC16(key) % 16384计算key属于哪个槽位和哪个Redis节点。而且Redis Cluster的Multi-Key操作受槽位限制,例如我们执行mget,获取不同槽位的数据,是限制执行的:

2.2.2 Lettuce的mget实现方式

lettuce对Multi-Key进行了支持,当我们调用mget方法,涉及跨槽位时,Lettuce对mget进行了拆分执行和结果合并,代码如下:

public RedisFuture<List<KeyValue<K, V>>> mget(Iterable<K> keys) {
   
   
    //将key按照槽位拆分
    Map<Integer, List<K>> partitioned = SlotHash.partition(codec, keys);

    if (partitioned.size() < 2) {
   
   
        return super.mget(keys);
    }

    Map<K, Integer> slots = SlotHash.getSlots(partitioned);
    Map<Integer, RedisFuture<List<KeyValue<K, V>>>> executions = new HashMap<>();
	   //对不同槽位的keys分别执行mget
    for (Map.Entry<Integer, List<K>> entry : partitioned.entrySet()) {
   
   
        RedisFuture<List<KeyValue<K, V>>> mget = super.mget(entry
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值