点一下关注吧!!!非常感谢!!持续更新!!!
目前已经更新到了:
- Hadoop(已更完)
- HDFS(已更完)
- MapReduce(已更完)
- Hive(已更完)
- Flume(已更完)
- Sqoop(已更完)
- Zookeeper(已更完)
- HBase(已更完)
- Redis (正在更新…)
章节内容
上节我们完成了:
- Redis 通信协议
- Redis 响应模式:串行模式、双工模式
- Redis 数据格式
- 处理流程、处理机制、文件事件
- Reactor 多路复用等基础概念
缓存穿透
问题描述
一般的缓存系统,都是按照Key去缓存查询,如果不存在对应的Value,就会去后端中查询。
在高并发情况下,过量查找不存在的Key就会出现缓存穿透的问题,数据库会因为量过大而宕机。
解决方案
- 对查询结果为空的情况也进行缓存,缓存时间(TTL)设置的短一点,或者Key对应的数据 INSERT 操作后清理缓存。
- 使用布隆过滤器,在缓存之前加一层。在查询前先到布隆过滤器中查找,不存在则直接返回,不需要到DB查找。
布隆过滤器
布隆过滤器(BloomFilter)是1970年提出的,它实际上一个很长的二进制向量和一系列随机的Hash。
布隆过滤器的原理是:当一个元素被加入到集合中,通过 k 个Hash函数将这个元素映射成一个数组中的 k 个点,把它设置为1。
检索时:查询这些点是否为1即可,如果这些点存在任何一个0,那这个元素一定不存在。但是如果都是1,则这个元素是可能存在的。
缓存雪崩
问题描述
当缓存服务器重启或者压力过大宕机时,会有大量的访问到达DB,导致数据库奔溃。
解决方案
- key 的失效期分散开,不同的key是指不同的过期时间
- 设置二级缓存
- 高可用
缓存击穿
问题描述
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发的访问,是一种非常热的数据。这个时候,可能会存在被击穿的问题。
缓存在某个时间点过期的时候,恰好有很多key的访问过来,这些请求都发现缓存中没有值,从而都到达DB。
解决方案
- 分布式锁控制线程的访问
- 不设置超时时间 但会造成读写一致问题
数据不一致
问题描述
缓存和DB中的数据不一致。
解决方案
强一致是非常难的,但是可以追求最终一致,采用 延时双删:
- 先更新数据库的同时删除缓存,等读的时候就会填充缓存
- 2秒后再删除一次缓存
- 设置过期时间
- 将缓存删除失败记录到日志中,利用脚本再次删除
更高级的方案:
- 通过 binglog 日志来删除缓存
并发竞争
问题描述
多个客户端并发写一个 key,比如写请求:1、2、3、4,最后本来是4,但是由于到达时间顺序问题,成了 2、1、4、3。
解决方案: 分布式锁 + 时间戳
实现原理
准备一个分布式锁,让大家抢锁,抢到再做 SET 操作。
目的是为了让原来的并行操作变成串行操作。
Redis分布式锁
通过 setnx() 函数实现,但是要注意要有时间:
系统A key 1: {A: 10:00}
系统B key 1: {B: 10:01}
如果是B先抢到锁执行后,在A抢到锁后,发现时间已经过了,那就不做SET操作了。保证数据的顺序。
解决方案:消息队列
在并发量过高的情况下,消息队列排队串行化。
再从消息队列中取出一个一个执行。
HotKey
问题描述
当有大量的请求访问某个Redis中的Key,由于流量集中达到网络的上限。
当有大量的请求(几十万)访问Redis中某个Key时,导致Redis的服务宕机了。接下来就导致流量会进入到DB中。
如何发现
- 预估热key,比如秒杀、火爆新闻
- 客户端进行统计
- Redis自带命令:monitor、hotkeys,但是执行慢
- 利用大数据技术:Storm、Spark、Flink等,发现后写入到ZK中
解决方案
- 变分布式缓存为本地缓存,发现hotkey后,加载本地的缓存(数据一致性可能会低)
- 在每个主节点上备份呢热key数据,到时候随机选节点读取即可
- 热点数据进行限流熔断
Big Key
问题描述
大Key指存储的值非常大:
- 热门话题下的讨论
- 大V的粉丝列表
- 序列化后的图片
- 没有及时处理的垃圾数据
大Key带来的问题:
- 大key会占用大量的内存,集群中无法均衡
- Redis性能下降,主从复制异常
- 删除时操作时间过长导致阻塞
如何发现
- 使用 --bigkeys 命令 但key较多时会很慢
- 获取 RDB 文件,进行分析
如何处理
- string类型的bigkey不要存入Redis,可用MongoDB或者CDN
- string类型bigkey如果非要存Redis,则单独存储,比如一台Redis单独存。
- 将Key拆分成多个 key-value,平摊到多次获取的压力上
- 大Key不要del,del会阻塞,而删除时间很长会导致阻塞
- 使用 lazy delelet (unlink指令)