学新通技术网

【redis】数据一致性问题

juejin 20 1
【redis】数据一致性问题

前言

当我们使用Redis做缓存时,数据不一致问题是绕不过的问题。如果我们没有很好的处理数据一致性问题,就有可能影响用户体验,最严重的会造成业务损失。数据一致性的场景和解决方式都有哪些呢?让我们一探究竟。

缓存和数据库的数据不一致发生原因

数据一致性总结就是2种情况:

  • 缓存中有数据,这时缓存的数据和数据库的相同;
  • 缓存中没有数据,这时数据库的值是最新值。

因此,不符合这2种情况的就是数据不一致了。而缓存又包括读写缓存和只读缓存。 发生数据不一致的情况有各自的区别。

读写缓存

对数据增删改操作时,需要在缓存中进行。同时呢还得根据写回策略,看是否要同步写回到数据库。

  • 同步直写策略:写缓存时,并同步写数据库。缓存和数据库的数据一致。
  • 异步写回策略:写缓存时不同步写数据库,而是等数据淘汰触发时,再将数据写回数据库。此情况下,若未触发数据淘汰就出现故障,数据库就没有最新数据,造成数据不一致的问题。

总结一下,采用读写缓存时,要保证数据一致性问题,就要采用同步直写策略。若数据一致性要求不是很高,可采用异步写回策略。

只读缓存

当要新增数据,就直接写到数据库;当数据删除或修改时,修改数据库的值,并将缓存的数据清除。

分析一下数据一致性的问题:

  • 新增数据:数据直接写到数据库,不对缓存操作。此时不存在一致性的问题。
  • 删除和修改数据:这种情况下,既要更新数据库,也要删除缓存。若两个操作保证不了原子性,就会出现数据不一致问题。

只读缓存删改数据的不一致问题

  • 先删除缓存,再更新数据库:若缓存删除成功,数据库更新失败。后面访问就会缓存缺失,然后访问数据库就会查到旧值。(数据库更新失败,这种情况此操作不就失败了吗?不能叫旧值吧?)
  • 先更新数据库,再删除缓存:若更新数据库成功,删除缓存失败。后续访问的就是缓存中的旧值。

看上图很好理解这个数据不一致的产生过程:

  1. a旧值=1,客户端要更新a的值为2;
  2. 先更新数据库成功,此时数据库a = 2;
  3. 再删除缓存,但失败了。缓存还在。此时缓存中a = 1, 数据库中a = 2;
  4. 再去查询a的值。因缓存命中,返回a = 1,和数据库不一致。

数据不一致的解决方案

处理方式一:先删除缓存,再更新数据库。(不推荐)

发生数据不一致的场景:

  1. 线程1删除缓存中的a数据后,还没来得及更新数据库。
  2. 此时线程2读取数据a,会发现缓存缺失,然后会去读数据库。

线程2去数据库读取这个动作会带来2个问题:

  • 线程2读取的a是旧值(因为线程1已经改了a的值,只是还没更新到数据库)
  • 线程2在缓存缺失时读了数据库的旧值,这也就算了,它还会将这个旧值回写到缓存中。后续读取a可能都会读到旧值。

这种处理方式很容易造成数据不一致的问题,而且可能会影响后续多个查询操作。很多教程有提供延迟双删的解决方案(线程1更新完数据库后,先sleep一段时间,再进行一次缓存删除操作),这种sleep的方式无法准确预估其他多个线程开始和结束的时间。还是可能会产生数据不一致问题。个人建议别使用这种处理方式。

处理方式二:先更新数据库,再删除缓存。(推荐)

发生数据不一致的场景:

  • 若线程1删除了数据库的值a,但还没来得及删除缓存。
  • 此时线程2读取a数据,就会命中缓存中a的值,这就是读到了旧值。

但此情况若并发读a的线程不多,影响就不大。因为线程1马上就会将缓存中的a删除。后续读取a时就会出现缓存缺失,然后去数据库读a的最新值。对业务的影响较小。

总结

在Redis的缓存和数据库的数据不一致问题上,可分为读写缓存只读缓存来分析。

读写缓存有同步写回策略异步写回策略。采用同步写回能保证数据一致性,若数据一致性要求不是很高,可采用异步写回策略。

只读缓存有2种处理方式:

  • 先删除缓存,再更新数据库(不推荐):并发请求多的时候,容易产生缓存和数据库值不一致的情况。
  • 先更新数据库,再删除缓存(推荐):并发请求多的时候,会存在短暂的数据不一致问题。

本文出至:学新通技术网

标签: