1.数据结构
1.1.简单动态字符串:
其属性有int len:长度,int free:空闲长度,char[] bur:字符数组(内容)
获取字符串长度简单;
杜绝缓冲区溢出;
减少修改字符串长度时所需的内存重分配次数;
二进制安全;兼容部分C字符串函数;
1.2.链表:使用listNode与list来实现
listNode其属性有 struct listNode *prev:前一个节点,struct listNode *next:后一个节点,void *value:当前节点值
list其属性有 listNode * head:头节点,listNode * tail:尾节点,long len:节点数量(长度)
1.3.字典:
根据k获取hash值,然后根据hash&sizemask获取dictht中table的下标,若冲突时dictEntry有链表指向下一个,
在对哈希表进行扩展或者收缩操作时,程序需要将现有哈希表包含的所有键值对rehash到新hash表中,且是渐进式完成的
1.4.跳跃表:有序集合的底层实现,由zskiplist和zskiplistNode组成,zskiplist保存跳跃表信息,如表头节点,表尾节点,长度;
1.5.整数集合:集合键的底层实现之一,底层实现为数组,以有序无重复的方式保存元素
1.6.压缩列表:列表键和哈希键底层实现之一,包含多个节点,每个节点可以保存一个字节数组或整数值
2.对象:每个对象都由redisObject结构表示
其属性有unsigned type:类型,encoding:编码,void*ptr:指向底层实现数据结构的指针
type(类型指的是存储的值即value的类型)的值有REDIS_STRING:字符串,REDIS_LIST:列表,REDIS_HASH:哈希,REDIS_SET:集合,REDIS_ZSET:有序集合
encoding(决定指针指向那种数据结构)的值有 编码所对应的底层数据结构
REDIS_ENCODING_INT long类型的整型
REDIS_ENCODING_EMBSTR embstr编码的简单字符串
REDIS_ENCODING_RAW 简单动态字符串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 双端链表
REDIS_ENCODING_ZIPLIST 压缩列表
REDIS_ENCODING_INTSET 整数集合
REDIS_ENCODING_SKIPLIST 跳跃表和字典
其属性有refcount:计数,用于内存回收及对象共享
内存回收:
引用计数信息,程序可以通过跟踪对象的引用计数信息,在适当的时候自动释放对象并进行内存回收
当创建新对象时为1;被新程序使用时,自增1;不再被使用,自减1;当为0时,内存释放;
对象共享:
将数据库的值指针指向一个现有的值对象;让两个键指向同一个字符串对象;引用计数加1;
redis在初始化服务器时,创建一万个字符串对象,包含了从0-9999的所有整数值,服务器会使用这些共享对象,而不新建
其属性有lru:记录了对象最后一次被命令程序访问的时间,回收内存时使用
服务器若配置maxmemory选项,服务器用于回收内存的算法为volatile-lru或者allkeys-lru,则超过设置上限时,空转时长高的被释放掉回收内存
3.数据库:
服务端struct redisServer
其属性redisDb *db:服务器所有数据库的数组
其属性int dbnum:服务器的数据库数量
客户端struct redisClient
其属性db:指向服务端中数组的某一个数据库
数据库键空间
typedef struct redisDb {
dict *dict; /* The keyspace for this DB */键空间
...
} redisDb;
针对数据库的操作 实际对dict字段进行操作实现的
键生存及删除
通过expire或pexpire命令指定键的生存时间
三种删除策略:
定时删除,设置过期同时创建定时器,过期即删除。对cpu不友好
惰性删除,键过期不管,获取该键检查,若过期则删除,未过期则返回。对内存不友好
定期删除,定期检查数据库,检查时有哪些过期则删除。需合理设置删除执行时长及频率
4.持久化:将数据库中的数据保存至磁盘中
1.RDB持久化
1.RDB文件的创建与载入:通过保存数据库数据来记录数据库状态
生成,SAVE及BGSAVE命令
SAVE阻塞服务器进程,直至RDB文件创建完成
BGSAVE派生一个子进程,由该进程创建RDB文件,服务器进程继续处理其他命令
载入,在服务器启动时自动载入RDB文件(未开启AOF持久化时)
2.在redis.conf配置中可以设置自动间隔时间保存:如下配置
save 900 1 900秒有一次修改则执行
save 300 10 300秒内有10次修改则执行
save 60 10000 60秒内有10000此修改则执行
serverCron每隔100毫秒执行一次检查save的配置条件是否满足,只要有一项满足则执行BGSAVE
3.RDB文件结构
REDIS | db_version | databases | EOF | check_sum
常量 版本4字节 包含0个或多个数据库 常量 8字节无符号整数
其database数据库结构如下
SELECTDB | db_number | key_value_pairs
常量 数据库号码 所有键值对
....
2.AOF持久化:通过保存redis服务器所执行的写命令来记录数据库状态
1.命令追加
redisServer中有aof_buf属性用于存储服务器执行的相关命令
2.文件写入与同步
服务器在执行完一个客户端请求事件前,会考虑是否将aof_buf缓冲区的内容写入到文件中、
服务器有配置项appendfsync,其值可以为
always:将aof_buf所有内容写入并同步到文件
everysec:所有内容写入文件,上次同步事件超过一秒,则专门有一个线程负责同步
no:写入到文件中,但不同步,由操作系统决定何时同步
3.AOF重写:
文件越来越大越来越多,重写一个新的 将命令合并,
如两个不同时间对同一集合操作则AOF文件中有两条命令,此则可以合并为一条命令
后台重写:父进程继续处理命令,自进程进行重写,在重写期间,父进程处理的命令放入AOF缓冲区中,之后再写入并同步到AOF文件。
5.事件:redis服务器是一个事件驱动程序
1.文件事件:服务器对套接字操作的抽象
I/O多路复用程序监听多个套接字,放入一个有序同步队列中,每次将队列中一个套接字传送给文件事件分派器,文件分派器根据产生的时间调用相应的事件处理器
最常用的事件处理器:
连接应答处理器,客户端连接监听套接字,服务器监听套接字 产生AE_READABLE事件,执行该处理器
命令请求处理器,客户端发送命令请求,服务器监听套接字 产生AE_READABLE事件,执行该处理器
命令回复处理器,客户端套接字产生AE_WRITABLE事件,执行该处理器
2.时间事件:定时时间(执行一次)及周期性事件(每隔多长时间执行一次)。
由三属性组成,id:标识,when:需要执行的时间,timeProc:需要处理的事情
所有时间事件放置在无序链表中,执行遍历所有链表,到时间的事件则执行
目前redis服务器中有时间事件应用实例serverCron函数,周期性事件运行,每隔一段时间会执行一次,直至服务器关闭
在redis.conf配置中有hz的选项,每秒执行该函数的次数,可以配置,默认10次
文件事件与时间事件为合作关系,服务器轮流处理这两种时间,处理中不会抢占,所有时间事件可能会被设定的时间完.
6.客户端:struct client
通过连接redis 输入client list命令出现如下
id=2 addr=192.168.37.134:43836 fd=6 name= age=181355 idle=1 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=replconf
id=14 addr=192.168.37.134:50888 fd=7 name= age=258 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
id=15 addr=192.168.37.1:55242 fd=8 name= age=4 idle=4 flags=N db=15 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=select
该服务器中有三个客户端在连接
id:唯一标识,addr:连接地址,fd:标志,-1为伪客户端(如aof或lua脚本),age:连接服务器时长,flags:角色标志(如S代表从服务器),db:数据库号码,qbuf:输入缓冲区,cmd:最后执行的命令
7.服务器:负责与多个客户端建立连接,保存数据,通过资源管理维持服务器运转
1.命令执行过程
1.服务器读取到resp协议内容,保存至客户端的输入缓冲区,对缓冲区的命令进行分析,将参数和参数个数
2.保存至客户端argv属性和argc属性里
3.调用命令执行器,执行命令:1.查找命令,2.执行预操作,3.调用命令实现函数,4.执行后续工作
4.客户端套接字可写状态,回复命令给客户端
2.serverCron函数
更新服务器状态信息,管理客户端资源和数据库状态,检查执行持久化
3.服务器启动
1.初始服务器状态
2.载入服务器配置
3.初始服务器数据结构
4.还原数据库状态
5.执行事件循环
8.复制
全部同步
1.从服务器发送SYNC命令,
2.主服务器收到从服务器的命令生成一个RDB文件,且记录现在开始执行的所有写命令道缓冲区
3.主服务器发送RDB文件至从服务器,从服务器载入RDB
4.主服务器将缓冲区的写命令发送至从服务器
部分同步:高效处理主从断线后重复制情况
通过复制偏移量\复制积压缓冲区\服务器运行id三部分来实现
9.哨兵: 由一个或多个sentinel实例监视主服务器及其所属从服务器,主服务器下线则将某个从服务器提升为主
1.初始化实例时记录所有被sentinel监视的主服务器相关信息,并创建两个链接至主服务器,命令链接和订阅链接
2.默认10秒一次通过命令链接发送info命令获取主服务器信息
3.获取到从服务器信息后创建到从服务器的命令链接和订阅链接,通过命令链接获取到从服务器信息保存至实例中
4.配置中配置主服务器是否客观下线,当判定为下线,监视的各个sentinel协商选举领头sentinel对下线主服务器故障转移(raft算法)
5.领头sentinel从主服务器的所有从服务器中挑选一个转换为主服务器,让其他从服务器复制新主服务器,将已下线的主服务器变为新主服务器的从服务器
10集群:分布式数据库方案,通过分片进行数据共享,并提供复制和故障转移功能
关于集群节点信息保存至clusterNode结构中
集群通过分片保存键值对,整个集群数据库被分为16384个槽,不同的槽指派给不同的节点
MOVED错误:节点接收到命令请求时,检查键所在槽是否为自己负责,若不是,则向客户端返回MOVED错误,MOVED错误携带信息转向正确节点
重新分片:将属于摸个槽的所有键值对从一个节点转移至另一节点
ASK错误:重新分片时,节点在自己的数据库中没有找到命令指定的数据库键,会返回一个ASK错误,指引客户端到另一个节点继续查找
11.订阅与发布
客户端订阅一个或多个频道,成为订阅者,当其他客户端向该频道发送消息,频道所有订阅者都收到该消息
订阅:subscribe "频道" ...,发送:publish "频道" "消息",退订:unsubscribe “频道” ...
所有频道的订阅关系保存至redisServer的pubsub_channels的字典中,一个channel指向所有订阅该频道的客户端链表,订阅则添加,退订则删除
所有模式的订阅关系保存至redisServer的pubsub_patterns的字典中,一个pattern包含客户端及匹配符相关信息
12.事物
通过multi、exec、watch等命令实现事物
执行过程 1.服务器收到multi命令,开启事物,2.输入其他如set\get命令,3.输入exec提交
客户端标记为事物状态,服务端接收到命令后,判断是否客户端是否在事物状态中,在事物将命令放入客户端状态(服务器记录的)的命令队列(FIFO)中,等到EXEC执行队列中的命令
watch乐观锁,在exec执行前监视数据库,键被修改则拒绝执行事物,返回空
exec提交事物时出错则命令全部不会执行,事物取消,若是该事物中某一条错误,exec可以提交,则错误命令不执行,其他的继续执行
13.Lua脚本
使用伪客户端来执行lua脚本中包含的redis命令,使用脚本字典保存所有被eval命令执行过或script load载入过的脚本
eval命令为客户端输入的脚本定义一个函数,调用函数来执行脚本
例如:eval "return redis.call(\'del\',unpack(redis.call(\'keys\',ARGV[1])))" 0 \'USER_ID_*\'
删除所有以USER_ID_开头的键值对
14.排序:SORT命令可以对列表键、集合键或游学集合键的值进行排序
将键所包含的元素载入至数组中,然后对数组排序,排序后有各种选项来返回不同的结果,BY\LIMIT\GET\ALPHA\DESC\ASC
15.二进制数组
使用SDS来保存,逆序保存位数组,BITCOUNT使用查表算法和variable-precision SWAR算法来优化
16.慢查询日志:用于记录执行时间超过给定时长的命令请求,通过这个功能产生的日志来监视和优化查询速度
配置slowlog-log-slower-than 微秒:执行时间超过多少微秒的命令记录至日志
slowlog-max-len 条数:指定服务器最多保存多少条慢查询日志
17.监视器:客户端执行monitor,将自己变为监视器,实时接收并打印服务器当前处理的命令请求的相关信息
192.168.37.134:8340> monitor
OK
1569290062.401279 [0 192.168.37.1:60115] "AUTH" "admin.1231"
1569290062.402377 [0 192.168.37.1:60115] "PING"
1569290062.403760 [0 192.168.37.1:60115] "INFO" "ALL"
1569290063.409147 [0 192.168.37.1:60115] "set" "t" "hh"
1569290075.303194 [0 192.168.37.1:60115] "set" "tt" "hh"