redis总结
《我们一起进大厂》系列-Redis常见面试题(带答案) - 掘金 (juejin.cn)
redis为什么这么快
- 基于内存
- 单线程模型
- 非阻塞IO
- 优化过的数据结构和底层模型
基本的数据结构
String
hash
list
朋友圈的点赞的人的显示是有顺序的,可以使用list来实现顺序;如果有人点赞rpush;如果有人取消点赞lrem
set
Zset
跳跃表
哈希表
充分利用 跳跃表的快速查找和插入操作,同时通过哈希表来快速查找元素与分数之间的映射关系
redis的持久化
RDB
RDB是把内存中的数据集以快照的形式写入磁盘,实际操作是fork子进程执行,采用二进制压缩存储;
redis默认使用RDB来持久化,在redis.conf配置文件中默认由此配置
1 |
|
Redis 提供了两个命令来生成 RDB 快照文件:
save
: 同步保存操作,会阻塞 Redis 主线程;bgsave
: fork 出一个子进程,子进程执行,不会阻塞 Redis 主线程,默认选项。
AOF
AOF是以文本日志的形式记录redis处理的写入和删除操作
AOF(append only file)的实时性更好。默认情况下,reids没有开启AOF,可以使用一下指令开启
1 |
|
AOF的流程
AOF 持久化功能的实现可以简单分为 5 步:
- **append:**所有的写命令会追加到AOF缓冲区
- write: 将AOF缓冲区的数据写到AOF文件中。这步 调用
write
函数(系统调用),write
将数据写到系统内核缓冲区 - fsync:fsycn向磁盘做同步操作
- rewrite:随着AOF文件越来越大,需要定期对AOF文件进行重写,压缩
- load: 当redis重启时,可以加载AOF文件进行数据恢复
AOF持久化的方式
appendfsync always
:主线程调用write
执行写操作后,后台线程(aof_fsync
线程)立即会调用fsync
函数同步 AOF 文件(刷盘),fsync
完成后线程返回,这样会严重降低 Redis 的性能(write
+fsync
)。appendfsync everysec
:主线程调用write
执行写操作后立即返回,由后台线程(aof_fsync
线程)每秒钟调用fsync
函数(系统调用)同步一次 AOF 文件(write
+fsync
,fsync
间隔为 1 秒)appendfsync no
:主线程调用write
执行写操作后立即返回,让操作系统决定何时进行同步,Linux 下一般为 30 秒一次(write
但不fsync
,fsync
的时机由操作系统决定)
RDB比AOF优秀的地方
- RDB文件是一个紧凑的二进制文件,占用空间小,传输速度快,适合做备份和灾难恢复
- RDB文件恢复数据的速度比AOF快,因为只需要加载一次文件即可
- RDB持久化对Redis服务器的性能影响较小,因为大部分工作由子进程完成
AOF比RDB优秀的地方
- AOF文件可以实时或者近实时地记录Redis内存中的数据,因为它是每次写命令或者每秒钟同步一次。如果在同步之间发生故障,可能会丢失一部分数据,但是数据丢失的概率比RDB小。
- AOF文件是一个文本文件,可以方便地查看和编辑。AOF文件中的命令是Redis协议格式的,可以直接用Redis客户端来执行。
- AOF文件可以自动进行重写,以减少冗余命令和文件体积。重写过程不影响Redis服务器的正常服务,也不会丢失任何数据。
考虑角度
- 数据的完整性
- 数据恢复的速度
- 磁盘的占用空间
- 数据的可读性
具体的 建议:
- 数据完整性要求不高,可以使用RDB或者是将AOF的同步频率设置为每秒一次
- 如果要数据尽可能不丢失,可只使用
使用RDB做镜像全量持久化,AOF做增量持久化。RDB耗时比较严重,实时性不够好,在停机的时候会导致大量丢失数据,所以需要AOF来配合使用。
在redis示例重启时,会使用RDB持久化文件重新构建内存,再使用AOF重放近期的操作指令来实现完整的数据恢复。
过期数据的删除策略
- 惰性删除:只有在取key的时候才对数据进行过期检查。这样对CPU友好,但可能造成太多key没有被删除。
- 定期删除:每隔一段时间,抽取一批key进行过期检查。
redis采取惰性删除+定期删除的策略
redis的事务
redis提供的事务不是严格的事务,redis保证串行执行命令,但能保证全部执行;若其中有一条命令执行失败,此时不会回滚,而是继续执行下去
redis生成的问题
缓存穿透 | 缓存击穿 | 缓存雪崩 | |
---|---|---|---|
数据在cache是否存在 | no | no | yes(但是过期了) |
数据在DB是否存在 | no | yes | yes |
缓存穿透
大量不合理的key:缓存和数据库中都不存在。这导致大量请求直接达到了数据库上。
- 接口限流:根据用户或者IP对接口进行限流,对于异常频繁的访问采取黑名单机制
- 布隆过滤器:不存在就不存在;存在不一定存在
缓存击穿
请求的key为热点数据,数据不在缓存中,数据库中存在
- 延长过期时间
- 提前预热数据,将数据存入缓存中,设置合理的过期时间,保证在秒杀结束前,数据不会过期
缓存雪崩
大量缓存同时失效,大量请求访问数据库
- 服务宕机
- 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
- 服务熔断请求限流
- 大量缓存过期
- 多级缓存,例如本地缓存+Redis 缓存的组合,当 Redis 缓存出现问题时,还可以从本地缓存中获取到部分数据。
- 互斥锁
- 设置随机过期时间
针对热点缓存失效的情况:
- 设置不同的失效时间比如随机设置缓存的失效时间。
- 缓存永不失效(不太推荐,实用性太差)。
- 缓存预热,也就是在程序启动后或运行过程中,主动将热点数据加载到缓存中。
缓存预热如何实现?
常见的缓存预热方式有两种:
- 使用定时任务,比如 xxl-job,来定时触发缓存预热的逻辑,将数据库中的热点数据查询出来并存入缓存中。
- 使用消息队列,比如 Kafka,来异步地进行缓存预热,将数据库中的热点数据的主键或者 ID 发送到消息队列中,然后由缓存服务消费消息队列中的数据,根据主键或者 ID 查询数据库并更新缓存
分布式锁
redis的回滚机制
redis不支持事务
大概的意思是,作者不支持事务回滚的原因有以下两个:
- 他认为 Redis 事务的执行时,错误通常都是编程错误造成的,这种错误通常只会出现在开发环境中,而很少会在实际的生产环境中出现,所以他认为没有必要为 Redis 开发事务回滚功能;
- 不支持事务回滚是因为这种复杂的功能和 Redis 追求的简单高效的设计主旨不符合。
这里指的不支持事务的运行回滚是指 运行时错误的事务回滚
1 |
|
主从数据同步
启动一台slave的时候,发送psycn命令给master,触发全量复制。slave将master的RDB写入本地磁盘,加载进内存
redis的分布式锁
缓存不一致问题
[常见的缓存更新策略剖析 - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/86396877#:~:text=常见的缓存更新策略剖析 1 一、先删缓存再更新数据库 2 二、先更新数据库,再删缓存 3,三、先更新数据库,再更新缓存 4 四、read%2Fwrite through 缓存代理 5 五、写回)