• 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种处理方式:

    • 先删除缓存,再更新数据库(不推荐):并发请求多的时候,容易产生缓存和数据库值不一致的情况。
    • 先更新数据库,再删除缓存(推荐):并发请求多的时候,会存在短暂的数据不一致问题。
  • 相关阅读:
    K8s: 持久化存储之卷, NFS卷
    开源与闭源:创新与安全的平衡
    元素可视区client系列和元素滚动 scroll 系列
    GBASE 8s事务配置参数
    OSPF协议LSDB同步过程和邻居状态机
    Nginx在前后端分离项目中的配置
    今日准备注销CSDN专栏
    Docker Buildx使用教程:使用Buildx构建多平台镜像
    echarts环图配置
    vue 设置定时器在某个时间段执行
  • 原文地址:https://blog.csdn.net/Huangjiazhen711/article/details/127817325