在高并发场景中,缓存能抵挡大量数据库查询,减少数据库压力,对于缓存更新通常有以下几种模式可以选择:
- cache aside
- read/write through
- write behind caching
cache aside模式
Cache-aside模式是一种常用的用于管理缓存的模式。它用于确保缓存与底层数据源之间的数据一致性。以下是cache-aside模式的工作原理:
- 从缓存读取:当有读取操作请求时,应用程序首先检查缓存中是否存在数据。如果在缓存中找到了数据,则将其返回给调用者,避免了访问底层数据源的需要。
- 缓存未命中:如果在缓存中未找到数据,则表示缓存未命中。在这种情况下,应用程序从底层数据源检索数据,并将检索到的数据填充到缓存中。
- 更新数据:当对数据执行写入或更新操作时,应用程序首先更新底层数据源中的数据。然后,清除缓存中的数据,以确保下一次读取从数据源中检索到更新后的数据。
通常写缓存和写数据库是两个独立的事务,选择先更新缓存还是先更新数据库都有可能产生数据不一致的情况。
先删缓存,再更新数据库的问题
假设有两个请求A、B。
- 请求A先删除缓存,此时还未更新数据库
- 请求B查询缓存未命中,然后查询数据库,查出旧数据写入缓存
- 请求A继续将数据写入数据库
- 此时缓存与数据库中的数据出现了不一致的情况
将缓存更新不做删除的问题
- 请求A先更新了数据库
- 请求B更新了数据库,并更新了缓存
- 请求A最后更新缓存,此时请求A的数据是脏数据。
先更新db再失效缓存问题
先更新DB,再失效缓存也会出现问题
- 请求A读取缓存未命中,查询数据库成功查到数据
- 请求B进来更新数据库成功,并删除缓存数据
- 请求A将查询的数据写入到缓存中,此时请求A写入缓存的数据已经是脏数据
read/write through模式
cache aside模式需要应用方维护缓存的读写,对数据和缓存的维护设计侵入代码,代码复杂性增加。read/write through模式弥补了这一问题,调用方无需管理缓存和数据库调用,通过抽象缓存管理组件维护缓存和数据库的读写,解耦业务代码。
read through模式
当客户端请求一个数据时,如果缓存中不存在该项(缓存未命中),缓存系统会自动从后端存储中加载数据,然后将其添加到缓存中,并返回给客户端。对于后续的相同请求,数据则直接从缓存中获取,直到缓存过期或被淘汰。
write through模式
在write-through模式中,当客户端更新一个数据项时,缓存系统会同时更新缓存和后端存储。这意味着所有的写操作都会同步地写入缓存和存储,确保二者的数据一致性。
write behind caching模式
Write Behind模式和Write Through模式整个架构是一样的,核心在于write through在缓存数据库中的更新是同步的,而Write Behind是异步的。
每次的请求写都是直接更新缓存然后就成功返回,并没有同步把数据更新到数据库。而把更新到数据库的过程称为flush,触发flush的条件可自定义,如定时或达到一定容量阈值时进行flush操作。并且可以实现批量写,合并写等策略,也有效减少了更新数据的频率,这种模式最大的好处就是读写响应非常快,吞吐量也会明显提升。这种模式也有其他的问题,比如数据不是强一致性的,因为把最新的数据放在缓存里,如果缓存在flush到数据库之前宕机了就会丢失数据,另外实现也比较复杂。
几种模式对比
模式 | 优点 | 缺点 |
---|---|---|
Cache Aside | 实现比较简单 | 需要应用程序负责缓存的读取和写入操作,代码侵入较大 |
Read/Write Through | 引入缓存管理组件,缓存和数据库的维护对应用程序透明;应用代码入侵小,逻辑更清晰 | 引入缓存管理组件,实现更复杂 |
Write Behind Caching | 读写直接和缓存交互,异步批量更新数据库,性能最好 | 实现最复杂,数据一致性最弱 |