分布式系统之数据库和缓存双写一致性

1. 缓存使用目的

缓存由于其高并发和高性能的特性,已经在项目中被广泛使用(IO cost比数据库低了几个数量级,redis是从内存中读取数据, mysql是从磁盘读取数据)

2. 读取数据

流程无异议:

  1.   判断是否有缓存 
  2. 有,直接返回数据给调用端. 无,跳到3 
  3. 从数据库加载数据,有数据直接写入缓存,无数据则跳到4
  4. 若不写入缓存, 可能发生缓存穿透(缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。)。可以缓存空对象来解决缓存穿透问题。
  5. 返回空数据。

3. 更新缓存

        3.1更新策略  

  • 先更新数据库,再更新缓存
  • 先删除缓存,再更新数据库
  • 先更新数据库,再删除缓存

       3.2 策略分析

  • 先更新数据库,再更新缓存

          技术角度分析: 多线程更新数据库和缓存时,并不能保证次序,比如A线程先发起,然后B线程后发起,但是B线程可能比A线程先更新缓存,这就导致了缓存最后的值是A线程的值导致数据库和缓存不一致

          业务角度分析:1. 加入业务是写多读少的场景,采用这种方案就会导致,数据压根还没读到,缓存就被频繁的更新,浪费性能。2. 加入写入缓存的值是要在数据库更新后,经过一系列计算再更新缓存的,采用此方案会造成性能浪费。

  • 先删除缓存,再更新数据库

          比如一个写线程A, 一个读线程B,在A线程删除缓存后,B线程发现缓存不存在就去读取数据库旧值,然后更新缓存,然后A线程才更新数据库,如此也会造成数据库缓存不一致。

  • 先更新数据库,再删除缓存

         这种方式同样有并发访问问题。同样2个线程,缓存失效,线程A探知缓存失效,查询数据库,取得旧值,线程B更新数据库并且删除缓存key,线程A将旧值写入缓存。

         但一般来说数据库IO远远慢比缓存IO,所以一般线程A写入缓存会发生在线程B删除缓存之前。

另外还有一个问题就是删除缓存不成功,用重试机制保障删除操作,一个是用消息队列,另一个是订阅binlog日志来更新缓存(canal)。

posted @ 2018-09-20 12:23  天天向上2015  阅读(219)  评论(0编辑  收藏  举报