常规稳定方案直接操作

  1. 稳定使用读:不存在加锁获取写入,其他碰到锁了就重试一下
  2. 稳定使用写:准备好数据,分布式锁,删缓存,写数据
    1. 数据一致性严苛,就是加锁删缓存,写数据,写缓存(mq 异步 tcc 等保证可靠性)

其他方案

同步:操作分布式锁住

异步:mq 消息保证、binlog 保证

持久化缓存处理——持久化内存Redis 持久化内存

多级

数据库、本地(还可以通过 hash优化一下)、redis

image.png

## 缓存模式
### 基本思路
- **Cache Aside(旁路缓存)**
  - 写操作:业务代码写数据库,再写缓存
  - 读操作:业务代码读缓存,缓存未命中读数据库
  - 一致性问题:先写数据库或先写缓存都有一致性问题
- **Read Through(读穿)**
  - 写操作:和Cache Aside一样
  - 读操作:业务代码读缓存,缓存会去加载数据
    - 缓存可以直接返回错误或者默认值
    - 缓存可以加载了数据库之后返回
    - 缓存可以加载了数据库数据,并回写缓存之后返回
  - 存在一致性问题
- **Write Through(写穿)**
  - 写操作:写入到缓存,缓存去更新数据库;读操作:和Cache Aside一样
  - 存在一致性问题
- **Write Back(回写/写回)**
  - 特点——读写缓存:缓存过期的时候才回写到数据库;存在数据丢失的可能
  - 使用场景——不介意少量数据丢失;缓存本身高可用
  - 一致性问题
    - 回写之前,数据库和缓存数据不一致,但是对于用户来说是一致的
    - 用 **SETNX(设置不存在的键)** 回写规避一致性问题
- **Refresh Ahead(提前刷新)**
  - 特点——利用 **CDC(变更数据捕获)** 接口监听数据变更;数据变更之后刷新缓存
  - 一致性问题
    - 在数据库中数据变更之后,到数据被刷新到缓存之前,数据是不一致的
    - 缓存未命中时,使用 **SETNX(设置不存在的键)** 来回写缓存
- **Singleflight(单次请求合并)**
  - 特点——多个线程或者协程在缓存未命中时去加载数据;同一个key,只有一个允许过去加载数据,其他都在等待
  - 适合高并发场景
- 删除缓存
  - 特点:更新数据库之后直接删除缓存——可以立刻删除,也可以异步删除
  - 降低缓存命中率
  - 存在数据一致性问题
- 延迟双删
  - 特点:更新数据库之后直接删除缓存;过一段时间后,再次删除缓存
  - 降低缓存命中率
  - 加重数据库查询负担
### 用装饰器模式实现缓存模式
- 无侵入式;可适配不同缓存实现
### 选用什么模式
- 因地制宜;面试——考虑延迟双删