《Redis设计与实现》_笔记


数据结构:sds、linkedlist、hashtable、skiplist、intset、ziplist
对象:string(位数组)、hash、list、set、zset、hyperloglog(键必须以key结尾)
单机:备份、过期、消息、事件
多机:复制、哨兵、集群
其他:发布订阅、事务、排序、二进制位数组、慢查询日志、监视器
命令:通用命令、键值命令、组件命令、集群命令

第一部分 数据结构与对象

2 简单动态字符串

Simple Dynamic String
redis字符串分两类:字面量字符串(无须修改,C语言字符串)、简单动态字符串(SDS,可修改)。
SDS属性:len已使用的长度,free未使用的长度,buf字节数组
2.2 SDS与C字符串的区别
获取字符串长度复杂度为O(1)。C为O(N),遍历字符串。
杜绝缓冲区溢出。根据free判定空间是否足够。
优化内存分配,空间预分配,惰性空间释放。
二进制安全,字节方式保存。可保存\0字符,以len判定结尾。
兼容部分C字符串函数。

3 链表

链表是列表键的底层实现之一。
链表节点属性:prev前置节点,next后置节点,value节点值
链表属性:head表头节点,tail表尾节点,len节点数量。
redis链表特性:双端无环,带表头表尾指针、表长度,多态。

4 字典

字典是数据库、哈希键的底层实现之一。
结构
字典,又称符号表、关联数组、映射;是一种保存键值对的数据结构。
Redis字典,使用哈希表作为底层实现。
哈希表属性:table表节点链表数组,size表大小,sizemask表掩码(计算索引),used已有节点数量
哈希表节点:key键,v值,next下一个节点
字典属性:type类型特定函数,privdata类型特定函数参数,ht哈希表及rehash表,trehashidx rehash索引,rehash不在进行时为-1
算法
哈希算法,键值key映射为数值;Redis采用MurmurHash算法随机分布性好。
Redis哈希表使用链地址法解决键冲突,哈希节点用next构成单向链表。新节点添加到表头(复杂度O(1))
rehash,对哈希表进行扩容/收缩,此时rehash不为-1。类似HashMap扩容缩容。
渐进式rehash,rehash期间,插入只在ht[1],删改查会在两个哈希表上进行。
rehash类似CopyOnWrite。

5 跳跃表

跳跃表,一种有序的数据结构;通过每个节点中维持多个指向其他节点的指针,达到快速访问节点的目的。
跳跃表平均复杂度O(logN),最坏O(N);还可通过顺序操作批量处理节点。
大部分情况下,跳跃表效率可以和平衡树媲美。
Redis使用跳跃表作为有序集合键的底层实现;若有序集合集合中元素较多,或元素较长,使用跳跃表。
结构
跳跃表属性:header表头,tail表尾,level节点最大层数,length长度。level、length不含表头节点。
表节点属性:level层(表尾方向同层节点指针、跨度),BW后退指针(表头方向指针),scope分值,obj成员对象(字符串SDS)。
表头到表尾,按level层跳跃访问,多层跳跃的链表;表尾到表头,按BW顺序访问,链表。
层高,每个跳跃表节点的层高都是1–32间的随机数。
跨度,用于计算排位。
跳跃表排位顺序:先分值升序、后对象升序。
查找
https://blog.csdn.net/lf_2016/article/details/74999564
跳表L1为数据层,L1以上层可理解为稀疏重复索引;查找时由高到低查询,找到节点则停止。
类似有重复非叶节点的B树。

6 整数集合

整数集合intset是集合键的底层实现之一。当一个集合只包含整数值,且元素数量不多时,使用整数集合作为集合键的底层实现。
整数集合属性:encoding编码方式,length集合元素数量,contents升序无重复的元素数组
升级:整数集合编码方式INTSET_ENC_INT16/32/64;当新加入数值超过当前编码方式值域,需更改编码方式,以保存新元素。引起升级的新元素保存在整数集合的开头、或结尾。
升级的好处:自适应数据类型、节约内存。
不支持降级操作。

7 压缩列表

压缩列表是列表键和哈希键的底层实现之一。
压缩列表是由连续内存块组成的顺序型数据结构。
压缩列表属性:zlbytes压缩列表字节数,zltail表头表尾字节数,zllen节点数,entryX节点,zlend列表末端(0xFF)。
压缩列表节点:previous_entry_length前节点长度,encoding节点数据类型及长度,content节点内容

8 对象

