文件
| 内容 |
---|---|
| Redis 的事件处理器实现(基于 Reactor 模式)。 |
| Redis 的网络连接库,负责发送命令回复和接受命令请求, 同时也负责创建/销毁客户端, 以及通信协议分析等工作。 |
| 单机 Redis 服务器的实现。 |
redis数据库结构
redis服务端的对象结构中redisDb主要用来存储具体信息,结构如下:
struct redisServer {
......
redisDb *db;
......
}
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. */
}
redis存储的底层是通过字典数据结构实现的,可以看到 *dict(键空间字典) 对应的就是键空间,是实际存储数据的字典, *expires(过期字典),为了节省空间,这个key只是挂接实际key的指针,避免重复存储和同步的问题
过期键删除时机和策略
一般针对过期信息的删除有三种方式,redis主要使用后面两种:
- 维护一个定时器,定时删除
- 发生查询等相关操作时,惰性删除
- 多次遍历随机检查的定期删除:通过多次遍历,随机检查可以避免一次性任务过重,演化为定时删除
过期键对rdb和aof
- 针对过期的键会在save或bgsave时提前过滤掉
- 针对过期的键会在aof时追加一条del命令
集群模式下的过期键
- 主从架构模式下,只会针对主做删除策略,然后通过同步机制到从库
事件驱动
redis事件处理器是基于事件驱动架构、reactor模式实现的,普遍的基于线程的架构模式需要在客户端和服务端之间一对一的建立连接和工作线程,基于事件驱动的架构模式只需要内部预留事件线程用于跟踪及回调。
事件驱动(Event-Driven)
由事件生产者和事件消费者构成,有两种交互模式
- 发布/订阅:构建发布订阅关系,当产生事件时发送到订阅者,当事件被接收之后,不能被重放,同时新的订阅者不能看到历史事件
- 事件流:事件可以被保证顺序(在一个分区内)及持久化,消费者自己拉取事件流,同时可以自己控制事件流的偏移量位置,新接入的消费者可以消费历史事件。
事件驱动有什么优势
- 解耦,各系统可以独立,自主性更强
- 可以多对多,随时加入生产或者消费节点
实现方式:
四种,感兴趣的可以了解一下:Reactor,Proactor,Asynchronous Completion Token,Acceptor-Connector
reactor
reactor是事件驱动架构的一种实现方式,通过一个单线程循环遍历监控事件生产源,并将事件派发给不同的事件处理器并触发回调,reactor实现中四个概念:
- Initiation Dispatcher:事件分派器,构建事件和事件处理器的关系,并负责调用Synchronous Event Demultiplexer
- Handle:系统资源的抽象
- Synchronous Event Demultiplexer:同步事件分离器,调用select(阻塞),内核会针对select传进去的handlers遍历,当sockets中有可用后返回
- Event Handler:连接应答处理器、命令请求处理器以及命令回复处理器、事件处理器等
Slots
redis集群模式下,redis的keyspace(键空间)被拆分到16384个slots中,16384个slots被分配给不同的主节点,因为节点间会同步每个节点所负责的slots,在hash期间,如果客户端打到当前节点,会判断key是否已迁移到新节点,如果已迁移会通过ack指引客户端访问新节点进行操作。