一、 发布与订阅:Publish、Subscribe、Pubsub等命令实现原理
通过执行 SUBSCRIBE命令,客户端可以订阅一个或者多个频道,从而成为这些频道的订阅者:每当有其他客户端向被订阅的频道发送消息时,频道的所有订阅者都会收到这条消息
1.1 频道的订阅与退订
所有频道的订阅关系都保存在服务器状态的 pubsub_channels字典里,这个字典的 键是某个被订阅的频道, 值是一个存储订阅这个频道的客户端链表
- 订阅频道(SUBSCRIBE) :如果已经存在键,添加到链表表尾,否则创建新的键然后创建一个空链表最后把客户端添加到链表
- 退订频道 (UNSUBSCRIBE):找到订阅者链表并删除,如果链表变成了空链表,则会删除频道对应的键
1.2 模式的订阅与退订
所有模式的订阅关系都保存在服务器状态的 pubsub_patterns链表属性里,链表中的每个节点都包含着一个 pubsubPattern结构,这个结构中 client属性记录了订阅模式的客户端, pattern属性记录了被订阅的模式。
- 订阅频道(PSUBSCRIBE) :当客户端执行PSUBSCRIBE命令订阅某个或者某些模式的时候,服务器会先新建一个pubsubPattern结构,将结构的pattern属性设置为被订阅的模式,client属性设置为订阅模式的客户端。然后将pubsubPattern结构添加到pubsub_patterns链表的表尾
- 退订频道 (PUNSUBSCRIBE):找到对应的pubsubPattern结构并将其删除
1.3 发送消息
当一个客户端执行 publish <channel> <messagge>命令将消息发送给频道channel时,服务器执行以下两个动作:
- 将消息发送给channel频道的所有订阅者
- 如果有一个或者多个模式pattern与频道channel匹配,将消息发送给pattern模式订阅者
1.4 查看订阅消息
- PUBSUB CHANNELS [pattern]:返回服务器当前被订阅的频道
- PUBSUB SUBNUM [channel-1 channel-2...channel-n]:返回多个频道的订阅者数量
- PUBSUB NUMPAT:返回服务器当前被订阅模式的数量
二、事务:Multi、Exec、Watch等命令实现原理
Redis 通过 MULTI、 EXEC、 WATCH等命令实现事务功能。事务提供一种将多个命令请求打包,然后一次性、按顺序执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,才去处理其他客户端的命令请求。
2.1 事务的实现
- 事务开始:MULTI命令可以将客户端从非事务状态切换至事务状态
- 命令入队:如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四种命令之外的命令服务器不会立即执行命令,而是会将这个命令放入事务队列中,并返回QUEUED回复
- 事务队列(先进先出):事务队列记录了命令相关的信息,包括命令实现函数的指针、命令的参数、参数的个数
- 执行事务:EXEC命令。服务器会遍历事务队列,并执行所有命令。
2.2 WATCH命令的实现
watch命令是一个乐观锁,它可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过,如果是的话,服务器会拒绝执行事务
- 使用WATCH命令监视数据库键:每个Redis数据库都保存一个watched_keys字典,字典的键是某个被WATCH命令监视的数据库键,而字典的值则是一个链表,链表记录了所有监视这个键的客户端
- 监视机制的触发:当被watch的键被修改后,表示该客户端事务安全性已经被破坏的REDIS_DIRTY_CAS标识会被打开
- 判断事务是否安全:服务器就是根据REDIS_DIRTY_CAS标识俩决定是否执行事务的
2.3 事务的ACID性质
- 原子性(Atomicity)
事务具有原子性是指数据库将事务中的多个操作当做一个整体来执行,服务器要么执行事务中所有操作,要么一个操作不执行。Redis不支持事务回滚机制,即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中所有命令都执行完毕为止
- 一致性(Consistentency)
事务具有一致性指的是如果数据库在执行事务之前是一致的,那么在事务执行之后无论事务是否执行成功,数据库也依然是一致的。这里的 一致指的是数据符合数据库本身的定义和要求,没有包含非法或者无效的错误数据 入队错误:如果一个事务在入队命令过程中出错,那么Redis将拒绝执行这个事务 执行错误:即使在事务的执行过程中发生错误,服务器不会中断事务的执行,会继续执行事务中余下的命令,并且已执行的命令不会被出错的命令影响 服务器停机:事务执行中途发生停机都不会影响数据库的一致性
- 隔离性(Isolation)
事务的隔离性指的是即使数据库中有多个事务并发执行,各个事务之间也不会互相影响、并且在并发状态下执行的事务和串行执行的事务产生的结果是相同的
因为Redis使用单线程的方式来执行事务并且服务器保证在执行事务期间不会对事务进行中断。因此事务总是以串行的方式运行的,并且事务也总是具有隔离性的。
- 耐久性(Durability)
耐久性指的是当一个事务执行完毕时,执行这个事务所得到的结果已经被保存到永久性存储介质中了,即使服务器在事务执行完毕之后宕机,执行事务所得的结果也不会丢失
Redis事务的耐久性由Redis所使用的的持久化模式决定。只有当服务器运行在AOF持久化模式下并且 appendfsync选项的值为always时,事务才具有耐久性。另外,不论Redis在什么模式下运行,在一个事务的最后加上 SAVE命令总可以保证事务的耐久性。但是这种做法会严重影响效率,不建议使用过。
最后总结下,Redis的事务总是具有ACID中的原子性、一致性、隔离性,当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时事务也具有耐久性