发布于订阅
频道订阅与退订
所有频道的订阅关系都保存在服务器的pubsub_channels字典中
- 字典中的键是某个被订阅的频道
- 字典中的值是订阅频道的客户端,是链表
模式订阅与退订
所有模式订阅关系都存在服务器的pubsub_patterns链表中,链表中的节点结构pubsubPattern。
示例:
发送消息
- 发送给频道订阅者:在
pubsub_channels字典中找到订阅者信息发送。 - 发送给模式订阅者:遍历
pubsub_patterns链表,找到与客户端相同模式的客户端,发送。
Redis 事务
事务的实现
- 事务开始
- 命令入队
- 事务执行
1) 事务开始
redis> MULTI
OK
MULTI命令把客户端从非事务状态切换至事务状态——flags属性表示。
2) 命令入队
- 当客户端处于非事务状态,命令会立即被服务器执行
- 当客户端处于事务状态,根据命令判断执行
- 如果客户端发送的命令为
EXEC、DISCARD、WATCH、MULTI四个命令的其中一个,那么服务器立即执行这个命令。 - 除此之外,不立即执行,放入事务队列,向客户端返回
QUEUED回复.
- 如果客户端发送的命令为
整体结构:
- 事务状态:保存在服务器状态的
mstate属性中typedef struct redisClient { // ... // 事务状态 multiState mstate; /* MULTI/EXEC state */ // ... } redisClient; - 事务状态
typedef struct multiState { // 事务队列,FIFO 顺序 multiCmd *commands; // 已入队命令计数 int count; } multiState; - 事务队列
typedef struct multiCmd { // 参数 robj **argv; // 参数数量 int argc; // 命令指针 struct redisCommand *cmd; } multiCmd;
示例:
- 执行命令
redis> MULTI OK redis> SET "name" "Practical Common Lisp" QUEUED redis> GET "name" QUEUED redis> SET "author" "Peter Seibel" QUEUED redis> GET "author" QUEUED - 结构图
3) 执行事务EXEC
服务器遍历事务队列,执行队列里的所有命令,最后把执行结果全部返回给客户端。
watch命令(乐观锁)
watch命令是一种乐观锁,可以在exec命令执行前,检测任意数量的数据库键,在exec命令执行时,检测键是否已经被修改,如果修改则拒绝执行,返回执行失败的空回复。 底层实现:
- Redis底层里有个
watched_keys字典属性用于监测
- 如果被修改则打开
REDIS_DIRTY_CAS标识,最后根据表示判断。
事务的ACID性质
× 原子性
Redis不具备原子性,因为Redis不支持事务回滚,在事务队列里即使有命令出错,也会执行下去。
√ 一致性
如何保证一致性:
- 入队错误:如果在入队命令过程中,命令不存在,格式错误,Redis拒绝执行。
- 执行错误:事务执行过程中,命令出错,不会对数据库进行修改,不会对事务的一致性产生影响。
- 服务器停机:
- 无持久化模式下,重启依旧是空白,因此数据一致。
- RDB模式下,事务中途停机不会导致不一致,因为可以恢复到一致状态。
- AOF模式下,事务中途停机不会导致不一致,因为可以恢复到一致状态。
√ 隔离性
Redis采用单线程方式执行事务,并且服务器保证,运行期间事务不会中断,因此事务总是串行执行,具有隔离性。
√/× 持久性
Redis在RDB和AOF模式下才会持久化。