读书笔记《Redis设计与实现》

Posted 在路上的德尔菲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了读书笔记《Redis设计与实现》相关的知识,希望对你有一定的参考价值。

数据结构

  • Redis支持键可以为String、list、hash、set、zset,5.0版本后还支持Pub/Sub 、Stream、GEO、bitmap、hyperloglogs。
  • dictEntry节点组成的链表没有指向链表表尾的指针,所以为了速度考虑,程序总是将新节点添加到链表表头位置(头插法时间复杂度O(1),排在其他节点的前面。
  • 当以下条件中的任意一个被满足时,程序会自动开始对哈希表做执行扩展操作:服务器目前没有做BGSAVE/BGREWRITEAOF命令,并且哈希表的负载因子大于等于;服务器目前正在做BGSAVE/BGREWRITEAOF命令,并且哈希表的负载因子大于等于5;当哈希表的负载因子小于0.1时,程序会自动开始对哈希表执行收缩操作。
  • 扩展或收缩哈希表需要将ht[0]里面的所有键值对rehash到ht[1]里面,考虑到避免rehash对服务器性能造成影响,是分多次、渐进式完成的。渐进式过程中,字典会同时使用ht[0]和ht[1]两个哈希表,如delete、find、update等操作会在两个哈希表上进行,如在字典里查找一个键的话,会先在ht[0]中进行查找,如果没有找到,就会继续到ht[1]里面进行查找。

  • Redis有两个地方泳道跳跃表,一个是zSet,另一个是集群节点中用作内部数据结构。
  • zskipList结构用于表示跳跃表节点,header指向跳跃表头结点,tail指向跳跃表尾结点,level记录目前跳跃表内层数最大的那个节点层数,length记录跳跃表的长度,即跳跃表当前包含节点的数量;zskiplistNode
  • 字符串对象的编码可以使用int、raw或者embstr,如果一个字符串对象保存的整数值,并且这个整数值可以用long类型来表示,此时字符串对象的编码设置为int;如果字符串对象保存的是一个字符串值,且长度大于32字节,编码设置为raw;如果字符串对象保存的是一个字符串值,且长度小于等于32字节,编码设置为embstr,embstr专门保存短字符串的一种优化编码方式。

  • 列表对象(list)的编码可以是zpilist或者linkedlist。当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:列表对象保存的所有字符串元素的长度都小于64字节;列表对象保存的元素数量小于512个。
  • 哈希对象(hash)的编码可以是ziplist或者hashtable,使用ziplist保存了同一键值对的两个节点总是紧挨在一起的,保存键的节点在前,保存值的节点在后。先添加到哈希对象中的键值对会放在压缩列表的表头方向,而后来添加到哈希对象中的键值对会放在压缩列表的表尾方向。
  • 集合对象(set)的编码可以是intset和hashtable。intset编码集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面。hashtable编码集合对象使用字典作为底层实现,字典的每个键都是一个人字符串都想,每个字符串对象包含了一个集合元素,而字典的值全部都设置为null。
  • 有序集合(zset)编码使用ziplist或者skiplist,ziplist每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,而第二个元素则保存元素的分值score。按分值从下到到进行排序,分值较小的元素被放置在靠近表头的方向,同理分值较大的元素被放置在靠近表尾的方向。
  • 使用skiplist,字典的键保存了元素的成员,而字典的值则保存了元素的分值。通过字典,可O(1)复杂度找给定成员的值,zscore命令根据这一特性实现的。

存储

  • RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态。RDB文件的创建:SAVE会阻塞Redis服务器进程,知道RDB文件创建完毕为止,在服务器阻塞期间,服务器不能处理任何命令请求。RDB文件的载入:在服务器启动时自动执行的,只要Redis服务器检测到RDB文件存在,他就会自动载入RDB文件。
  • 在执行SAVE或者BGSAVE命令创建一个新的RDB文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的RDB文件中,同理AOF。
  • RDB持久化保存数据库的状态将msg、fruits、number三个键地键值对保存在RDB文件中,而AOF持久化是将服务器执行的SET、SADD、RPUSH三个命令保存到AOF中。
  • 如果服务器开启了AOF,服务器会优先使用AOF文件来还原数据库状态。只有AOF处于关闭状态,才会使用RDB还原数据库状态。
  • AOF功能分为命令追加(append)、文件写入、文件同步(sync)
  • 为了解决AOF文件体积膨胀问题,Redis提供了AOF文件重写功能,首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令。

集群

  • Redis复制功能分为同步(sync)和命令传播(command propagate)两个操作:同步操作用于将从服务器的状态更新至主服务器当前所处的数据库状态;命令传播操作用于在主服务器的数据库状态被修改,导致主从服务器的数据率状态出现不一致时,让主从服务器的数据率重新回到一致状态。
  • sync命令非常耗费资源操作,1)主服务器需执行BFSAVE命令生成RDB文件,耗费主服务器大量CPU、内存和磁盘IO资源;2)主服务器需要将自己生成的RDB文件发送给从服务器,耗费主从服务器大量网络资源(带宽和流量);3)接收到RDB文件的从服务器需要载入主服务器发来的RDB文件,期间从服务器会因为阻塞而没办法处理命令请求。

  • 新版使用psync代替sync,psync具有全量同步和增量同步,全量同步和sync一致,增量同步用于处理断线后重复制情况,主从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送从服务器,从服务器接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态。

  • Sentinel哨兵是Redis高可用性的解决方案,由一个或多个Sentinel实例组成Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
  • Sentinel本质是一个运行在特殊模式下的Redis服务器,所以启动Sentinel的第一步,就是初始化一个普通的Redis服务器。普通服务器在初始化时会通过载入RDB文件或者AOF文件来还原数据库状态,但是因为Sentinel并不使用数据库,所以初始化Sentinel时就不会载入RDB文件或者AOF文件。
  • Sentinel默认会以每十秒的频率,通过命令连接向背监视的主服务器发送INFO命令,并通过分析INFO命令的回复来获取主服务器的当前信息。

  • Redis集群是Redis提供的分布式数据库方案,集群通过分片sharding来进行数据共享,并提供复制和故障转移功能。分为主节点和从节点,主节点用于处理槽,而从节点用于复制某个主节点,并在对应主节点下线时,代替下线的主节点继续处理命令请求。
  • 集群中每个节点都会定期地向集群中其他节点发送PING消息。
  • 如果一个集群中,半数以上负责处理槽的主节点都将某个主节点X报告为疑似下线,那么这个主节点将被标记为已下线FAIL,将主节点X标记为已下线的节点会向集群广播一条关于主节点X的FAIL消息,所有收到这条FAIL消息的节点都会立即将主节点X标记为已下线。
  • Redis集群中的各个节点通过Gossip协议来交换各自关于不同节点的状态信息,其中Gossip协议由MEET、PING、PONG三种消息来实现。
  • MEET消息:当发送者到客户端发送的CLUSTER MEET命令时,发送者会向接受者发送MEET消息,请求接受者加入到发送者当前所处的集群里面。
  • PING消息:集群里每个节点默认每隔一秒钟就会从已知节点列表中随机选出五个节点,然后对这五个节点中1最长时间没有发送过PING消息的节点发送PING消息,以此来检测被选中的节点是否在线。除此之外,如果节点A最后一次收到节点B发送的PONG消息的时间,距离当前时间已经超过了节点A设置的超时时间的一般,那么A节点也会向节点B发送PING消息,可以防止节点A因为长时间没有随机选中节点B作为PING消息的发送对象而导致对节点B的信息更新滞后。
  • Redis将所有频道的订阅关系都保存在服务器状态的PUBSUB_CHANNEL字典里面,这个之巅的键是某个被订阅的频道,而键的值是一个链表,链表里面记录了所有订阅这个频道的客户端。

事务

  • Redis通过MULTI、EXEC、WATCH命令来实现事务功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,他会将事务中的所有命令都执行完毕,然后才会去处理其他的命令请求。
  • 一个事务从开始到结束的三个阶段:1)事务开始;2)命令入队;3)事务执行。
  • 如果客户端正处于事务状态,且这个命令是否为EXEC、DISCARD、WATCH、MULTI,立即执行这个命令,否则将命令放入事务队列,向客户端返回QUEUED。
  • WATCH命令是一个乐观锁,他可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务失败的的空回复。
  • Redis事务和传统的关系型数据库事务最大区别在于,Redis不支持事务回滚机制rollback,即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,知道将事务队列中所有命令都执行完毕。//关系数据库原子性定义为要么做,要么不做,但是Redis原子性做就做完。Redis的缺点主要体现在并不支持完整的ACID事务,Redis虽然提供事务功能,但Redis的事务和关系数据库的事务不可同日而语,Redis的事务只能保证隔离性和一致性(I和C),无法保证原子性和持久性(A和D)。
  • Redis使用单线程的方式来执行事务,并且服务器保证在执行事务期间不会对事务进行中断,因此Redis事务总是以串行的方式运行的,并且事务总是具有隔离性的。
  • Redis事务不过是简单地用队列包裹一组Redis命令,Redis并没有为事务提供任何额外的持久化功能,Redis事务持久性由Redis所使用的持久化模式决定的。不论Redis在什么模式下运作,在一个事务的最后加上SAVE命令总可以保证事务的持久性。//这种做法效率太低,不具有实用性。

以上是关于读书笔记《Redis设计与实现》的主要内容,如果未能解决你的问题,请参考以下文章

读书笔记-《Redis设计与实现》数据结构与对象

读书笔记-《Redis设计与实现》数据结构与对象

Redis设计与实现读书笔记 SDS

读书笔记《Redis设计与实现》

读书笔记《Redis设计与实现》

大话设计模式读书笔记--19.责任链模式