这篇分享缓存异常场景和缓存一致性... 这两个相关性高,容易左脚踩右脚螺旋升天...

缓存异常三连

缓存穿透(缓存、数据库双杀)

缓存穿透是什么

  • 查一个 缓存和数据库都没有的数据就是穿透

  • 高频请求直接打数据库会把数据库打崩

  • 为异常数据建立数据项并重建缓存,比如给异常数据设置数据库记录项“nil”...(兜底)

怎么办

  1. 作请求合法校验,异常数据直接不让查,比如设计的ID 范围是 0 - 65535 uint16 的值但是查询的ID 是-1,这种直接丢

  2. 缓存后面架个布隆过滤,没有一定没有(然后返回),但有的数据只是可能有(布隆过滤是几个哈希函数映射一个bit位表,其他数据可能会提前占用这个数据刚好对应的几个比特位)。

  3. 一般联合使用.

缓存击穿 (没有缓存,数据库有)

缓存击穿是什么

  • 缓存没有,但数据库有

  • 如果不及时缓存重建,高频请求会打崩数据库

怎么办

  • 每次查询就刷新,续期

  • 及时重建缓存,发现缓存失效业务线程加分布式锁拉数据重建缓存,其他线程发现有锁后休眠等待或者返回。

  • 某证券方案,多重缓存

缓存雪崩

缓存雪崩是什么

  • 大量key在同一时间失效,导致大量查询直接打向数据导致数据库负载过大崩溃。

怎么办

  • 每次查询就刷新,续期

  • 设置随机EX/PEX,防止同一事件批量过期

  • 设置二级缓存,二级缓存过期时间是一级缓存时间过期时间更长。

  • 及时加分布式锁重建缓存,竞争线程发现已加锁直接返回,或者短时休眠等待


简谈一致性

缓存一致性如何保障

大致方向有三:

  1. 只更新后端数据库,设置缓存过期不管缓存,等缓存过期后再重建(完全以过期时间兜底)

  2. 更新后端数据库后,再操作Redis

  3. 异步将后端数据库(MySQL)更新同步给Redis: 比如先更新MySQL,Redis 作为MySQL 从机用阿里开源的canal组件 订阅MySQL binlog 异步更新Redis

分析

方向1:

缺点:

  • 完全依赖过期,每次数据库更新都会产生脏数据

  • 设置过期时间太短会频繁失效,过长会容易有较长时间不一致,开发者编程和场景经验有一定经验

优点:

  • 管理成本低,出问题概率小

  • redis 原生接口,开发成本低,易实现

方向2:

缺点:

  • 后端数据库更新后需要操作redis 需要消耗资源

  • 若是数据库更新后也更新redis,容易出现时序问题,由于网络延时导致的延时问题不可避免

  • 删除失败后会退化成方向1,但可以使用双删尝试解决

优点:

  • 对比方向1 达成一致的滞后时间缩短

  • 开发成本低,若为删除缓存,只需要添加删除逻辑。

方向3:

缺点:

  • 引入消息队列这种重型组件,需要搭建同步服务,维护成本不低

  • 同步服可能会成为系统关键节点,同步服务崩了,缓存得不到及时更新

  • 异步更新码,可能达成一致的时间会较长,适合长时间不怎更新的业务,或者不怎需要即时更新的业务,比如电影网站,稍晚点入库几部电影也没问题。

优点:

  • 与开发解耦更新MySQL 时不需要再额外操作缓存

  • 无时序性问题,可靠性强