写在前面
哈哈哈哈哈,这次又来到了redis,redis对于大家来说,想必都已经熟悉了吧,笔者最近在做高并发的时候碰到一个问题,就是一个http接口,里面携带一个list<string> ~ a,然后通过a从redis中获取数据,考虑到数据量比较大,后来增加了redis集群,但是在访问的时候,使用的是 redisTemplate的mget,无论怎么优化,性能都是在秒级,对于缓存的读取,这个量级基本上算是废废的了,于是从其他地方入手。
性能测试背景
通过项目提供的数据插入接口向redis-cluster 插入1000条记录,然后在通过key的集合从redis-cluster中获取数据。
效果对比
优化前
redisTemplate.mget
好像一次查询1000条数据2245ms返回没有啥问题,单个平均也就2245ms/1000=2.245ms,还算行吧,但是对于一个高并发的场景,http接口而言,就是个垃圾,完全不可用。一般的互联网公司对于,如果是To C的产品的接口,要求的RT都是200内,很果断的放弃这种。
优化后
重写了jedis的get,并且加入pipeline 返回数据原因分析
对于redisTeplate的实现原理,这里就不赘述了,接下来从根儿上分析下改造后的实现原理,在这之前,我们先了解下redis集群的一个重要属性 slot
在redis集群中,数据的主要存储单元,每一个key都要对应到一个slot中去,具体的算法是CRC16(key)算法,流程如下图,参见
redis.clients.util.JedisClusterCRC16#getSlot(java.lang.String)
CRC16算法实现源码分析
思路:先找出哪些key在哪一个slot中,然后再通过jedispool获取到jedis,这里每个jedispool对应一个redis实例,从jedis中通过pipeline的方式获取,拿到结果,再做merge
下面以图的方式展示
1:将keys映射到jedispool,com.zcc.testrediscluster.jedis.SelfDefJedisSlotBasedConnectionHandler#getJedisPoolFromSlot这个方法在原生的jedis中是没有,我们重写了。
keys映射到jedispool2:重写原生jedisHandler
添加一个getJedisPoolFromSlot方法3:将这个handler托管到spring
bean托管4:接口调用
从redis中获取值
获取jedis-pipeline数据合并
数据整理总结
上述结果充分说明了阅读源码的重要性,在做高并发的时候,就凸显源码是多么的重要,如果没有深入源码的话,在做性能优化的时候,也是很头疼的事情,以上的实现参考了搜狐开源的cache-cloud设计思想,在实践的同时,把cache-cloud的源码也撸了一遍,收获颇多,会从别人的身上汲取很多的经验,突然想起了牛顿说的那句话 "如果我看得更远一点的话,是因为我站在巨人的肩膀上" 共勉~
网友评论