带着问题看 redis 源码 -- blpop实现 - (sunznx) 振翅飞翔
12 October 2019

blpop 是用来阻塞读一个 list 的数据,如果 list 里面有数据,就直接返回。block 时间表示在数据返回数据之前,最长阻塞多久,为 0 表示一直阻塞

redis-cli> blpop l 0

一直好奇这个功能是如何实现的,今天看源码的时候不小心瞥到了,因此记录下这个小功能的实现。

在 redis 里面,如果一个 list 为空,那么这个 list 就会从 redis 数据库里面删除。

知道这个实现就简单了。

redis 会维护一个 blocking_keys 列表,表示有多少 key 需要特殊处理。blocking_keys 是一个 hash 结构,hash 的 key 为阻塞的 key,value 是一个 list 结构,存储的是对应的 client 列表。
然后还会维护一个 ready_keys 列表,当一个 list 结构的 key 被创建的时候,redis 会调用 signalKeyAsReady ,将 key 添加到 ready_keys 列表中。现在只要遍历 ready_keys 的每个 item,通过 blocking_keys[item] 来获取 client 列表,再将对应的 value 返回给对应的客户端。blpop 这个功能的实现便完成了。

何时遍历?
redis 每次执行完命令后会调用一次 handleClientsBlockedOnKeys ,这个方法就是遍历 ready_keys ,然后通知给各个 client。

ready_keys:(ready_keys 在 server 里面有,在 db 里面也有)

  1. server 的 ready_keys 是一个 list 结构,每个 item 的结构为 {key: xxx, db: xxx}
  2. db 的 ready_keys 是一个 hash 结构,hash 的 key 为对应的 key,value 为 null,这里的 ready_keys 是为了快速查询这个 key 是不是已经存在 ready_keys,来防止重复处理

所以,完整的 blpop 实现流程:
客户端执行 blpop l ,判断 l 有没有数据:(判断 l 有没有数据只是简单的判断 l 这个 key 存不存在)

  1. 有数据,将 blpop 改成 lpop 来执行,即直接返回
  2. 没有数据,将 l append 进 blocking_keys 里面,直到 ready_keys 有数据才返回(即 l 被创建的时候)