Redis(Remote dictionary service 远程字典服务)
特性:
- 快 -- 内存型数据库 key-value
- 持久化 -- 过期、淘汰策略
- 高可用 集群
- 单线程命令执行
- IO多路复用
Redis内部数据的存储结构-redis Object
1、结构体定义:
typedef struct redisObject {
2、字段说明
type: 数据类型
encoding: 内部编码
lru: 如果是LRU 设置的是全局的lru_clock的值 即是最近访问时间
如果是LFU,低8位设置的是使用次数(频率),高16位设置的是访问时间
refcount: 引用数,如果为0说明现在这个value没人引用,可以回收
Redis五大数据类型
1、String
内部编码(转换不可逆)
- int -- 8字节长整型 redis.conf文件中可配
- raw -- 44字节以上
- embstr -- 44字节以下 只读
2、hash(可以存储对象类型数据)
key和value中间有一个field(小key)但是field不能设置过期时间
内部编码
- ziplist
- 类似于双向链表,不同的是ziplist指向的是指针并不是具体的数据。典型的时间换空间
- 所以只能用在数据比较小的地方
- 64字节 数量<512
- hashtable
- 数组+链表的结构
- 可以扩容 有两个数组ht[0]和ht[1]
- 先为ht[1]分配空间 = ht[0].used*2
- ht[1]=ht[0]
- ht[0]删除
- ht[1]改为ht[0],创建新的ht[1]
3、list(有序可以重复)
内部编码
- quicklist(双向链表+ziplist组成)
4、set(无序集合)
内部编码
- intset 存储的都是整数类型的数据
- hashtable
5、zset(有序集合)
内部编码
- ziplist
- skiplist(跳表)
- 有一个level属性,给数据随机分层
过期策略
1、定时过期 -- redis没有用
redis自己去定时判断key是否过期,对内存友好但是对cpu不友好,影响缓存的响应时间和吞吐量
2、惰性过期
当key被动访问的时候,才会去判断是否过期。对cpu友好但是对内存不友好
3、定期过期(redis采用了这种策略)
每个一段时间去请求判断是否有过期的key,key 的过期时间室友单独存放的
具体的操作:( 触发--serverCron 执行频率--hz 10[每1秒执行10次] )
- 从所有设置了过期时间的key中取20个
- 删除这20个key里面已经过期的数据
- 删除后,计算过期键的比例,如果超过25%(有1/4以上过期)重复操作1 2
淘汰策略
1、LRU(least recently used 最近未使用)
通过最近被使用的时间,来判断最久没有被使用的可以然后进行淘汰
算法:-- 计算key没有被使用的时间
- 首先系统里有一个server.lruclock字段,它serverCron每次执行完后被更新,所以它是一个延迟100ms的系统时间
- 计算没有被使用的时间,比较server.lruclock和redisObject.lru
if(server.lruclock>=redisObject.lru) server.lruclock-redisObject.lru
else server.lruclock+(LRU_CLOCK_MAX-redisObject.lru)
redisObject.lru在每次创建或访问时,都会把server.lruclock赋值
LRU_CLOCK_MAX 是lru的最大值 24位
server.lruclock如果超过最大值,就会重新开始计算,所以存在else的情况
问题:仅根据最后的使用时间淘汰不合理
此时,淘汰第一个并不合理
2、LFU(least frequently used)最不常用
根据次数和访问时间进行判断
redis.conf中的相关配置:
lfu-log-factory 10 次数的增长频率因子
lfu-decay-time 10 每分钟没有访问次数减少10
持久化机制
为什么要持久化?
redis是内存数据库,宕机数据会丢失。
1、RDB
将当前内存中的数据快照写入磁盘,恢复时将文件直接读到内存
redis.conf配置
- dbfilename dump.rdb 快照文件名称
- dir ./ 文件生成位置--默认在启动目录下
自动触发
save 900 1 900s内至少有一个key被修改
save 300 10 300s内至少有10个key被修改
save 60 10000 60m内至少有10000个key被修改
手动触发
shutdown
flushall
save指令 生成RDB文件会阻塞Redis服务器,不能处理其他命令
bgsave 主进程通过fork一个子进程去处理,主进程不用进行任何磁盘I/O操作
命令
stop-writes-on-bgsave-error: yes
- 当启用RDB且最后一次数据保存失败,Redis是否停止接收数据。让用户意识到数据没有正确持久化到硬盘上,如果Redis重启,又可以重新接收数据了。
优点
比AOF快,保存的是某个时间点的数据集
缺点
- 无法做到实时备份,数据可能会丢失,因为是每隔一段时间做的。保存的是二进制的数据,无法直接查看文件内容。
2、AOF
配置
- appendonly on 是否开启(默认不开启)
- appendfilename "appendfile.aof" 文件名
- appendfsync everysec AOF持久化策略(默认everysec)
- no(表示不执行fsync,由操作系统保证数据同步到磁盘。速度快但是不太安全)
- always(表示每次写入都执行fsync,以保证数据同步到磁盘。效率低)
- everysec(表示每秒执行一次fsync,可能会导致丢失1s数据,但是兼顾效率和安全性)
问题:AOF是命令追加,所以会导致文件很大
解决:重写AOF
配置
- auto-aof-rewrite-percentage 100(当目前文件大小超过上次重写的aof文件的百分之多少进行重写,默认100)
- auto-aof-rewrite-min-size 64(设置允许重写的最小AOF文件的大小,默认64M)
避免达到了约定百分比但文件大小仍然很小的情况(避免不需要的重写)
重写机制
- 当需要重写时,Redis会fork一个子进程去处理,首先读取内存中的数据,重新写到一个临时的文件中。
- 在子进程执行AOF重写时,主进程也要做一些事情
- 处理客户端发过来的命令请求
- 将写命令追加到现有的AOF文件中
- 将写命令追加到AOF重写缓存中
- 当子进程重写完后,会发一个信号给主进程,主进程收到后悔调用信号处理函数(会Block主进程)执行以下工作:
- 将AOF重写缓存区中的数据全部写入新的AOF文件中
- 对新的AOF文件进行改名,新旧替换
RDB和AOF可以同时开启,在数据恢复时用AOF