Redis对象系统:string、hash、list、set、zset。
对象属性:type类型,encoding编码,ptr底层对象指针,refcount引用计数,lru最后访问时间
Redis数据库的键为字符串对象;type查看值对象类型,object encoding查看值对象底层数据结构。
底层数据结构:sds、linkedlist、hashtable、skiplist、intset、ziplist

类型编码/REDIS_ENCODING_输出
stringINT、EMBSTR、RAWint、embstr、raw
listZIPLIST、LINKEDLISTziplist、linkedlist
hashZIPLIST、HTziplist、hashtable
setINTSET、HTintset、hashtable
zsetZIPLIST、SKIPLIST(+HT)ziplist、skiplist

quicklist,ziplist和linkedlist的结合;将linkedlist切分,用ziplist紧凑存储,并通过双向指针串接。
zset多元素时,通过集合hashtable、skiplist方式实现,前者查score,后者查范围。
Redis通过引用计数实现内存回收(refcount=0回收)、对象共享(只共享整数类型字符串,refcount+1)

第二部分 单机数据库的实现

redis结构:server–db–keyspace–entry
备份过期消息事件

9,数据库

redis服务器属性:db数据库数组,dbnum数据库数量(默认16)
redis客户端属性:db执行当前使用的数据库,
redis数据库属性:dict数据库键空间,expires过期字典,
select命令,选择数据库;默认0
键空间:保存数据库中所有键值对的字典;对数据库的操作通过键空间实现。
9.4 过期时间
expire/expireat,pexpire/pexpireat;设置过期时间s,ms。最终都转为pexpireat执行。
数据库属性expires,过期字典;键为指向键空间键的指针,值为过期时间(long类型ms)
persist,移除过期时间;将键值从过期字典中移除。
ttl,pttl;剩余生存时间s,ms
过期键删除策略:定时删除(一键一定时器)、惰性删除(get时判定)、定期删除(限制频率、时长);redis采用惰性删除、定期删除。
淘汰:内存满时删除键值对。
9.7 AOF、RDB和复制功能对过期键的处理
RDB,save/bgsave创建rdb文件时,忽略过期键;载入时主服务器忽略过期键,从服务器正常载入(主从同步时清空从服务器,故无影响)。
AOF,执行删除操作时才记录DEL命令;重写时忽略过期键。
复制,从服务器只在收到主服务器DEL命令后才删除过期键。不自己执行惰性删除、定期删除,确保主从一致。
9.8 数据库通知
subscribe channel;订阅给定频道。java中订阅是一次性的。

  • __keyspace@0__:keyName;订阅数据库0指定键全部事件。
  • __keyspace@0__:del;订阅数据库0删除事件

notify_keyspace_events;配置服务器发送通知的类型。

10,RDB持久化

Replication DataBase
redis是内存数据库;RDB(二进制压缩文件)、AOF(日志)持久化。
save、bgsave;创建rdb文件。save服务器进程执行,阻塞,bgsave派生子进程,不阻塞。
rdb存储当前时刻数据,副本写入。
bgsave执行时,拒绝客户端save、bgsave命令,延迟客户端bgrewriteaof命令。bgrewriteaof执行时拒绝bgsave。
rdb文件在服务器启动时自动载入,载入完成前阻塞服务器。优先使用aof还原数据库状态。
save配置文件参数,设置自动持久化bgsave策略。

11,AOF持久化

Append Only File;通过保存写命令来记录数据库状态。
RDB–mysql基于行,AOF–mysql基于sql。
11.1 AOF持久化的实现
AOF实现过程:命令追加、文件写入、文件同步。参考RandomAccessFile写入。

  • 命令追加;服务器执行命令后将其追加到aof_buf缓冲区末尾。
  • 文件写入;redis将aof_buf写入文件。
  • 文件同步;redis服务器根据appendfsync,将文件内容同步到磁盘。

appendfsync值:

  • always;写入并同步
  • everysec;写入,每秒同步
  • no;只写入,同步由操作系统决定

写入和同步:现代操作系统,写文件时,通常将数据写入内存缓冲区,等到缓冲区满或者超过指定时限后,再写入磁盘。也可调用函数立即写入。
AOF中保存redis命令,redis服务器启动后,开启一个伪客户端读取AOF,执行命令。
11.3 AOF重写
读取数据库内容,生成redis命令写入AOF。忽略过期键。
为避免执行命令时客户端溢出,对list、hash、set、zset等,元素数量超过指定值后,拆分为多个命令插入值。
通过AOF重写缓冲区,存储重写过程中的命令;最后添加到aof重写文件中。

12,事件

Redis服务器是一个事件驱动的程序;需处理以下两类事件:

  • 文件事件;客户端操作的抽象。连接、读、写、关闭等。
  • 时间事件;定时操作的抽象。

服务器轮流执行文件事件、时间事件。
12.1文件事件
文件事件处理器以单线程运行;使用多路复用监听多个socket。java NIO。
文件事件处理器组成:socket、IO多路复用程序、文件事件分派器、事件处理器。
IO多路复用程序将socket产生的事件放到一个队列中,有序同步的发给分派器,每次一个socket。
若一个socket可读可写,则先读后写。
12.2 时间事件
Redis时间事件分两类:定时事件、周期性事件。
时间事件属性:id全局唯一标识(递增),when到达时间(ms),timeProc时间事件处理器
服务器将所有事件放在链表中,新事件加在表头;遍历链表执行事件,执行后删除定时事件,更新周期性事件when属性。
时间事件的执行事件一般会比到达时间晚。服务器一般情况下只执行serverCron周期性时间事件。

第三部分 多机数据库的实现

复制哨兵集群

15,复制

slaveof命令,配置文件slaveof参数;设置主从复制。slave no one,设为主服务器。
sync/psync复制,命令传播。
replconf ack;心跳检测。
15.1 旧版复制
旧版复制:同步(复制数据库)、命令传播。

  • 同步;从向主发sync命令;主机bgsave,发送rdb到从;从装载rdb。
  • 命令传播;写操作(增删改)发送到从。同步期间写操作写入缓冲区,同步后发从。

缺陷:断线重连后重新同步,消耗大量资源。
15.3 新版复制
psync;替代sync,完整重同步、部分重同步。
部分重同步构成:

  • 主服务器的复制偏移量、从服务器的复制偏移量;记录数据位置
  • 主服务器的复制积压缓冲区;固定长度FIFO队列,存储写命令。若未同步数据都在队列中,部分重同步,否则完整重同步。
  • 服务器ID;初次同步时,主服务器发送ID到从;psync时,从发送此ID到主,判定是否同一主服务器。

15.7 心跳检测
命令传播阶段,从服务器每秒向主服务器发送一次命令:

  • replconf ack <replication_offset>;replication_offset为偏移量

replconf ack命令作用:

  • 检测主从服务器的网络连接状态;info replication,列出从服务器状态
  • 辅助实现min-slaves;配置:min-slaves-to-write、min-slaves-max-lag
  • 检测命令丢失;通过偏移量,重新发送缺失数据。类似部分重同步。

16 ,Sentinel

哨兵启动,命令连接(获取状态、发布消息、在线判定及哨兵选主、故障转移),订阅连接(发现其他哨兵)。
配置文件,sentinel.conf。
16.1 启动及初始化
redis-sentinel sConf,redis-server conf --sentinel;启动哨兵
sentinel初始化步骤:

  • 初始化服务器;sentinel是运行在特殊模式下的Redis服务器,不使用数据库。
  • 使用Sentinel专用代码;端口26379,载入部分命令。
  • 初始化Sentinel状态;
  • 初始化Sentinel监视的主服务器列表;
  • 创建连向主服务器的网络连接;命令连接(监控服务器状态,伪客户端)、订阅连接(监听__sentinel__:hello,发现哨兵)。

16.2 获取信息
Sentinel默认每10S向主服务器发送info命令,获取主服务器信息:id、role、slaves(自动发现从服务器)等。
Sentinel默认每10S向从服务器发送info命令,获取从服务器信息,更新状态。
Sentinel默认每2S向主从服务器发送publish命令(哨兵、服务器信息),向__sentinel__:hello频道发送消息。sentinel监听频道消息,丢弃自身,接受他哨,更新服务器状态(哨兵字典)。
当Sentinel通过频道发现新哨兵时,相互创建命令连接;进行信息交换。
16.6 检查下线状态
Sentinel默认每1S向命令连接的实例发送ping命令,检测在线。down-after-milliseconds,主观下线时长。
sentinel is-master-down-by-addr;检测到主观下线后,向其他哨兵询问主服务器下线状态。主观下线哨兵数达到配置值quorum时,判定客观下线,选举领头sentinel(半数以上),进行服务器故障转移。
故障转移步骤,slave of命令:

  • 选出新主服务器;剔除断线及超时服务器,按优先级、最大偏移量、id选主。slave of no one。
  • 复制新主服务器;slave of命令。
  • 已下线主设为从;上线时发送slave of命令。

