Redis
基本数据类型,以及数据结构
String-动态字符串
Hash –字典
List-列表
Set-集合
Sorted-set有序集合 跳跃表
第一章动态字符串
Struct sdshdr{
Int len;//保存字符串的长度
Int free;//记录buf中未使用的字节的数量
char []buf;
}
动态字符串和c字符串的区别
- 获取字符串长度的时间复杂度为0(1);
- 杜绝缓冲区溢出,不够的话会自动扩容
- 减少修改字符串长度时所需要的内存分配次数,通过预分配,成倍,或者加1,1M为界线
- 二进制安全,是字节数组,存的是01二进制数据,所以不仅可以存文本,
第二章链表
Struct sdshdr{
//前置节点
Struct listNode *next;
//后置节点
Struct listNode *next;
Void *value;
}
特点:
- 双端
- 无环
- 带表头指针和表尾指针
第三章字典
Struct sdshdr{
//哈希表数组
DictEntry **table;
//哈希表大小
Unsigned long size;
//哈希表大小掩码,用于计算索引
//总是等于size-1;
Unsigned long sizemark;
Unsigned long used;
}
特定:
- 每个字典带有两个哈希表,一个平时使用,另外一个在rehash是时使用
- Rehash有渐进式的完成
第四章 跳跃表
跳跃表是一种有序的数据机构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问的目的。平均时间复杂度是o(logN).最坏是o(n)
在大部分情况下,跳跃表的效率可以和平衡二叉树媲美。而且实现还简单。
第五章整数集合
Typedef struct intset{
//编码方式
Uint 32_t encoding;
//集合中包含的元素数量
Uint_32_t length;
//保存元素的数组
Int8_t contents[];
}
- 整数集合可以保存的数据类型是int16_t,int32_t,int64_t的整数数值,并且保证集合中不会出现重复元素。
- 整数集合的底层是数组,这个数据以有序,无重复的的方式保存集合元素。
- 升级操作节约了内存。
- 整数集合只支持升级操作,不支持降级操作
第六章,压缩列表
Ziplist的数据结构
|
Zlbytes |
Zltail |
Zlen |
Entry1 |
Entry2 |
… |
entryN |
Zlend |
- Zlbytes整个压缩列表的占用内存字节数
- Zltail记录表尾节点距离起始地址有多远
- Zllen节点个数
- Entry的数据结构
|
previous_entry_length |
encoding |
content |
previous_entry_length记录前一个节点的长度。Encoding编码方式
- 压缩列表是为了节约内存而设计的
- 压缩列表可以包含多个节点,每个节点可以保存一个(字节数组)数组,或者整数值
- 因为previous_entry_length记录前一个节点的长度,如果前一个节点的长度发生变化,会导致previous_entry_length的变化,进而影响到自己。引起连锁更新,不过可能性比较低
第八章对象
- Redis数据库中每个键值对的键和值都是一个对象
- Redis共有字符串,列表,集合,有序集合,哈希,等五种类型的对象。
- Redis对象系统带有引用计数实现的内存回收机制,当一个对象不再使用时,该对象的所占用的内存空间将被释放
- 在创建一个新对象时,引用计数的值会被初始化为1;
- 当对象被一个新程序使用时,它的引用计数值会被增1
- 当对象不再被一个程序使用时,它的引用计数会减1
- 当对象的引用计数为0时,对象所占用的内存会被释放。
第九章 数据库
- Redis服务器的所有数据库都保存在redisServer.db数组中,而数据库的数量则由redisServer.dbnum属性保存。
- 数据库主要由dict和expire两个字典构成,其中dict字典负责保存键值对,而expire字典负责保存键的过期时间
- 因为数据库是由字典构成的,所以对数据库的操作都是建立在字典之上。
- 数据库的键总是一个字符串对象,而值可以是任意一种redis对象类型,包括,字符串对象、哈希表对象、集合对象、列表对象、和有序集合对象,分别对应字符串键、哈希表键、集合键、列表键、和有序集合键。
- Expire字典的键指向数据库中的某个键,而值则记录了数据库键的过期时间。
- Redis使用惰性删除和定期删除两种策略去删除过期的键,惰性删除策略只在碰到过期键才会进行删除操作,定期删除策略每隔一段时间主动查找并删除过期键
- 执行save命令或者bgsave命令所产生的新的RDB文件不会包含过期的键
- 当一个过期键被删除的时候,服务器会追加一条Del命令道AOF文件的末尾,显示的删除过期键。
- 当主服务器的一个键被删除之后,它会向所有从服务器发送一条Del命令,显示的删除过期键
- 从服务器即使发现过期键,也不会自作主张的删除它,而是等待主节点发来DEL命令,这种统一,中心化的过期删除策略可以保证主从服务器的一致性。
- 当redis命令对数据库进行修改之后,服务器会根据配置向客户端发送数据库通知。
第十章RDB持久化
Redis提供RDB持久化功能,这个功能可以将Redis在内存中的数据库状态保存到磁盘里,避免数据丢失。RDB文件是一个经过压缩的二进制文件。
- 因为Aof文件的更新频率比RDB的更新频率高,所以AOF开启,优先使用AOF.
- SAVE命令由服务器直接进行保存操作,该命令会阻塞服务器
- BGSAVE令子进程执行保存操作,所以该命令不会阻塞服务器。
第十一章AOF持久化
- AOF文件通过保存所有修改数据库的命令请求来记录服务器的数据库状态。
- 命令请求先保存到AOF缓存区里面,之后再定期的写入并同步到AOF文件。
- 右缓存到Aof文件这个更新频率可以调整,也就是缓存到AOF文件的更新频率
- AOF重写可以产生一个新的AOF文件,此时文件更小,因为可以合并多条执行语句为一条,多条语句合并成一句,结果是一样的。
- 重写操作是由子进程完成的,所以不需要加锁,在进行重写的时候设置两个缓存区,新Aof执行操作完毕之后,直接接受AOF重写缓冲区中的数据,这样的就可以达到一致性。
第十二章 事件
- 文件事件:Redis服务器通过套接字与客户端进行连接,而文件事件就是服务器对套接字的抽象。服务器和客户端的通信会产生相应的文件事件,而服务器通过监听处理这些事件而进行系列网络通信操作。
- 时间事件:就是Redis服务器对定时操作的抽象。
- Redis是基于Reactor模式开发的自己的网络时间处理器。,文件事件处理器使用IO多路复用程序同时监听多个套接字,
- 文件事件处理器由四个部分组成,分别是套接字,io多路复用,文件事件分配器,以及事件处理器
底层是通过epoll实现的
- 时间类型:套接字分为可读可写,或者既可读又可写;
- 文件事件的处理器:
第十三章 客户端
- 服务器状态结构使用clients链表连接多个客户端状态,新添加的客户端被放到链表尾部。
- 客户端通过flag属性来标记客户端的属性,以及客户端当前所处的状态
- 输入缓存区记录了客户端发送的命令请求,这个缓存区的大小不能超过1Gb
- 客户端的缓存区有两种个,一种固定,一个可变,固定是16kb,可变不能超过服务器设置的最大值
- 输出缓存区:执行命令所得的回复会被保存到客户端状态的输出缓存区。
- 当一个客户端通过网络连接上服务器,服务器会为这个客户端创建相应的客户端状态。
网络连接关闭、发送了不合协议规定的命令请求、成为clinet kill 命令的目标、空转时间超时、输出缓冲区的大小超过限制、以上这些原因都会导致客户端被关闭、
第十四章服务器
- 一个命令请求从发送到完成主要包括如下步骤:1)客户端将命令请求发送给服务器,2)服务器读取命令请求,并分析命令参数。3)命令执行器根据命令参数查找命令的实现函数,然后执行实现函数并得出命令回复4)服务器将命令回复返回给客户端
第十五章复制
- 旧版redis复制分为两步同步和命令传播
- 下图是同步阶段
在同步操作结束之后,主从服务器达到一致,但是如果主服务器执行客户端的命令,主从就会导致不一致,需要命令传播。
命令传播:
就是将主服务器执行的命令传播给从服务器
- 旧版的缺陷:从服务器断线重新连接,使用RDB文件进行一致更新,效率比较低。
- 新版的部分重同步功能由三部分组成:主服务器的复制偏移量和从服务器的复制偏移量、复制积压缓存区、服务器运行id/
- 主服务器通过从服务器传播命令来更新服务器状态,保持从服务器的一致,而从服务器通过向主服务器发送心跳检测,以及命令丢失检测。
第十六章 哨兵
哨兵是Redis高可用性的解决方案:由一个或者多个sentinal实例组成的sentinal系统可以监视任意多个主服务器,以及主服务器线下的所有从服务器,自动将从服务升级为主服务器。
- 哨兵会读入用户指定的配置信息,为每个要被监视的着急服务器创建相应的实例结构,并创建连向主服务器的命令连接,和订阅连接,其中命令连接主要向主服务器发送命令请求,而订阅连接则用于接受指定频道的消息
- 哨兵与哨兵之间只有命令连接,没有订阅连接,而哨兵与主服务器和从服务器建立命令连接和订阅连接。这是因为哨兵需要服务器发送来的频道信息来发现未知的新哨兵。
而相互已知的哨兵只需要命令连接来进行通信就可以了。
- 主观下线:默认情况下哨兵会以每秒一次的频率向所有与它创建命令连接的实例(主服务器,从服务器,其他哨兵)发送PING命令,超过五十秒没有回复,就判定为主观下线
- 客观下线:当哨兵将一个主服务器判定为主观下线之后,为了判定是否真正下线,它会向同样监视该主服务器的其他哨兵进行询问,如果达到一定数量(可配置的,每个哨兵不一样)的哨兵回复已经下线,那么该哨兵就会判定该主服务器客观下线,并执行故障转移。
- 选举领头哨兵:规则是先到先得,进行投票,超过哨兵的一般数量,就成为领头哨兵
- 故障转移中新主服务器的选择:删除下线的从服务器,删除五秒没有回复哨兵的从服务器,复制偏移量最大,id小的。
第十七章集群
Redis集群是Redis通过的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能。
- 节点通过握手将其他节点添加到自己所处的集群当中去。
握手的指令执行如下:
- 槽指派:Redis集群通过分片的方式来保存数据库中的键值对,集群中的整个数据库被分为16384个槽,数据库中每个键都属于这16384个槽中的任意一个,集群中每个节点可以处理0个最多16384个槽。每个节点都会记录哪些槽指派给了自己,而哪些槽又被指派给其他节点。
- 在集群中执行命令:节点在接受到一个命令请求时,会先检查这个命令请求要处理的键所在的槽是否由自己负责,如果不是的话,节点会向客户端发送一条MOVED错误,MOVED错误携带的信息可以指引客户转向正在负责相关槽的节点。
- 重新分片:重分配的关键是将属于某个槽的所有键值从一个节点转移到另一个节点。