当前位置: 首页 > 科技观察

4处优化,我把Redis的性能“压榨”到了极致!

时间:2023-03-16 11:27:47 科技观察

我们有这样的要求:每件抢购商品每天只能购买一次,全场抢购商品的总次数不得超过5次。那么,整个商品限购的流程大致如下图所示:那么,每购买一个商品成功后,发送的MQ大概是这样的(假设当前订单有两个抢购商品):[{"orderId":"2020020622000001","orderTime":"1581001673012","productId":"599055114591","userId":"860000000000001","merchantCode":"A045"},{"orderId":"20200200622000":"1581001673012","productId":"599055114592","userId":"860000000000001","merchantCode":"A045"}]这条消息的意思是用户86000000000001时间为1581001673012(北京时间2020/02/06)23:07:53)在商户A045购买了商品ID为599055114591和599055114592的两件商品。那么消费这些信息后,更新频率控制的几个关键Redis命令如下(以上需求不是重点,优化以下5条命令是本文的重点):命令1:hsetmall:sale:freq:ctrl:8600000000000015990551145911(hash结构,字段表示购买的商品ID,value表示购买次数)命令2:hsetmall:sale:freq:ctrl:860000000000015990551145922命令3:expiremall:sale:freq:ctrl:8600000013expiration)020(command4:setmall:total:freq:ctrl:8600000000000013command5:expiremall:total:freq:ctrl:8600000000000013127(设置过期时间)我们先来了解一下执行一个Redis命令需要哪些部分:发送请求的网络传输时间命令,命令在Redis服务端队列中等待时间,命令执行时间(Redis中的slowlog只检测到这一步的时间),结果返回到服务端的时间客户端。如下图所示:以上业务一共涉及到5条Redis命令,每条命令都需要经过这几个步骤。可想而知,性能确实很弱(可能整个执行过程不需要10ms,但还是很弱)。第一次优化第一次优化很简单,稍微有点经验就可以看出来。使用hmset命令将两个hmset命令合二为一。优化的REDIS命令如下:HMSETMALL:销售:freq:CTRL:8600000000000015990551145114591159905511451145951145922222222222222222222222222222222222Y转换:freq:ctrl:ctrl:8600000000000000000000000013127SET000000000010000000000000000000000000000000000000000000000来00000000000来并过期命令分为一个,这对那些对REDIS的了解:HMSETMALL:sale:freq:CTRL:86000000000015990551145999059055114592222222222222222222222222222222159999999部1部子后子后内3次优化需要借用pipeline,简单直接就是Redis优化的一大杀手。但是需要注意的是,在RedisCluster中使用pipeline时,pipeline打包的所有commandkey必须在RedisCluster的同一个slot上。如果打包命令的key不在同一个slot,会报错。所以我们需要分两批打包:--这两个命令的key是一样的,必须在同一个slot上。:total:freq:ctrl:860000000000001和mall:sale:freq:ctrl:860000000000001不在同一个槽位,所以下面的命令setexmall:total:freq:ctrl:860000000000000131273已经优化了第三次之后,这些命令仍然需要2个网络交互。我还是不甘心竞争。我想将其优化为只需要一次网络交互。有办法吗?当然有!第四个优化这个优化利用了一个高级特性:hashtag。你是什??么意思?我们知道RedisCluster一共有16*1024=16384个槽。那么在执行Redis命令时,key对应的是哪个slot呢?使用这样一个计算公式得到:slot=CRC16(key)%16384,原理图如下:也就是说默认key在哪个slot上是和key相关的。那么,是否可以只让key与哪个slot上的某些key相关呢?当然,这是标签功能。用法很简单,假设一个key是mall:sale:freq:ctrl:860000000000001,我们只需要用{}把我们需要的部分包含在key中即可。比如我们只希望根据用户的IMEI计算,那么key是这样的:mall:sale:freq:ctrl:{860000000000001}。只要key里面有{860000000000001},就一定落在同一个slot上。因此,第四次优化后的命令执行如下:pipeline(hmsetmall:sale:freq:ctrl:${860000000000001}59905511459115990551145922expiremall:sale:freq:ctrl:${860000000000001}3127setexmall:total:freq10:0206000}After)优化,将5条Redis命令压缩为3条Redis命令,3条Redis命令只需要发送一次,一次性返回所有结果。简直完美!!注意事项在使用hashtag特性时,一定要注意不要把key的离散性做得很差。以这篇文章为例,在使用hashtag功能之前,key是这样的:mall:sale:freq:ctrl:860000000000001。显然,这种密钥是非常离散的,因为它与用户有关。使用hashtag后,key是这样的:mall:sale:freq:ctrl:{8600000000000001},这种key还是和用户相关的,所以离散性还是很好的。我们一定不能这样使用hashtag特性,比如设置key为:mall:{sale:freq:ctrl}:860000000000001。这样的话,不管有多少用户,多少key,{}里面的内容和sale:freq:ctrl是一模一样的,也就是说所有的key都会落在同一个slot上,导致严重的问题整个Redis集群。倾斜问题。