17,集群

开启集群功能;配置文件redis cluster部分。
必须打开集群端口,客户端端口+10000。
17.1 节点
开启集群后redis的每个节点,开始时都处于一个只包含自己的集群中。
cluster meet;连接节点,组成集群。两节点握手,而后Gossip传播。
cluster nodes;查看集群节点
集群数据:clusterState集群状态,clusterNode节点状态,clusterLink连接节点的信息。每个节点都有自己的集群数据,clusterState–clusterNode–clusterLink。
17.2 槽指派
集群数据库被分为16384个槽;每个节点可处理0-16384槽。槽号:0-16383
集群上线状态(ok),16384槽都有节点处理;集群下线状态(fail),有槽未分配有节点处理。
cluster addslots;分配槽位。槽位信息记录在clusterNode.slots,二进制位数组,2KB。clusterState.slots;集群槽位指派信息,16384项,每项都指向一个clusterNode。
cluster keyslot;根据key计算槽位。计算key中{K}部分,或者整个key的hash值。
redis通过重定向的方式,引导客户端连接正确的节点。
clusterState.slots_to_keys;跳表保存本节点数据库键-槽位信息;槽位为分值。方便批量操作槽。
重新分片,由redis-trib软件实现。
cluster replicate;设置集群节点的从节点
主节点下线判定、故障转移、选主;类似哨兵机制。

第四部分 独立功能的实现

18,发布与订阅

publish;发布。订阅时创建消息队列。0失败1成功。
subscribe,unsubscribe;订阅,退订;单订。
psubscribe,punsubscribe;订阅,退订;topic模式。
pubsub channels/numsub/numpat;查看订阅发布信息,读取pubsub_channels、pubsub_patterns实现。需先订阅,
18.1 订阅与退订
redisServer.pubsub_channels属性,dict;保存所有频道的订阅关系。key为频道,val为客户端链表。
redisServer.pubsub_patterns属性,list;保存所有模式订阅关系。链表元素包含pattern、client。
发送消息时,先找到订阅者,遍历发送;而后遍历模式订阅链表,判定发送。

19,事务

multi-exec-watch;提供事务功能,命令按序执行,事务执行中不响应其它客户端请求。
事务阶段:事务开始,命令入队,事务执行(此时不响应其它客户端)。
multi;开始事务
exec;提交事务
discard;取消事务
watch;乐观锁,监视指定键;若exec前有键被改变,则拒绝执行事务。标志位方式实现。
19.3 Redis事务的ACID性质
A;原子性,redis事务要么全部执行,要么全部不执行;不支持回滚。exec前出错不执行,exec后出错不回滚。
C;一致性,入队出错不执行,exec出错不影响数据,关机重启后通过备份执行。
I;隔离性,单线程串行执行事务。
D;持久性,RDB、AOF持久化;但仅AOF-always方式写,有持久性

21,排序

sort ;排序,将排序键值载入数组,通过快速排序排序。
alpha;字符串排序
asc、desc;升序,降序
by pattern;指定键值作为权重,如:*-price,*为排序元素
limit offset count;数量限制
get pattern;排序后查找键值返回。
store dest;将结果保存至指定键,数组。
多个选项执行顺序:

  1. 排序,alpha、asc/desc、by。
  2. 限制长度,limit
  3. 获取外部键,get
  4. 保存并展示,store

22,二进制位数组

setbit、getbit;设置、获取
bitcount;统计1个数
bitop;二进制位数组运算,与或非、异或。bitop and dst a b
redis使用字符串表示位数组;SDS逆序保存位数组。并使用SDS结构的操作函数处理位数组。
a,97,01100001;从高位到低位保存为位数组。
offset/8,定位字节;offset%8+1,定位位。setbit时若位补足则扩容,补0。

23,慢查询日志

配置:

  • slowlog-log-slower-than,慢查询时间;
  • slowlog-max-len;慢查询日志最大数量,达到后删除最旧日志。

slowlog get;查看慢查询日志。redisServer.slowlog日志链表保存,新日志加到表头。
slowlog len;日志数量
slowlog reset;清除日志

24,监视器

monitor;将客户端转为监视器
redisServer.monitors;监视器链表。服务器处理请求时遍历链表发送相关信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值