

5分钟学会这种更高效的Redis数据删除方式
source link: http://mp.weixin.qq.com/s?__biz=Mzg4NjA4NTAzNQ%3D%3D&%3Bmid=2247485724&%3Bidx=1&%3Bsn=3386e75af0ba2dc33cf2798262676c1b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1
| 作者: 冯伟源,腾讯云数据库架构师,腾讯云Redis从零到一的技术运营负责人,第九届中国数据库技术大会分享嘉宾,拥有八年数据库的运营开发与海量运维经验,曾让QQ与Qzone数据库业务达到一人万台服务器的运营成熟度。
1
我们知道,Del命令能删除数据,除此之外,数据在Redis中,还会以哪种方式被删除呢? 在Redis内存满一定会返回OOM错误? Key到达过期时间就立即删除? 删除大Key会影响性能吗? 下面,咱们一起探讨。
1
同步和异步删除
1.DEL 和 UNLINK
Redis服务自身对Key的删除,可以分为「同步删除」和「异步删除」。使用DEL命令会触发「同步删除」,如果Key是一个有很多元素的复杂类型,这个过程可能会堵塞一下Redis服务自身,从而影响用户的访问。如果使用UNLINK命令,Redis服务会先计算删除Key的成本,从而更智能地做出「同步删除」或「异步删除」的选择。注意,只有4.0版本后,才有UNLINK命令。
2.成本计算
那么,成本是如何计算的呢?对于list,hash,set,zset的对象类型,如果长度大于64(由宏LAZYFREE_THRESHOLD定义),才会采用异步删除的手段,从当前db先释放该key,再由另外一个线程做异步删除。对于长度不大于64的复杂类型,异步删除比同步删除还多了一些函数调用与多线程同步的代价,所以同步删除更好。对于string对象,底层的数据结构sds是一份连续的内存,内存分配器回收这块内存的复杂度是O(1),所以采用同步删除也不会堵塞服务。
总的来说,我们作为用户,都能用UNLINK替代DEL。
1
Key的驱逐
1.定义
Redis处理命令前根据内存容量是否触达上限而进行的Key驱逐。
2.驱逐策略
Redis通过参数maxmemory来选择不同的驱逐策略:
-
volatile-random 从已设置过期时间的数据集(server.db[i].expires)中任意选择数据驱逐;
-
volatile-lru 从数据集(server.db[i].dict)中挑选最近最少使用的数据驱逐(2.8默认);
-
volatile-ttl 从已设置过期时间的数据集(server.db[i].expires)中寻找最近即将过期(ttl最小)的key来驱逐;
-
allkeys-random 从数据集(server.db[i].dict)中任意选择数据驱逐;
-
allkeys-lru 从数据集(server.db[i].dict)中挑选最近最少使用的数据驱逐;
-
noeviction 禁止驱逐数据,永远不驱逐,仅对写操作返回一个错误(4.0默认);
在4.0版本后,还增加了以下两种驱逐策略。
-
volatile-lfu在过期集合中使用LFU链来驱逐数据;
-
allkeys-lfu 从数据集(server.db[i].dict)使用LFU算法来驱逐数据;
3.简述
Redis在处理命令前,会看看容量是否触达上限。
如果驱逐策略为noeviction,则不会驱逐Key,而是返回写失败。4.0后,在返回写入失败前,还会先检测lazyfree线程是否还有待删除的Key,没有才会给用户返回写入失败。
对于其他策略,都会根据相应定义,进行Key的驱逐,这里不再详述。
在4.0或以上的版本,Key的驱逐会基于参数lazyfree_lazy_eviction,来决定采用unlink还是del。在2.8版本,则只会用del。lazyfree_lazy_eviction参数在Qcloud 4.0上是no。
这里试问,主从节点都会进行「驱逐」么?
答案是都会的,各自会因应自身的驱逐策略进行驱逐,并且Master节点驱逐的删除命令还会传播到Slave节点。
1
Key的访问淘汰
1.定义
访问一个已过期的Key会触发对其的删除。
2.简述
与Key的驱逐一样,Key的访问淘汰同样是基于访问事件来触发的。
主从角色的节点在处理访问淘汰上的逻辑是不同的。
对于Slave节点,访问到了已过期的Key,Slave节点会返回该Key不存在,但不会主动删除该Key。删除的动作,还是会从Master上同步过来。
对于Master节点,在4.0或以上的版本,会根据参数lazyfree-lazy-expire,来决定用DEL还是UNLINK。对于2.8版本,则只能用DEL了。这些删除的动作,都会同步到Slave与AOF文件中。
在Qcloud 4.0以上的版本,默认是开启异步删除的,即lazyfree-lazy-expire=yes
1
Key的定时淘汰
1.定义
Redis自身的定时调度把已过期Key删除。
2.简述
多久会执行一次定时调度呢?
redis服务的参数hz能控制定时淘汰的频率,hz默认是10,即每秒能调度100次。
刚才说「访问淘汰」的逻辑只会在Master角色上发生,那「访问淘汰」也是吗?
一般来说,Slave节点不会进行定时淘汰,它只会等待从Master节点同步过来的删除命令,这样就保持了主从之间的一致性。然而,有些时候,用户会把Slave节点设置成可写,那么Slave上写的带有过期时间的Key,因为Master是不知道的,就一直不会淘汰掉。所以在版本4.0以后,Redis增加了单独的逻辑,在定时淘汰中删除这些在slave节点上写入的过期Key。
对于Master节点,根据宏ACTIVE_EXPIRE_CYCLE_SLOW,能选择两种淘汰模式,分别是“FAST淘汰”和“SLOW淘汰”,前者每次淘汰只能花1毫秒,不能花更多了,后者是Qcloud默认的选项,这样能在每次调度中淘汰更多的Key,但会花更多的CPU时间在淘汰上,降低了处理的访问吞吐量。下面我们针对“SLOW淘汰”展开描述。
SLOW淘汰模式,以hz=10为例,每次调度的总时间是100ms,这里调度不会25%的cpu时间,即25ms。
每淘汰多少个key,就检测一次是否超25ms呢?
如果每淘汰1个Key就检测一次,无疑代价太大。从源码上看,定时淘汰会尝试遍历每个db,遍历完了或者时间到了就退出循环。第一层循环是遍历各个db,第二层循环是遍历db里面的一批批key,一批key是20个,如果第三层循环结束后有大于5个key是成功淘汰的(说明这个db很多淘汰key),那么二层就继续循环,如果小于等于5个key,说明这个db没有很多key需要淘汰,则退出二层循环,第三层循环是一批key里面逐个key进行淘汰。即最多320个key进行判断后,就会看看是否已经超过cpu占用时间。
在4.0或以上的版本,会根据参数lazyfree-lazy-expire(默认no)来做DEL还是UNLINK。在Qcloud的4.0以上版本,这里会特意配置成yes,以便尽量采用UNLINK操作。2.8版本不支持lazyfree-lazy-expire,就只能选择DEL命令。
这里的定时淘汰,也会以命令的形式,传播到Slave节点与记录到AOF文件中。
1
1.驱逐策略的选择,往往与业务特点、使用场景紧密相关。不当的选择,可能会让用户丢失不想丢失的数据,或者导致较差的驱逐效率;
2.已过期的Key往往不会立刻被删除,用户在导出快照与建立主从时,会疑惑主从之间的Key数量不一致,我们都需要了解这一点;
3.驱逐与淘汰都有可能影响服务,在新版本下,最好都开启unlink代替del。
直播预告
特惠体验云数据库
↓↓更多惊喜优惠请点这儿~
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK