Redis的5种基础数据结构:string (字符串)、list (列表 )、hash (字典)、 set (集合)、zset (有序集合)。
Redis所有的数据结构都以唯一的key字符串作为名称, 然后通过这个唯一key值来获取相应的value数据。
Redis字符串是可以修改的字符串,在内存中是以字节数组的形式存在,字符串最大长度为512MB。
Redis字符串的结构叫“SDS”(Simple Dynamic String),是一个带长度信息的字节数组。
struct SDS<T> {
T capacity; // 数组容量,表示所分配的数组长度
T len; // 内容的实际长度
byte flags; // 特殊标志位,不用理睬它
byte[] content; // 数组的内容
}
Redis列表的内存结构:在列表元素较少的时候,所有的元素彼此紧挨着—起存储,分配的是一块连续的内存,这个结构是“压缩列表”(ziplist)。当列表元素较多的时候,会改成“快速链表”(quicklist)。因为普通的链表需要的附加指针空间太大,会浪费空间,还会加重内存的碎片化,比如某普通链表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。所以Redis将多个ziplist使用双向指针串起来使用,既满足了快速的插入删除性能, 又不会出现太大的空间冗余。
Redis列表插入和删除操作非常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为O(n)。
Redis字典类似于Java语言里面的 HashMap,它是无序字典,内部存储了很多键值对,结构为 “数组+链表” 。
Redis字典的值只能是字符串。
Redis集合相当于Java 语言里面的 HasbSet,它内部的键值对是无序的、唯一的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值NULL。
Redis有序列表的内部实现用的是 “ 跳跃列表” ,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重。
list、set、hash、zset 这四种数据结构是容器型数据结构,它们共享下面两条通用规则:
Redis所有的数据结构都可以设置过期时间, 时间到了,Redis会自动删除相应的对象。
Redis同步的是指令流,主节点会将那些对自己的状态产生修改性影响的指令记录在本地的内存buffer中,然后异步将buffer中的指令同步到从节点,从节点一边执行同步的指令流来达到和主节点一样的状态,一边向主节点反馈自己同步到哪里了(偏移量)。因为内存的buffer是有限的,所以Redis主节点不能将所有的指令都记录在内存buffer中。Redis的复制内存buffer是—个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。如果因为网络状况不好,从节点在短时间内无法和主节点进行同步,那么当网络状况恢复时,Redis的主节点中那些没有同步的指令在buffer中有可能已经被后续的指令覆盖掉了,从节点将无法直接通过指令流来进行同步。
快照同步首先需要在主节点上将当前内存的数据全部快照到磁盘文件中,然后再将快照文件的内容全部传送到从节点。从节点将快照文件接受完毕后,立即执行一次全量加载。加载之前先要将当前内存的数据清空,加载完毕后通知主节点继续进行增量同步。在整个快照同步进行的过程中,主节点的复制buffer还在不停地往前移动,如果快照同步的时间过长或者复制buffer太小,都会导致同步期间的增量指令在复制buffer中被覆盖,这样就会导致快照同步完成后无法进行增最复制,然后会再次发起快照同步,如此极有可能会陷入快照同步的死循环。所以需要配置一个合适的复制buffer大小参数,避免快照复制的死循环。
Sentinel负责持续监控主从节点的健康,当主节点挂掉时,自动选择—个最优的从节点切换成为主节点。客户端来连接集群时, 会首先连接Sentinel,通过Sentinel来查询主节点的地址,然后再连接主节点进行数据交互。当主节点发生故障时, 客户端会重新向Sentinel要地址,Sentinel会将最新的主节点地址告诉客户端。如此应用程序将无须重启即可自动完成节点切换。
Redis主从采用异步复制,意味着当主节点挂掉时,从节点可能没有收到全部的同步消息,这部分未同步的消息就丢失了。
如果主从延迟特别大,那么丢失的数据就可能会特别多。Sentinel无法保证消息完全不丢失,但是也能尽量保证消息少丢失。
下面两个选项可以限制主从延迟过大:
min-slaves-to-write 1
min-slaves-max-lag 10
第一个参数表示主节点必须至少有一个从节点在进行正常复制,否则就停止对外写服务,丧失可用性。
第二个参数控制是否为正常复制,它的单位是秒(s),表示如果在10s内没有收到从节点的反馈,就意味着从节点同步不正常,要么是网络断开了,要么是一直没有给反馈。
Redis Cluster是Redis的“亲儿子”,它是Redis作者自己提供的Redis集群化方案,它去中心化的。
Redis Cluster将所有数据划分为16384个槽位,集群的每个节点负责其中一部分槽位,槽位的信息存储于每个节点中,当Redis Cluster的客户端来连接集群时,也会得到一份集群的槽位配置信息。这样当客户端要查找某个key时,可以直接定位到目标节点。
Redis Cluster可以为每个主节点设置若干个从节点, 当主节点发生故陓时,集群会自动将其中某个从节点提升为主节点。如果某个主节点没有从节点,那么当它发生故障时,集群将完全处千不可用状态。
Redis Cluster是去中心化的,一个节点认为某个节点失联了并不代表所有的节点都认为它失联了,所以集群还得经过一次协商的过程,只有当大多数节点都认定某个节点失联了,集群才认为该节点需要进行主从切换来容错。
Redis集群节点采用Gossip协议来广播自己的状态以及改变对整个集群的认知。
比如一个节点发现某个节点失联了(PFail即Possibly Fail),它会将这条信息向整个集群广播,其他节点就可以收到这点的失联信息。如果收到了某个节点失联的节点数量(PFail Count)已经达到了集群的大多数,就可以标记该失联节点为确定下线状态(Fail),然后向整个集群广播,强迫其他节点也接受该节点已经下线的事实,并立即对该失联节点进行主从切换。