缓存模式
总结
- Cache aside。 对于读操作,先读缓存,读不到就读db,然后回写缓存;对于写操作,会先更新db,然后删除缓存(使缓存失效)
- Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,读cache不到就读db,然后写入缓存。写直接写cache, 然后 cache 服务自己更新 db(同步更新 cache 和 db)。
- Write Behind 则是只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。和read/write through区别就是同步和异步的
在并发场景下,没办法保证更新操作的有序性。关于这点,我们可以利用系统本身的有序性来解决,例如利用binlog来保证有序性,更新或删除缓存。
或者延迟删除,避免读了mysql从库又把数据写到缓存了, 主动同步有延迟。跨机房也有类似问题。
缓存模式
Cache Aside 标准缓存模式
对于读操作,先读缓存,读不到就读db,然后回写缓存;对于写操作,会先更新db,然后删除缓存(使缓存失效)。
- 为什么是删除缓存而不是更新缓存呢? 防止并发出现更新缓存更新的是旧的数据,这个概率很大。同时如果写db成功,更新缓存失败,会导致数据不一致
缺点:
- 极端情况不一致:坑点在于缓存因冷启动或过期无数据的情况下读写并发操作,读请求读不到缓存,读请求从持久层读取到老数据,写请求更新持久层,写请求失效缓存,读请求将老数据写入缓存;缓存和持久层不一致
- 请求的数据如果不在缓存中就需要经过多个步骤来获取数据,这会导致明显的延迟
- 当写操作很频繁的时候,缓存会一直被删除,导致请求一直打到db,有打爆db 的风险
优点:
- Cache Aside 在缓存miss的时候读db回写cache是在业务代码(Application)中支持该逻辑的,自由度很高,支持db 和 缓存的 数据模型不一致的情况(甚至是跨表跨数据库)
Read Through 模式 && Write Through 模式(直写)
应用使用缓存作为主要的数据存储,
对于读请求:直接读cache, 由其他系统负责将database数据异步写到cache里。
对于写请求:将数据读直接写到缓存中,而缓存负责同步写数据到数据库中。
- 应用向缓存中添加/更新数据
- 缓存同步地写入数据存储
- 返回所需内容
Read-Through
对于读操作,直接读cache, cache的数据是由其他系统回写到cache的。
Read-Through 和 Cache Aside 很像,但是有两个不同点:
- Cache Aside 在缓存miss的时候读db回写cache是在业务代码(Application)中支持该逻辑的,而Read-Through是由其他系统回写cache的
- Cache Aside 由于回写cache 是业务代码自身控制,所以自由度很高,支持db 和 缓存的 数据模型不一致的情况(甚至是跨表跨数据库);而Read-Through 只能支持相同数据模型(其实也未必,但是不能跨表和跨数据库是确定的。而且多行对应一个缓存key也不好处理)
Write Through
对于写操作,直接写cache, cache写到database成功后返回,配合read-through后可以达到数据最终一致性。而且不需要在更新DB后删除缓存。
缺点:
- 写入的大多数数据可能永远都不会被读取,用 TTL 可以最小化这种情况的出现。
Write Behind 模式 (回写)
- 读取数据:命中缓存则直接读取,未命中缓存则需要缓存从持久层读取数据
- 写入数据:只更新缓存,不更新持久层,而缓存会异步地批量更新持久层
- 注意!无论读写如果需要挤出已更新到缓存但未持久化的数据,需要提前将这些数据写入持久层
好处:
- I/O速度飞快,多份对持久层的写入操作可以合并进行
- 这种方式能极高地提升写入性能,适用于写入qps极高的服务,配合Read-Through 可以让数据有很好的一致性。
- 可以容忍db故障一段时间。
- 如果Cache 故障,可能会丢数据。可以考虑msyql算法(change-buffer不会丢)
坏处:
- 数据不是强一致性的,而且可能会丢失(缓存可能在其内容成功存储之前丢失数据)
刷新(自动异步刷新)
你可以将缓存配置成在到期之前自动刷新最近访问过的内容。
如果缓存可以准确预测将来可能请求哪些数据,那么刷新可能会导致延迟与读取时间的降低。
刷新的缺点:
- 不能准确预测到未来需要用到的数据可能会导致性能不如不使用刷新。
工程优化
Local cache
redis-mysql的缓存一致性。 通过监听mysql的binlog来删除。这种的优势是不需要自己编写更新的业务逻辑,不侵入代码
localCache-redis的缓存一致性。设置定时器,比如每1s从database读数据更新到localCache. (有人想为何不更新redis的时候自动写入localCache,这个多实例的话实现不了)
热key
通过local-cache, redis, database三层缓存。
- set的时候,会自动的先设置本地的缓存。然后再设置到业务redis。
- get的时候,先找本地缓存,再找redis
- hotkey通过一个线程将数据发送到其他的服务上进行计算,并有一个线程主动的拉取,更新缓存