首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

深入分析与解决方案:缓存与数据库双写不一致问题

编程知识
2024年08月20日 08:06

我们上次探讨了 Redis 的常见问题,本章将深入分析更细致的细节,例如如何从业务角度有效处理缓存与数据库之间的双写不一致问题。接下来,让我们深入研究这个话题。

key重建优化

开发人员通常使用“缓存+过期时间”的策略,以便既能加速数据读写,又能确保数据的定期更新。这种模式基本上能够满足绝大部分需求。然而,当以下两个问题同时出现时,可能会对应用系统造成严重的影响:

  1. 热点 key 的出现:当前的 key 是一个热点 key,例如一条热门的娱乐新闻,导致并发请求量非常大。这种情况会使得缓存的读取请求集中在这个热点 key 上,造成缓存的压力显著增加。
  2. 缓存重建的复杂性:当缓存失效后,重建缓存的过程不能在短时间内完成。重建缓存可能涉及复杂的计算任务,例如执行复杂的 SQL 查询、多次 I/O 操作、以及处理多个数据依赖等。这种复杂的重建过程可能会导致系统性能下降,进而影响用户体验。

在缓存失效的瞬间,如果大量线程同时启动缓存重建操作,会导致后端负载急剧增加,甚至可能使应用系统崩溃。这种情况会显著影响系统的稳定性和性能。为了解决这一问题,关键在于避免大量线程同时进行缓存重建。

一个有效的解决方案是使用互斥锁机制,该方法确保在任何给定时刻只有一个线程被允许执行缓存重建操作。其他线程则需要等待重建线程完成缓存重建后,才能从缓存中重新获取数据。这种策略不仅能减轻后端系统的压力,还能避免因并发重建引起的性能瓶颈,显著提升系统的稳定性和响应速度。

示例伪代码:

String get(String key) {

    // 从Redis中获取数据
    String value = redis.get(key);

    // 如果value为空,则开始重构缓存
    if (value == null) {

        // 生成唯一的mutex key,确保只有一个线程能重建缓存
        String mutexKey = "mutex:key:" + key;

        // 尝试设置mutex key,使用NX(仅在不存在时设置)和EX(设置过期时间)
        boolean isMutexSet = redis.set(mutexKey, "1", "ex 180", "nx");

        if (isMutexSet) {
            try {
                // 从数据源获取数据
                value = db.get(key);

                // 回写数据到Redis,设置过期时间
                redis.setex(key, timeout, value);

            } finally {
                // 删除mutex key,确保其他线程可以继续重建缓存
                redis.delete(mutexKey);
            }

        } else {
            // 其他线程等待50毫秒后重试
            Thread.sleep(50);
            value = get(key);
        }
    }

    return value;
}

缓存与数据库双写不一致

在高并发场景下,同时进行数据库与缓存的操作可能会引发数据不一致性的问题。具体来说,当多个线程或进程同时尝试更新缓存和数据库时,可能会导致缓存与数据库之间的数据不匹配。

双写不一致情况

当多个线程或进程同时进行缓存和数据库的更新时,可能出现以下问题:

  • 缓存与数据库的数据不一致:例如,两个线程同时更新数据库,但只一个线程更新了缓存,这会导致缓存中的数据和数据库中的数据不一致。
  • 延迟问题:即使在更新缓存和数据库时都执行了操作,也可能由于网络延迟或其他因素,导致缓存和数据库之间的状态不同步。

image

读写并发不一致

读写并发不一致是指在并发场景下,多个线程或进程对同一数据进行读写操作时,可能导致数据的不一致或错误。

image

