1 缓存删除超时,导致数据库/缓存数据不一致问题
先更新数据库,再删除缓存,缓存删除失败时,采用异步删除策略, 异步删除缓存脏数据
流程如下所示
(1)更新数据库数据;
(2)缓存因为种种问题删除失败
(3)将需要删除的key发送至消息队列
(4)自己消费消息,获得需要删除的key
(5)继续重试删除操作,直到成功
2 持久化方式有哪些?有什么区别?
Redis 提供了 RDB 和 AOF 两种持久化方式,RDB 是把内存中的数据集以快照形式写入磁盘,实际操作是通过 fork 子进程执行,采用二进制压缩存储;AOF 是以文本日志的形式记录 Redis 处理的每一个写入或删除操作。
2.1 RDB
把整个 Redis 的数据保存在单一文件中,比较适合用来做灾备,但缺点是快照保存完成之前如果宕机,这段时间的数据将会丢失,另外保存快照时可能导致服务短时间不可用。
2.2 AOF
对日志文件的写入操作使用的追加模式,有灵活的同步策略,支持每秒同步、每次修改同步和不同步,缺点就是相同规模的数据集,AOF 要大于 RDB,AOF 在运行效率上往往会慢于 RDB。
3 Redis主从复制
3.1 作用
3.1.1 解决单机故障问题。
当主节点也就是master出现问题时,可以由从节点来提供服务也就是slave,实现了快速恢复故障
3.2.2 读写分离
master服务器主要是写,slave主要用来读数据,可以提高服务器的负载能力。同时可以根据需求的变化,添加从节点的数量。
3.2 原理
3.2.1 建立连接过程:
这个过程就是slave跟master连接的过程
3.2.2 数据同步过程(RDB)
是master给slave同步数据的过程
3.2.3 命令传播过程(AOF)
是反复同步数据
3.3 同步方式
3.3.1 全量同步(RDB)
- 从服务器连接主服务器,发送SYNC命令
- 主服务器接收到SYNC命名后,执行BGSAVE命令生成RDB文件, 并在缓冲区记录此后执行的所有写命令
- 主服务器BGSAVE执行完后,向所有从服务器发送RDB快照文件,并在发送期间继续记录被执行的写命令
- 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照(从磁盘加载到内存中)
- 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令(通过redis协议传输)
- 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令
3.3.2 增量同步
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程是通过AOF方式, 主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
3.3.3 同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。
slave 在任何时候都可以发起全量同步。
redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
3.4 心跳机制
命令传播阶段是,主节点与从节点之间一直都需要进行信息互换,使用心跳机制进行维护,实现主节点和从节点连接保持在线。
-
master心跳
指令:ping
间隔 : 默认10s/次
任务 : 主要做的事情就是判断从节点是否在线
-
slave心跳
指令:replconf ack {offset}
间隔 : 1s/次
任务 : 主要做的事情是给主节点发送自己的复制偏移量,从主节点获取到最新的数据变更命令,还做一件事情就是判断主节点是否在线。
注意:
主节点为保障数据稳定性,当从节点挂掉的数量或者延迟过高时。将会拒绝所有信息同步。
3.5 常见问题
1. 数据不一致问题
由于网络因素,多个从节点的数据会不一致。这个因素是没有办法避免的。
关于这个问题给出俩个解决方案:
第一个数据需要高度一致配置一台redis服务器,读写都用一台服务器,这种方式仅限于少量数据,并且数据需高度一直。
第二个监控主从节点的偏移量,如果从节点的延迟过大,暂时屏蔽客户端对该从节点的访问。设置参数为slave-serve-stale-data yes|no。 这个参数一但设置就只能响应info slaveof等少数命令。
2 频繁的网路中断
由于主节点的cpu占用过高,或者从节点频繁连接。出现这种情况造成的结果就是主节点各种资源被严重占用,其中包括但不限于缓冲区,宽带,连接等。
为什么会出现主节点资源被严重占用?
在心跳机制中,从节点每秒会发送一个指令replconf ack指令到主节点。
从节点执行了慢查询,占用大量的cpu
主节点每秒调用复制定时函数replicationCron,然后从节点长时间没有相应。
解决方案:
设置从节点超时释放
设置参数:repl-timeout
这个参数默认为60秒。超过60秒,释放slave。
4 Redis中BigKey的优化
4.1 BigKey定义
- 字符串类型:
它的big体现在单个value值很大,一般认为超过10KB就是bigkey。
- 非字符串类型:(hash,list,set,zset等)
哈希、列表、集合、有序集合,它们的big体现在元素个数太多。
一般来说hash、list、set、zset元素个数不要超过5000。
- bigkey的删除
非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,如果没有使用Redis 4.0的过期异步删除(lazyfree-lazy-expire yes),就会存在阻塞Redis的可能性)
4.2 BigKey的发现
- redis-cli自带--bigkeys,例如:redis-cli -h -a --bigkeys
- 获取生产Redis的rdb文件,通过rdbtools分析rdb生成csv文件,再导入MySQL或其他数据库中进行分析统计,根据size_in_bytes统计bigkey
4.3 优化BigKey
- 优化big key的原则就是string减少字符串长度,list、hash、set、zset等减少成员数
- 以hash类型举例来说,对于field过多的场景,可以根据field进行hash取模,生成一个新的key,例如原来的
hash_key:{filed1:value, filed2:value, filed3:value ...},可以hash取模后形成如下key:value形式
hash_key:mod1:{filed1:value}
hash_key:mod2:{filed2:value}
hash_key:mod3:{filed3:value}
...
取模后,将原先单个key分成多个key,每个key filed个数为原先的1/N - string类型的big key,如文章正文,建议不要存入redis,用文档型数据库MongoDB代替或者直接缓存到CDN上等方式优化
5 缓存失效
5.1 缓存击穿
5.1.1 概念
缓存击穿的概念就是单个key并发访问过高,过期时导致所有请求直接打到db上,这个和热key的问题比较类似,只是说的点在于过期导致请求全部打到DB上而已。
5.1.2 解决方案:
-
加锁更新,比如请求查询A,发现缓存中没有,对A这个key加锁,同时去数据库查询数据,写入缓存,再返回给用户,这样后面的请求就可以从缓存中拿到数据了。
-
将过期时间组合写在value中,通过异步的方式不断的刷新过期时间,防止此类现象
-
热点key永不过期
5.2 缓存穿透
5.2.1 概念
缓存穿透是指查询不存在缓存中的数据,每次请求都会打到DB,就像缓存不存在一样。
5.2.2 解决方案:
1 查询之前加上入参合理化校验,避免查询请求都打到DB
2 查不到的值,也加上缓存, 直接返回, 下次查询还是走缓存
3 利用NGINX配置把请求频繁的ip加入黑名单
4 布隆过滤器(Bloom Filter)这个也能很好的防止缓存穿透的发生,原理是利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好了,存在你就去查了DB刷新KV再return。
5.3 缓存雪崩
5.3.1 概念
当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求进来直接打到DB上,这样可能导致整个系统的崩溃,称为雪崩。雪崩和击穿、热key的问题不太一样的是,他是指大规模的缓存都过期失效了。
5.3.2 解决方案:
-
针对不同key设置不同的过期时间,避免同时过期
-
限流,如果redis宕机,可以限流,避免同时刻大量请求打崩DB
-
二级缓存,同热key的方案。
6 Redis的过期策略有哪些?
6.1 惰性删除
惰性删除指的是当我们查询key的时候才对key进行检测,如果已经达到过期时间,则删除。显然,他有一个缺点就是如果这些过期的key没有被访问,那么他就一直无法被删除,而且一直占用内存
7.2 定期删除
定期删除指的是redis每隔一段时间对数据库做一次检查,删除里面的过期key。由于不可能对所有key去做轮询来删除,所以redis会每次随机取一些key去做检查和删除。
7.3 那么定期+惰性都没有删除过期的key怎么办?
假设redis每次定期随机查询key的时候没有删掉,这些key也没有做查询的话,就会导致这些key一直保存在redis里面无法被删除,这时候就会走到redis的内存淘汰机制。
-
volatile-lru:从已设置过期时间的key中,移出最近最少使用的key进行淘汰
-
volatile-ttl:从已设置过期时间的key中,移出将要过期的key
-
volatile-random:从已设置过期时间的key中随机选择key淘汰
-
allkeys-lru:从key中选择最近最少使用的进行淘汰
-
allkeys-random:从key中随机选择key进行淘汰
-
noeviction:当内存达到阈值的时候,新写入操作报错