目录
Lazy Free要解决的问题
Lazy Free的官方简介
问题分析
Lazy Free的静态结构
Lazy Free的配置定义
Lazy Free的流程分析
首先,我们来看看官方给出的,关于这个新特性的简介:
* Lazy freeing of keys. Redis is now able to delete keys in the background in a different thread without blocking the server. The new `UNLINK` command is the same as `DEL` but working in a non blocking way. Similarly an `ASYNC` option was added to `FLUSHALL` and `FLUSHDB` in order to let the entire dataset or a single database to be freed asynchronously.可以很轻易地抓到这段描述中的重点:blocking the server
这里就引出了一个值得讨论的问题:为什么删除key会导致Redis服务阻塞?
首先,我们讨论Redis如何存储KV对。
这里直接把redisDb结构体的定义和《Redis设计与实现》中的贴出来,可以看到KV对直接存储在一个dict,即字典中。
/* Redis database representation. There are multiple databases identified * by integers from 0 (the default database) up to the max configured * database. The database number is the 'id' field in the structure. */ typedef struct redisDb { dict *dict; /* The keyspace for this DB */ dict *expires; /* Timeout of keys with a timeout set */ dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/ dict *ready_keys; /* Blocked keys that received a PUSH */ dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */ int id; /* Database ID */ long long avg_ttl; /* Average TTL, just for stats */ unsigned long expires_cursor; /* Cursor of the active expire cycle. */ list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */ } redisDb;也就是说,Redis对KV的CURD操作,底层实现能追溯到dict的相关代码。最后我们可以发现对于val,我们是将其转化为一个list结构体进行释放。在引入Lazy Free机制之前,删除一个大Key必然导致Redis的主进程卡死。
这里附上一部分的源码分析,从删除的入口函数一路往下跟,最后可以发现调用的是dictListDestructor函数。
void dictListDestructor(void *privdata, void *val) { DICT_NOTUSED(privdata); listRelease((list*)val); }如图所示,lazy free这个模块其实可以视为一个中间层:
北向暴露2个接口,分别用于查询队列中等待删除的任务数量和触发删除;北向对接的其他模块此处不展开,用一个Redis Server代替。
南向需要后台进程管理模块提供一个入队接口,需要集群模块和字典模块提供一些实际的删除接口,即针对这种特定的数据结构的删除方法;同时向进程管理模块暴露一个删除的wrapper,其实就是集群模块和字典模块提供的删除接口的wrapper。
lazyfree-lazy-eviction
内存占用过高时发生的驱逐是否使用lazy freenoyes|nolazyfree-lazy-expire
过期键的删除是否使用lazy freenoyes|nolazyfree-lazy-server-del
其他命令或机制引起的删除(即Redis自身触发的删除)是否使用lazy freenoyes|noreplica-lazy-flush
主从同步时,从库触发全量同步时的删库是否使用lazy freenoyes|nolazyfree-lazy-user-del
是否将用户发起的删除全部视为lazy free处理noyes|nolazy free的主要流程如上图所示