以下是一些常见的读写并发不一致的解决方法:

  1. 针对并发几率较小的数据
    • 对于个人维度的订单数据、用户数据等,并发操作较少且对数据一致性的要求相对宽松。对于这类数据,可以通过设置缓存的过期时间来解决缓存与数据库之间的数据不一致问题。具体做法是,在缓存中设置合理的过期时间,缓存数据会在过期后自动失效。每当缓存失效时,系统将自动从数据库中读取最新的数据,并更新缓存。这种策略简单有效,可以大大减少缓存不一致的发生几率。
  2. 在并发较高的场景下的缓存数据一致性
    • 即使在业务场景下并发较高,但如果可以容忍短时间的缓存数据不一致(例如商品名称、商品分类菜单等),则仍然可以通过设置缓存的过期时间来满足大部分业务需求。通过合理设置过期时间,虽然缓存数据可能会在短时间内出现不一致,但这种不一致通常不会对业务造成严重影响。因此,缓存过期策略仍然是一种有效的解决方案。
  3. 对于不能容忍缓存数据不一致的场景
    • 如果业务对缓存数据的一致性有严格要求,可以使用分布式读写锁来保证并发读写操作的顺序性。具体做法是,在进行写操作时,通过分布式锁机制来确保只有一个操作能够执行,从而避免写写冲突。而对于读操作,通常可以在不加锁的情况下进行,以提高性能。分布式锁能够有效地控制并发写操作,确保数据的一致性,尽管可能会对系统性能产生一定影响。
  4. 引入中间件以维护数据一致性
    • 可以使用阿里开源的 Canal 工具,通过监听数据库的 binlog 日志来及时更新缓存。这种方法可以在数据发生变化时自动更新缓存,从而减少缓存和数据库之间的一致性问题。然而,引入 Canal 或类似的中间件会增加系统的复杂度,因此需要权衡其带来的额外复杂性和对系统一致性的增强。使用这种方案时,应考虑中间件的维护、配置和潜在的性能影响,以确保系统的稳定性和可靠性。

image

总结

上述解决方案主要针对的是读多写少的场景,通过引入缓存来提升性能。然而,对于写多读多且不能容忍缓存数据不一致的情况,我们需要重新考虑缓存的使用策略。以下是针对这种情况的优化建议:

  1. 避免使用缓存
    • 在写操作频繁且读操作也较多的场景中,如果业务对数据一致性的要求非常高,使用缓存可能并不是最佳选择。此时,直接操作数据库可以避免缓存数据与数据库数据之间的不一致问题,因为所有的数据操作都直接在数据库中进行,从而确保数据的一致性和准确性。
  2. 数据库作为主存储
    • 如果数据库面临着高负载的压力,但仍然需要处理大量的读写操作,可以考虑将缓存作为数据的主存储,而将数据库作为备份。具体做法是:所有的读写操作都先写入缓存,缓存会异步地将数据同步到数据库中。这样,缓存可以在高并发读写操作中提供快速的响应,而数据库则用于长期的数据存储和备份。这种策略可以提高系统的读写性能,同时保持数据库的数据完整性。
  3. 缓存适用的数据类型
    • 将缓存用于对实时性和一致性要求不是特别高的数据。例如,商品分类信息、系统配置等数据可以缓存,因为这些数据变化频率较低,对一致性要求不是很高。缓存能显著提升访问速度,但在数据不一致的情况下,对业务影响较小。避免将缓存用于对一致性要求极高的关键业务数据,以减少因缓存引发的复杂性和风险。
  4. 避免过度设计
    • 在设计缓存系统时,要避免为了保证绝对一致性而进行过度设计和复杂控制。这种过度设计不仅会增加系统的复杂性,还可能影响系统的性能。应当根据实际业务需求,合理选择缓存策略,平衡性能和一致性要求,避免不必要的复杂性和资源浪费。

总之,在选择是否使用缓存及其设计时,需要根据业务场景和数据一致性要求进行权衡。缓存应主要用于提升读操作性能,而对于写多读多且对一致性要求高的场景,可能需要依赖数据库本身的能力或采用其他策略来处理数据的一致性问题。


我是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。同时也是一位掘金优秀作者、腾讯云创作之星、阿里云专家博主、华为云云享专家。

💡 我将不吝分享我在技术道路上的个人探索与经验,希望能为你的学习与成长带来一些启发与帮助。

🌟 欢迎关注努力的小雨!🌟

