乐趣区

缓存一致性的设计

对数据库的热数据进行缓存是一种常见方案,但是如何保证缓存的数据和数据库的数据一致性呢?本文将进行详细的探讨。常见的设计有如下三种:

一、先更新数据库再更新缓存

数据库有对应操作的时候,则操作对应的缓存,即数据库插入,则缓存插入;数据库更新缓存更新;数据库删除,缓存删除。

此种方案常见于缓存的数据比较简单,不需要复杂计算的过程中。

1.1 优点

缓存不会 miss,命中率高。

1.2 缺点

并发写的情况下,可能导致缓存数据不一致。

并发写的情况下,如果两个请求分别更新了某条数据,在更新完数据库之后,此时需要更新缓存数据。由于网络传输的问题,此时 更新数据库的请求 更新的缓存,便会导致缓存的数据存储的是第一次数据库更新之后的数据,导致数据不一致。因为缓存更新的时序应该和数据库更新的顺序一致。

因此对于 高并发写操作 的数据库业务不建议此方案。

1.3 改进事项

  • 需要保证缓存的执行顺序 和 数据库的执行顺序一致;同事也要避免长事务;

二、先删除缓存再操作数据库

当有更新操作的时候,先删除缓存,然后再更新数据库,如果查询缓存没有取到数据,则重新读取数据库数据,根据该数据更新缓存。

2.1 优点

淘汰缓存策略操作简单,且通用;

2.2 缺点

  • 增加了一次 cache miss
  • 并发读的情况下可能导致数据不一致

因此对于 高并发读操作 的数据库业务不建议此方案。

2.3 改进事项

  • cache miss 的情况下,读取数据库的数据时是否可以采用锁定读,如 select for update 的模式;并且更新的时候将缓存更新封装到事务里面(注:不仅需要在事务里面,还需要在事务的 select for update 之后),否则依然可能由于时序的问题导致缓存数据不一致;

三、先更新数据库 再删除缓存

先更新数据库,再删除缓存即 Cache Aside Pattern;先更新数据库,再删除缓存,如果查询缓存没有取到数据,则重新读取数据库数据,根据该数据更新缓存。

3.1 优点

淘汰缓存策略操作简单,且通用;

3.2 缺点

高并发的情况下,如果缓存不存在,则重新读取数据库数据,根据该数据更新缓存;此时如果主从延迟,可能会从数据库读到历史的数据,依然导致缓存不一致。

3.3 改进事项

  • cache miss 的情况下,读取数据库的数据时是否可以采用锁定读,如 select for update 的模式;或者强制读主库取数据;

四、小结

导致缓存数据不一致的因素主要有:

  • 高并发导致的,并发写 或者 并发读;
  • 数据库主从延迟;
  • 当然还有服务异常另当别论;

最后的最后使用缓存能显示的设置过期时间还是尽量设置过期时间吧,避免各种最终的不一致。

五、参考

1、缓存更新的套路

2、究竟先操作缓存,还是数据库?

退出移动版