Redis使用定时删除、惰性删除策略来删除过期的key。惰性删除是指在获取key的时候,判断key是否过期,如果过期则删除。不过当redis实例作为从库运行时,遇到过期的key却不会删除。
惰性删除策略
惰性删除是指在获取key的时候,先检查key是否过期:
1 | robj *lookupKeyRead(redisDb *db, robj *key) { |
查看检查key是否过期的expireIfNeeded()
函数:
1 | int expireIfNeeded(redisDb *db, robj *key) { |
如果当前实例是主库,会将过期key删除;如果是从库,遇到过期key并不删除,而是等待主库的DEL
命令来删除。
从库过期key处理
既然没有删除key,在收到主库的DEL
命令之前,能否在从库查询到过期的key?
在Redis 3.2版本之前,从库确实可以查询到已经过期key。
在3.2版本对这个bug进行了修复,修复之后查询过期key返回NULL:
1 | robj *lookupKeyRead(redisDb *db, robj *key) { |
总结
- 主库查到过期的key时,会将key删除(惰性删除策略)。
- 从库查到过期的key时,不会主动删除已过期的key,而是等待主库的
DEL
命令来删除。 - 在Redis 3.2之前,可能在从库查询到已过期的key。
- 在Redis 3.2及之后,从库查询到过期key时,返回NULL。
思考
为什么从库遇到过期的key不删除呢?如果删除了会有什么问题?
在Redis源代码的lookupKeyRead()
函数中的注释:
However if we are in the context of a slave, expireIfNeeded() will
not really try to expire the key, it only returns information
about the “logical” status of the key: key expiring is up to the
master in order to have a consistent view of master’s data set.
注释中提到是为了保证数据的一致性。按照redis主从复制的设计,主库会将自己执行的写命令发送给从库,从库接收主库的写命令,这样就可以保证主从库的一致性。如果在从库执行了写操作,那么这个写操作不会同步到主库,就会造成数据不一致。
因此,从库是只读的,除非是来自主库的写命令。
那对于key已经过期这种场景来说,反正迟早都是要删除的,为啥从库不立即删除呢?
有一种比较极端的情况:由于从库的时钟快了,导致从库认为key已经过期了,而主库认为key还没过期。如果从库的时钟恢复正常了,再去从库查询这个key应该是可以查询到的。所以从库判断key已经过期时,不能主动删除key。