From:https://www.cnblogs.com/guoxiaoyu/p/18363049
本文地址: http://www.shuzixingkong.net/article/1258
0评论
提交 加载更多评论
其他文章 manim边学边做--圆弧形
圆弧形可以算是一种特殊的圆形,它是圆形的一部分。manim中,单独为圆弧形状封装了几个模块: Arc:通用的圆弧,根据半径和角度来绘制圆弧 ArcBetweenPoints:根据两个点和角度来绘制圆弧 AnnularSector:环形圆弧,相当于是圆环的一部分 Sector:扇形,始终是1/4个圆环
manim边学边做--圆弧形 manim边学边做--圆弧形 manim边学边做--圆弧形
RISC-V全志D1多媒体套件文章汇总
提示 此开发板的任何问题都可以在我们的论坛交流讨论 https://forums.100ask.net/c/aw/d1/57 文章目录汇总 教程共计14章,下面是章节汇总: 第0章_RISC-V全志D1多媒体套件 第1章_快速启动 1_1 快速开始使用 1_2 学习路线 第2章_安装并配置
线上问题排查——接口长时间未响应
刚看到鱼皮的文章,一下午连续故障两次,谁把我们接口堵死了?!,想起之前刚进公司时遇到了一个类似问题 线上接口访问不通,超时等待,但是看后台日志是正常运行的,进服务器看监控,CPU 占用100%,经典面试题了 使用jsp -l 和 jstack <进程PID> > stack.txt
线上问题排查——接口长时间未响应
使用CyFES对配体运动轨迹进行数据透视
分子动力学模拟是一个以时间换空间的方法,那么在时间尺度上留下轨迹之后,如何把轨迹做一个静态的展现,正是数据透视所解决的问题。CyFES是一个开源的、基于GPU硬件加速的数据透视高性能计算工具,我们通过一个蛋白-配体相互作用的运动轨迹的示例,演示一下CyFES的基本使用方法。
使用CyFES对配体运动轨迹进行数据透视 使用CyFES对配体运动轨迹进行数据透视 使用CyFES对配体运动轨迹进行数据透视
Docker 容器中镜像导出/导入
目录【容器】镜像导出/导入导出导入带标签不带标签,后期修改【仓库】镜像导出/导入导出导入导入(完整命令) 创建一个简单的Docker镜像 利用docker ps -a命令查看要导出的镜像。 【容器】镜像导出/导入 导出 查看要导出的镜像 [root@localhost ~]# docker ps -
Docker 容器中镜像导出/导入 Docker 容器中镜像导出/导入 Docker 容器中镜像导出/导入
.NET 9 优化,抢先体验 C# 13 新特性
前言 微软即将在 2024年11月12日发布 .NET 9 的最终版本,而08月09日发布的.NET 9 Preview 7 是最终发布前的最后一个预览版。这个版本将与.NET Conf 2024一同亮相,并已与 Visual Studio 2022 17.12 预览版1一同发布,可以直接通过Vis
.NET 9 优化,抢先体验 C# 13 新特性 .NET 9 优化,抢先体验 C# 13 新特性
寻访中国100家.NET中大企业 —— 第二站:苏州行
一:事情起因 在.NET圈里混了十多年,相信有不少人知道我专注于玩 .NET高级调试,如今技术上的硬实力还是能够解决市面上的一些疑难杂症,但软实力却在另一个极端,如(人际交往,人情事故),所以就萌生了刻意训练的念头,便自我发起了这个活动 "寻访中国100家.NET中大企业"。 二
寻访中国100家.NET中大企业 —— 第二站:苏州行 寻访中国100家.NET中大企业 —— 第二站:苏州行 寻访中国100家.NET中大企业 —— 第二站:苏州行
网易云音乐故障 2 小时,这次到底谁背锅?(今天记得领补偿)
大家好,我是程序员鱼皮,8 月 19 日下午,网易云音乐突发严重故障,并登顶微博热搜,跟黑神话悟空抢了热度。 根据用户的反馈,故障的具体表现为:用户无法登录、歌单加载失败、播放信息获取失败、无法搜索歌曲等等,几乎是无法使用了,妥妥的 P0 级事故! 根据官方发布的说明,本次故障的主要原因是基础设施,
网易云音乐故障 2 小时,这次到底谁背锅?(今天记得领补偿) 网易云音乐故障 2 小时,这次到底谁背锅?(今天记得领补偿) 网易云音乐故障 2 小时,这次到底谁背锅?(今天记得领补偿)