Redis深入
- Redis 持久化
- Redis 过期策略
- Redis 淘汰策略
- Redis 事务
- 引发相关问题
Redis 持久化
包含RDB(Redis Database)和AOF(Append Only File )两种方式,两种方式可以共同使用,也可以单独使用
RDB 数据库快照
相关配置
1 |
|
何时工作?
配置文件配置
1
2
3
4# save seconds changes 多少秒内多少次更新就执行
save 3600 1
save 300 100
save 60 10000执行save/bgsave命令
执行flushdb/flushall命令
主从同步的时候
如何工作?
save 命令是同步执行,不需要fork,整个操作会堵塞主进程
- Redis fork,此时存在父子进程,这个会阻塞主(父)进程
- 子进程开始写数据到临时的rdb文件
- 写完新的RDB文件,替换旧的RDB文件
AOF 日志文件
1 |
|
何时工作?
1 |
|
日志文件过大怎么办?
日志过大文件进行日志重写,日志重写过程中,日志日志还会继续写么?同时新的改变命令会存储到buffer中,等新的文件生成会写到新的文件中
1 |
|
什么时候进行重写
1 |
|
如何进行重写
- Redis fork,此时会有父子进程
- 子进程开始写新的临时AOF文件
- 父进程在内存缓冲区中存储新的改变命令,同时根据上面的配置写到旧的文件中
- 当子进程完成文件的重写,父进程会获取到信号,子进程将内存缓存区命令追加到临时的AOF文件中
- 最后修改文件名覆盖旧文件,开始新数据追加写到新文件中
Redis 过期策略
过期策略包含被动策略和主动策略
被动策略
每次获取的时候判断key是否已过期,过期的话就进行删除
主动策略
每秒10次(每隔100ms)的定时任务,定时任务过程如下:
- 每次随机20个过期key
- 删除已经过期的key
- 若是超过25%的key过期,重复上面步骤
Redis淘汰策略
配置方式,使用淘汰策略必须设置最大内存,不然不启作用
1 |
|
- allkeys-lru 针对所有key的lru
- volatile-lru 针对过期key的lru
- allkeys-lfu 针对所有key的lfu
- volatile-lfu 针对过期key的lfu
- allkeys-random 针对所有key的随机
- volatile-random 针对过期key的随机
- volatile-ttl 针对过期key的过期时间
- noeviction 默认的策略,直接报错
LRU Less Recently Used 最近最少使用算法
淘汰最长时间未被使用的key
常规算法实现
基于map和双向链表来实现
Redis中的实现
采用随机采样5个key,通过配置来配置采样的key
1 |
|
LFU Least Frequently Used 最不常用算法
淘汰一定时间内被访问次数最小的key,也就是需要记录访问次数
常规算法实现
基于map和横向、纵向的双向链表实现,横向链表代表使用次数,纵向列表为使用的当前次数的key
Redis中的实现
配置信息
1 |
|
**次数计算规则(非线性增长)**:
1 |
|
次数衰减机制:
1 |
|
Redis 事务
原子指令
默认提供的指令都是原子性的,不会产生不一致的数据,若是想组合多个指令只能采用下面的方法
事务指令
MULTI
开启事务EXEC
执行事务DISCARD
回滚事务
注意事项:
- 开启事务后,提交执行命令会进行一定的检查(无法提交到队列中),检查错误,无法执行事务(事务会自行回滚)
- 事务过程中碰到执行过程中碰到运行时异常,其他的命令会正常执行
CAS机制指令
WATCH
监听指令,获取当前key的值,后续事务操作的时候会判断值是否一致,不一致不进行处理UNWATCH
取消监听
Lua 脚本
1 |
|
使用eval
执行shell脚本
1 |
|
推荐使用script load
进行加载后,再用evalsha
执行
1 |
|
引发相关的问题
缓存一致性
缓存中的数据与数据库中的数据不一致,采用的方式一般都是先操作数据库后删除缓存
Cache Aside 旁路缓存策略
若是先删除缓存的话,会导致一个读线程的值读到没有提交前的值(也就是旧值),后面会一直存在这个值,需要再删除一次,也叫延迟双删。
先操作数据库,提交事务后删除缓存,这个基本能保证一致性。若删除缓存失败,利用重试达到最终的一致性。
缓存雪崩
是指同一时间过多的key失效导致全部打穿到数据库,可以打散key的失效时间,避免同一时间过多的key过期
缓存穿透
是指有缓存的数据,但缓存失效穿透到数据库,上面的缓存雪崩也会导致缓存穿透,针对热值不设置失效时间
缓存击穿
是指数据库压根没有这个值,所有的操作都会先经过redis后击穿到数据库。可以使用如下的方式进行解决:
- 空值也进行缓存,代价是占用空间,也不知会有多少这样的值
- 布隆过滤器,通过位图来实现,对数据进行多次hash来生成位图,针对不存在的数据直接返回,但是有误判的概率会击穿
参考文献
- Redis 持久化:https://redis.io/topics/persistence
- Redis 过期策略:https://redis.io/commands/expire
- Redis 淘汰策略:https://redis.io/topics/lru-cache
- Redis 事务 :https://redis.io/topics/transactions
- Redis lua 脚本:https://redis.io/commands/eval
- 缓存更新的套路:https://coolshell.cn/articles/17416.html
- 极客时间Redis专栏:https://time.geekbang.org/column/intro/100056701?tab=catalog