《Redis设计与实现》学习笔记-发布与订阅、事务、慢查询日志

发布与订阅

Redis通过发布订阅提供一对多甚至是多对多的节点消息通信,发布订阅由PUBLISH、SUBSCRIBE、PSUBSCRIBE、PUBSUB等命令组成。

  • SUBSCRIBE命令:订阅某频道,在redisServer结构中通过pubsub_channels字典属性保存当前服务器所有频道的订阅关系,字典键时频道名称,字典值是一个链表,记录了所有订阅这个频道的客户端。
  • UNSUBSCRIBE命令:退订频道,调用该命令之后,会把订阅关系从pubsub_channels中删掉,如果键对应的链表为空了,则把键从字典中删除。
  • PSUBSCRIBE命令:订阅模式,服务器将所有的模式订阅关系保存在redisServer结构的pubsub_patterns属性中,该属性是一个链表,每个链表节点包含一个pubsubPattern结构,该结构记录了被订阅的模式和订阅该模式的客户端。
  • PUNSUBSCRIBE命令:退订模式,调用该命令之后,服务器会遍历pubsub_patterns链表,把客户端和模式都匹配的那个节点删除。
  • PUBLISH命令:发送订阅消息,服务器接收到该命令之后,先遍历pubsub_channels找出频道订阅者,把消息发送给所有频道订阅者,然后遍历pubsub_patterns找出与channel匹配的模式,并将消息发送给订阅了这些模式的客户端。
  • PUBSUB命令,查看订阅信息包含下面三个子命令:
  • PUBSUB CHANNLES [pattern]:返回当前服务器被订阅的频道,如指定pattern,则返回与模式匹配的频道
  • PUBSUB NUMSUB [channel-1 ... channel-n]:返回指定频道的订阅者数量
  • PUBSUB NUMPAT:返回服务器当前被订阅模式的数量

事务

Redis通过MULTI、EXEC、WATCH等命令来实现事务功能,实现将多个命令打包然后一次性顺序执行。事务以MULTI命令开始,EXEC命令结束。在执行MULTI命令之后执行的命令不立刻执行,而是向客户端返回一个QUEUED,等执行EXEC命令之后再执行。

事务的实现

事务开始

执行MULTI命令之后,服务器把客户端从非事务态切换到事务态,通过设置redisClient的flags标识,把REDIS_MULTI标识打开。

命令入队

客户端处于非事务态时,客户端发送的命令立刻执行,当处于事务态时:

如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI其中的一个时,服务器立即执行。
如果客户端发送的命令是其它的命令,服务器不会立刻执行这个命令,而是把命令放到事务队列中,然后向客户端返回QUEUED。
redisClient中有个multiState结构的属性mstate记录客户端的事务状态,在multiState结构中有个commands列表属性,它记录了调用MULTI之后该客户端入队的命令,count属性记录入队命令的数量。multiCmd结构记录命令实现函数指针,命令的参数,已经参数的数量。事务队列以FIFO的方式保存入队命令,先入队的命令先执行。
执行事务
执行EXEC命令提交事务,服务器接收该命令之后:

  1. 遍历client.mstate.commands,获取已入队的命令并且挨个执行,把命令的返回值添加到回复队列尾部。
  2. 清空客户端的事务状态,包括清零入队命令计数器,释放事务队列。
  3. 把执行结果返回给客户端。

WATCH命令实现

WATCH命令是一个乐观锁,在执行EXEC之前监视制定的数据库键,在执行EXEC命令执行时检查被监视的键是否至少有一个已经被修改过,如果是的话,服务器拒绝执行命令,并向客户端返回代表事务执行失败的空回复。
Redis数据库的redisDb结构保存了一个watched_keys字典,字典的键时某个被WATCH命令监视的键,值是一个链表,链表中记录了所有监视相应数据库键的客户端。
在执行对数据库修改的命令时,执行之后会调用multi.c/touchWatchKey函数对watched_keys字典进行检查,查看是否有客户端正在监视刚刚被命令修改过的数据库键,如果有的话,touchWatchKey函数将监视被修改键的客户端的REDIS_DIRTY_CAS标识打开,表示该客户端的事务安全性已经被破坏。当服务器接收到一个客户端发来的EXEC命令时,服务器根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务。

事务的ACID

原子性:对Redis事务来说,事务中的多个命令被当做一个整体,事务队列中的命令要么全部被执行,要么都不执行,Redis事务时具有原子性的,但是Redis事务部支持回滚,即使队列中某个命令执行出现了错误也不影响后面的命令,整个事务会继续执行。
一致性:在命令入队错误、执行错误、服务器停机时都不会影响数据库的一致性。ps:当入队错误时对2.6.5之前的版本事务会继续执行,2.6.5之后事务会被拒绝执行
隔离性:Redis事务执行期间不会被其它命令中断,以串行的方式运行,Redis事务是具有隔离性的
持久性:

  • 当服务器运行在无持久化的内存模式下时,事务不具有持久性:一旦服务器停机,所有数据都会丢失
  • 当服务器运行在RDB持久化模式下时,当服务器停机时会有一段时间的数据会丢失,事务不具有持久性
  • 当服务器运行在AOF持久化模式下,并且appendsync选项值为always时,数据会被及时保存到硬盘中,此时事务具有持久性(前提是没有打开no-appendfsync-on-rewrite选项,打开该选项之后,为了尽可能的减少IO操作,当服务器执行BGSAVE或BGREWRITEAOF时,会停止AOF文件同步,所以此时事务也不具有持久性)
  • 当服务器运行在AOF持久化模式下,并且appendsync选项值为everysec时,可能会丢失1秒的数据,此时事务不具有持久性
  • 当服务器运行在AOF持久化模式下,并且appendsync选项值为no,服务器停机时数据会丢失,此时事务不具有持久性

慢查询日志

Redis 的慢查询日志功能用于记录执行时间超过给定时长的命令请求, 用户可以通过这个功能产生的日志来监视和优化查询速度。

服务器配置有两个和慢查询日志相关的选项:

  • slowlog-log-slower-than 选项指定执行时间超过多少微秒(1 秒等于 1,000,000 微秒)的命令请求会被记录到日志上。

    举个例子, 如果这个选项的值为 100 , 那么执行时间超过 100 微秒的命令就会被记录到慢查询日志; 如果这个选项的值为 500 , 那么执行时间超过 500 微秒的命令就会被记录到慢查询日志; 诸如此类。

  • slowlog-max-len 选项指定服务器最多保存多少条慢查询日志。

    服务器使用先进先出的方式保存多条慢查询日志: 当服务器储存的慢查询日志数量等于 slowlog-max-len 选项的值时, 服务器在添加一条新的慢查询日志之前, 会先将最旧的一条慢查询日志删除。

    举个例子, 如果服务器 slowlog-max-len 的值为 100 , 并且假设服务器已经储存了 100 条慢查询日志, 那么如果服务器打算添加一条新日志的话, 它就必须先删除目前保存的最旧的那条日志, 然后再添加新日志。

猜你喜欢

转载自wely.iteye.com/blog/2361639