参考链接
juejin.cn/post/690866… Redis集群内的数据同步
github.com/alibaba/Red… Redis数据同步工具
juejin.cn/post/700101… redis数据同步之redis-shake
Redis实现消息队列 Redis实现消息队列
zhuanlan.zhihu.com/p/344269737 Redis 消息队列的三种方案(List、Streams、Pub/Sub)
为什么Redis是单线程且这么快
1.请求基于内存,大部分操作都是纯内存的,所以非常的快。
2.单线程模型,少了上下文切换和竞争条件,不考虑多线程情况下的各种问题,比如锁,CPU消耗,资源竞争等。
3.使用了多路IO复用模型,非阻塞IO。
使用场景
1.热点数据缓存。
2.限时业务运用。
3.分布式锁,涉及到并发的时候,可以使用这个,主要依赖的是setnx命令。
4.消息队列。
缓存击穿,缓存穿透与缓存雪崩
缓存穿透
- 问题
用户始终使用一个不存在的key去请求数据库,导致DB和Cache都无法生效,从而使得DB每次都去执行查询,当流量大的时候就直接导致DB崩溃。
- 解决办法
- 接口层增加校验,对不符合要求的请求进行拦截。
- 针对非法Key设置缓存,并设置一定的过期时间,这样在这段时间内,所有的非法请求还是都会打到Redis中去。
- 实现布隆过滤器。
缓存击穿
- 问题
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
- 解决办法
- 热点Key设置永不过期,就算要过期也是人为操作。
- 接口限流,服务降级。
- 加互斥锁。
缓存雪崩
问题来源缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:利用二级缓存,发现热Key之后首先先读取到内存中来,让热Key请求来了,就先取内存,再去Redis,可以大大缓解Redis压力。
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。
- 设置热点数据永远不过期。
Hot Key
发现
- 经验发现,对可能成为热词的Key提前做防护。
- 客户端埋点收集热Key。
- Redis基础Proxy层做上报收集。
- Redis自带命令统计,不推荐可能会占用较大资源。
- 人为抓包Redis客户端进行统计。
处置
- 使用二级别缓存,预先将热数据读取到内存,当热Key请求过来的时候,就会先去内存里面读取,这样就可以大大缓解Redis压力了。
- 集群备份多个热Key(涉及到数据同步问题)
持久化机制
RDB
- 原理
- 把当前进程中的数据,生成一份快照保存到磁盘上。
- 触发方式
-
- 手动触发
- save命令:阻塞当前服务,直到RDB进程同步完成位置,此操作有可能造成大面积阻塞。
- bgsave命令:Redis fork一个子进程后台进行持久化,这样阻塞的时间只有fork的过程。
- 自动触发
- redis.conf中写道save m n,指定m秒内发生了n次修改就自动触发生成rdb文件。
- master-slave模式全量复制的时候也会产生bgsave操作。
- debug reload命令重置redis的时候也会触发bgsave操作。
- 执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作。
- 保证数据一致性
- RDB中的核心思路是Copy-on-Write,来保证在进行快照操作的这段时间,需要压缩写入磁盘上的数据在内存中不会发生变化。在正常的快照操作中,一方面Redis主进程会fork一个新的快照进程专门来做这个事情,这样保证了Redis服务不会停止对客户端包括写请求在内的任何响应。另一方面这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。
- 复制过程中服务崩溃
- 很简单,在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的RDB快照文件作为恢复内存数据的参考。
- 不建议高频进行RDB同步
- 优点
- 默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景;
- Redis加载RDB文件恢复数据要远远快于AOF方式;
- 缺点
- RDB方式实时性不够,无法做到秒级的持久化;
- fork子进程,属于重量级操作,频繁操作执行成本较高。
- RDB是二进制格式,无可读性。
- RDB模式存在版本兼容问题。
-
AOF
- AOF采用写后日志,即先写内存,再写日志。
- 优点:
- 避免额外开销,Redis 在向 AOF 里面记录日志的时候,并不会先去对这些命令进行语法检查。
- 不阻塞当前的写操作。
- 缺点:
- 如果命令执行完成,写日志之前宕机了,会丢失数据。
- 主线程写磁盘压力大,导致写盘慢,阻塞后续操作。
- 实现AOF:
- 命令追加:当服务器执行完一个命令后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。
- Redis提供了三种方式写入磁盘,Always,Everysec,No。
- Always:同步写回,命令执行完立马把日志写入磁盘。
- Everysec:每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
- No:操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
- 开启AOF
appendonly yes # appendonly参数开启AOF持久化 appendfilename "appendonly.aof" # AOF持久化的文件名,默认是appendonly.aof dir ./ # AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的 appendfsync everysec # 同步策略 no-appendfsync-on-rewrite no # aof重写期间是否同步 auto-aof-rewrite-percentage 100 # 重写触发配置 auto-aof-rewrite-min-size 64mb aof-load-truncated yes # 加载aof出错如何处理 aof-rewrite-incremental-fsync yes # 文件重写策略
- appendonly:参数改为yes则开启redis的AOF。
- appendfilename:AOF存储名字。
- appendfsync:这个参数项是AOF功能最重要的设置项之一,主要用于设置“真正执行”操作命令向AOF文件中同步的策略。
-
Redis Key的过期策略
-
删除策略
-
- 惰性删除:服务器不会主动删除数据,只有当客户端查询某个数据时,服务器判断该数据是否过期,如果过期则删除。
- 定期删除:服务器执行定时任务删除过期数据,但是考虑到内存和CPU的折中(删除会释放内存,但是频繁的删除操作对CPU不友好),该删除的频率和执行时间都受到了限制。
-
淘汰策略
- Redis共支持八种淘汰策略,分别是noeviction、volatile-random、volatile-ttl、volatile-lru、volatile-lfu、allkeys-lru、allkeys-random 和 allkeys-lfu 策略。
- 不淘汰
- noeviction (v4.0后默认的)
- 对设置了过期时间的数据中进行淘汰
- 随机:volatile-random
- ttl:volatile-ttl
- lru:volatile-lru
- lfu:volatile-lfu
- 全部数据进行淘汰
- 随机:allkeys-random
- lru:allkeys-lru
- lfu:allkeys-lfu
-
内存优化
- 如果内存达到上限?
- 如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
- 如何做优化?
- 缩减键值对象: 缩减键(key)和值(value)的长度。
- 共享对象池。
- 字符串优化。
- 编码优化。
- 控制key的数量。
-
分布式锁
- 常规
- 加锁: SET NX PX + 校验唯一随机值
- 解锁: Lua脚本
- RedLock
- 搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。 - Redisson框架
- Redisson
- watchdog或者它实现了RedLock方式
- 不淘汰
- 手动触发