在项目中对于数据库和redis中的数据一致性问题,要考虑这些问题
- 删除缓存还是更新缓存
- 如何保证缓存与数据库同时操作成功
- 先操作缓存还是先操作数据库
删除缓存和更新缓存
更新缓存:
那么每次更新数据库都会触发更新缓存的动作,操作一次就得改一次缓存,这其中无效的操作很多
orderService.update(order); // 数据库更新
// 随之带来的就是缓存也得更新
redis: 更新对应键值对
// 这俩是一对一的关系
删除缓存:
只要操作数据库信息,那就把缓存删除。只有查的时候才重新把数据写到缓存中
orderService.update(order); // 数据库更新
orderService.update(order); // 数据库更新
orderService.update(order); // 数据库更新
// ......
// 以上操作触发一次删除缓存操作,然后对于redis就没有操作了
orderService.select(); // 查询数据库
// 把数据写到缓存并返回
如果选择删除缓存,那多个更新操作只对应一个删除缓存操作,在下一次查的时候再写入缓存。这其中就少了很多无效的操作
如何保证缓存与数据库同时操作成功
- 单体系统:将缓存与数据库操作放在一个事务内,保证两个操作的原子性
- 分布式系统:利用 TCC 等分布式事务方案
先操作缓存还是先操作数据库
缓存---数据库
正常情况下拿到的数据是没有问题的,但如果在线程1更新数据库之前,线程二来查询,就会出现问题
线程1只删除了缓存,线程2来查询没有命中缓存,然后就去查数据库,写入缓存。然后切换到线程1,再把线程1的新数据写到数据库里,而现在的缓存是由线程2写入的旧数据,和数据库里的数据不一致,出现脏数据的情况
数据库---缓存
线程2更新数据库,然后把缓存删了,然后线程1来查,发现并没有命中缓存,然后就去查数据库,再把数据写到缓存。但还是会出现异常情况
线程1查询数据,此时是没有缓存的,然后去查数据库,再查数据库的时间里,线程2更新的数据库字段,并且把缓存删了,此时删除的是空对象。最后线程1把查出来的旧数据写到缓存中里,现在缓存中的数据是旧的,而数据库中的数据是新的
对比两种情况
先操作缓存再操作数据库的过程中,由于线程1查询时缓存刚好失效,从而到数据库中查到旧数据,然后线程2先更新了数据库,再删除缓存,这两个操作其实是比较耗时的。线程1查完数据库之后直接就写入缓存,这期间的时间间隔是比较短的,可能造成的情况是线程1刚写完缓存,线程2就又给删了,下次再来线程查询拿到的是新的数据库中数据,然后再写到缓存中。
所以 先操作数据库,后操作缓存的方案更加可靠
总结
删除缓存而不是更新缓存,更新缓存会有很多无效操作
先操作数据库后操作缓存的方式更加可靠
用户不查则不重建缓存
先删除,后重建