Redis
6大数据结构
动态字符串(sds)
链表
字典
- dict结构
跳跃表(zkiplist)
- zskiplistNode、zskiplist结构组成
整数集合(intset)
- 支持根据数据类型自动升级
压缩列表(ziplist)
- 支持由后向前访问
- 连锁更新
5种对象
字符串(String)
-
set、sget
-
数据结构
- 整数值的字符串对象、embstr编码的简单动态字符串对象、简单动态字符串对象
哈希(Hash)又名字典
-
hmset、hget
-
数据结构
- ziplist、hashtable
列表(List)
-
lpush、lrange
-
无序列表
-
数据结构
- ziplist、linkedlist
集合(Set)
-
sadd、amembers
-
数据结构
- intset、hashtable[hash元素中key指向的value为null]
有序集合(zset)
-
zadd、zrangebyscore
-
数据结构
- ziplist、skiplist
其他内容
-
类型检查
- 检查redisObject中type属性(键对应的值对象)类型
-
命令多态
- 不区分类型:按照键类型多态
- 区分类型:按照值编码方式多态
-
内存回收
- redisObject中引用计数器
-
对象共享
-
对象空转时长
- redisObejct中lru属性:记载上一次访问时间
应用业务
大key定义:单个key中存储的value很大;列表中元素过多
单个value超过1MB、列表元素超过1万个
服务器
不同数据库的存储
- redisServer.db[]数组中,select命令切换
单个数据库中键空间
- redisDb.dict字典中,key是字符串对象,value是其他类型对象
键的过期时间
- redisDb.expires字典中,字典中key是键,value是键过期时间戳
过期键判定
- is_expired(key)
过期键删除策略
-
定时删除
- cpu占用时间长
-
惰性删除
- 内存占用多
-
定期删除
- 定期频率和时长
持久化
RDB持久化:保存数据库中键值对
-
生成RDB文件
- save:主进程
- bgsave:子进程
-
触发持久化条件
- redisServer中saveParams数组:多久修改操作多少次
- dirty计数器
- lastsave属性
-
RDB文件组成
- REDIS + 数据库版本 + database[0] / datebase[1] + EOF+check_sum(校验和)
AOF:保存Redis写命令记录数据库状态
-
持久化实现
-
记录命令:reidsServer.aof_buf
-
写入AOF文件:aof_buf的内容写入AOF文件。何时同步依赖apendfsync配置
- always
- everysec
- no
-
-
AOF文件载入与数据还原
- 启动程序时进行文件命令的重执行
-
AOF文件重写
- 背景:命令积累多。只记录一个key终态数据
- 实现:遍历数据库,遍历key,用一条命令记录键值状态;过期时间
- 后台重写:开启子进程进行重写,父进程追加AOF缓冲区 + AOF重写缓冲区;子进程执行完,父进程调用信号处理函数将重写缓冲区内容保存进新的AOF文件中
事件
文件事件
-
客户端-服务端:套接字
-
文件事件处理器
-
IO多路复用程序
-
文件事件分派器
-
事件处理器
- 连接应答处理器
- 命令请求处理器
- 命令回复处理器
-
-
事件类型
- 读事件:AE_READABLE
- 写事件:AE_WRITEABLE
时间事件:serverCron函数
- 定时事件
- 实际执行:周期事件
客户端
服务器状态结构中保存了clients链表:保存所有客户端状态
客户端状态属性
-
套接字描述符
- int fd
-
标志:标志客户端角色
- int flags
-
输入缓冲区
- sds querybuf:最大1GB
-
命令与命令参数
- robj **argv
- int argc
-
命令的实现函数
- redisCommand *cmd
-
输出缓冲区
-
可变大小
- list *reply
-
固定大小
- char buf[16KB]
- int bufpos:已使用的字节数量
-
-
身份验证
- int authenticated:决定能否处理该客户端命令
-
时间
- ctime、lastinteraction
客户端类型
- 普通客户端
- 伪客户端
XMind - Trial Version
复制
作用:从服务器复制主服务器数据内容
命令:slaveof 主ip 主port
旧版复制功能缺陷
- 2.8版本之前从服务器与主服务器断开重连之后需要执行sync同步操作,耗时
- 与新版区别:新版支持断开重连之后部分重同步(psync)
主要模块
-
复制偏移量
- 主从服务器都维护一个偏移量,通过偏移量值是否一致判断同步状态是否达到一致
-
复制积压缓冲区
- 主服务器维护
- 固定FIFO队列,默认1MB
- 传播命令时同时写入队列
-
服务器运行ID
-
psync实现
- 第一次复制:完整重同步;非第一次->psync 主运行ID offset->①fullresync(完整重同步)②continue(部分重同步)
具体实现
-
1、设置主服务器ip和port -> 2、建立套接字连接
-
3、发送ping命令 -> 4、身份验证
-
5、发送端口信息 -> 6、同步
-
7、初次同步后的命令传播
-
8、心跳检测
-
命令:replconf ack offset / info replication
-
作用
- 检测主从之间网络
- 辅助实现min-slaves配置
- 检测命令丢失
-
集群
集群中的节点通过发送和接受消息来进行通信
节点
-
一个集群由多个节点组成,多个主节点、从节点
-
节点中数据结构
- clusterNode记录本节点管理的槽信息
- clusterState记录槽被指派给了哪些节点信息
-
主节点
- 管理槽信息
-
从节点
- 复制主节点信息,主节点下线时竞争主节点[故障转移]
槽
-
一个集群总共是16384个槽、每个槽有多个键值对
-
槽指派
- n个槽可以指派给单个节点
-
重新分片
-
redis-trib:将某个槽的键值对转移给另一个节点
-
MOVED错误
- 槽负责权已经交接完毕,客户端访问旧节点返回信息
-
ASK错误
- 槽负责权交接过程中,客户端访问旧节点返回信息
-
消息
-
消息类型
- MEET:某个节点申请加入集群
- PING、PONG:保持节点之间的沟通
- FAIL:针对主节点下线的广播
- PUBLISH:客户端向节点发送命令
-
结构
-
消息头
- 发送者自身节点信息
- 消息正文
-
消息正文
- 不同类型的消息结构
-
Sentinel
没看下去!下次有兴趣再看
XMind - Trial Version
发布与订阅
频道
-
订阅方式:subscribe c1,c2...
-
退订:unsubscribe c1,c2....
-
作用
- 客户端订阅频道,频道收到消息通知所有订阅者;以及所有命中频道的模式订阅者
-
实现方式
- redisServer中字典结构:pubsub_channels
- 字典结构:key,value(链表)
模式
-
订阅方式:psubscribe p1,p2....
-
退订方式:unpsubscribe p1,p2....
-
实现方式
- redisServer中链表结构:pubsub_patterns
消息发送
- publish 频道 消息内容
查看订阅
-
频道
-
查询频道订阅者数量
- pubsub numsub [channel1,channel2.....]
-
查询频道/与频道模式匹配的所有频道
- pubsub channels pattern
-
-
模式
-
查询被订阅模式数量
- pubsub numpat
-
Q:为什么频道的数据结构与模式的数据结构不一样
- 猜测是因为频道的名称很精准,hash很方便;模式的名称是模糊的,用hash不方便
事务
使用方式
- multi、命令1、命令2....、exec
实现原理
-
客户端切换至事务状态:client.flags|=REDIS_MULTI 打开事务标识
-
事务存储结构
-
事务状态:MultiState
-
命令队列数组
- 先进先出
-
命令个数计数器
-
-
-
ACID性质
-
原子性
- 要么命令都执行要么都不执行。不支持回滚
-
一致性
-
处理错误
- 入队错误:命令不存在->事务被拒绝
- 执行错误:命令参数不对->该条命令不会执行
- 服务器停机:持久化模式都会保证数据一致
-
-
隔离性
- redis事务是单线程的,串行保证事务的隔离
-
耐久性
-
由持久化模式决定
- 无持久化:无耐久性
- RDB->无耐久性
- AOF + appendfsync=always -> 有耐久性
- AOF + appendfsync=everysec -> 无耐久性
- AOF + appendfsync=no -> 无耐久性
-
含义:事务结果保存在永久性存储介质里
-
-
WATCH
-
监听某个键是否有被修改
-
被修改了->打开客户端REDIS_DIRTY_CAS标识
- 事务不能执行
-
-
实现:redisDB结构中有一个键监听字典。key键、value监听的客户端列表
Lua脚本
初始化lua环境
-
1、创建lua环境、2、载入函数库、3、创建redis表格包含redis一些函数;4、替换lua随机函数;5、创建排序辅助函数。等等
-
lua环境协作组件
- 1、伪客户端
- 2、lua_scripts字典:保存sha1校验和、lua脚本内容
执行
- eval "脚本命令" numkeys
- redis.call/redis.pcall(redis命令)
命令
-
eval
- 执行过程:定义脚本函数->保存脚本到lua_scripts字典中 -> 执行脚本函数
-
evalsha
- evalsha + 校验和 -> 执行脚本
-
脚本管理命令
-
script flush:清空Lua_script字典内容
-
script exists:判断sha1对应的脚本是否存在
-
script load:加载一个脚本函数。创建函数+保存到lua_scripts字典中
-
script kill:命令停止函数执行
- 与shutdown nosave区别:kill未有写操作;nosave已经执行过写操作
-
脚本复制
-
eval、script flush、script load:直接广播给所有从服务器
-
evalsha
- 从服务器已经都载入了lua脚本?执行广播evalsha命令:置换成等效的eval命令广播
慢查询日志
记录执行时间超过指定时长的命令
存储结构
- 链表
实现方式
- 命令执行前后时差>=指定时长;头插法插入一条数据、链表中数据超过指定长度,尾部删除一调数据FIFO
XMind - Trial Version