首页 > 数据库 >Redis缓存和Mysql数据一致性问题

Redis缓存和Mysql数据一致性问题

时间:2024-09-09 12:23:36浏览次数:12  
标签:缓存 Redis 数据库 redis 更新 value key Mysql

        在高并发环境下,保持 Redis 缓存和 MySQL 数据库的数据一致性是一个复杂但至关重要的任务。下面是对这一问题的详细讲解,并结合 PHP 代码示例来展示如何解决这些一致性问题。

问题背景

Redis 缓存和 MySQL 数据库的主要挑战在于:

  1. 缓存和数据库之间的延迟:在缓存更新与数据库更新之间,可能存在时间差,导致数据不一致。
  2. 高并发下的缓存击穿:缓存被并发请求同时击穿,导致大量请求直接访问数据库。
  3. 缓存雪崩:大量缓存同时过期,导致瞬间对数据库的压力激增。
  4. 缓存穿透:恶意请求绕过缓存直接访问数据库,造成数据库的压力。

解决方案

1. 缓存一致性策略
Cache Aside Pattern (旁路缓存模式) 是最常用的缓存一致性策略:
  • 读取:先从缓存读取数据,如果缓存中有数据,直接返回;如果缓存中没有数据,则从数据库中读取,并将数据写入缓存。
  • 写入:先更新数据库,然后删除缓存,使下一次读取时重新从数据库获取数据。

示例代码

function getFromCacheOrDb($key) {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    // 从缓存中读取数据
    $cachedData = $redis->get($key);
    if ($cachedData !== false) {
        return $cachedData;
    }

    // 缓存未命中,从数据库中查询
    $data = getDataFromDb($key);
    if ($data) {
        $redis->setex($key, 3600, $data); // 缓存 1 小时
    }
    return $data;
}

function updateData($key, $newData) {
    $db = new PDO('mysql:host=localhost;dbname=test', 'root', '');

    // 更新数据库
    $stmt = $db->prepare("UPDATE table_name SET value = :value WHERE key = :key");
    $stmt->execute([':value' => $newData, ':key' => $key]);

    // 删除缓存
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $redis->del($key);
}

优点

  • 简单易用,符合大多数场景。

缺点

  • 在高并发下,可能会有缓存击穿问题。
2. 延迟双删

延迟双删策略是针对 Cache Aside 模式的改进,主要用于防止缓存和数据库的更新顺序导致数据不一致问题:

  1. 更新数据库
  2. 删除缓存
  3. 等待一段时间(例如 500ms),然后再次删除缓存

示例代码

function updateDataWithDelayDelete($key, $newData) {
    $db = new PDO('mysql:host=localhost;dbname=test', 'root', '');

    // 更新数据库
    $stmt = $db->prepare("UPDATE table_name SET value = :value WHERE key = :key");
    $stmt->execute([':value' => $newData, ':key' => $key]);

    // 删除缓存
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $redis->del($key);

    // 延迟再次删除缓存
    usleep(500 * 1000); // 延迟 500ms
    $redis->del($key);
}

优点

  • 有效减少了因为缓存删除延迟导致的数据不一致。

缺点

  • 延迟设置不当可能仍会导致短暂的不一致。
3. 分布式锁

分布式锁可以确保在高并发情况下,只有一个线程可以进行数据和缓存的更新操作。Redis 提供了实现分布式锁的能力。

示例代码

function updateDataWithLock($key, $newData) {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    $lockKey = "lock:" . $key;
    if ($redis->set($lockKey, 1, ['nx', 'ex' => 5])) { // 获取锁,超时 5 秒
        try {
            // 更新数据库
            $db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
            $stmt = $db->prepare("UPDATE table_name SET value = :value WHERE key = :key");
            $stmt->execute([':value' => $newData, ':key' => $key]);

            // 删除缓存
            $redis->del($key);
        } finally {
            // 释放锁
            $redis->del($lockKey);
        }
    } else {
        // 获取不到锁,稍后重试
        return false;
    }

    return true;
}

优点

  • 确保在高并发情况下的数据一致性,避免并发更新问题。

缺点

  • 实现复杂,锁的超时和释放需要精确控制。
4. 异步更新

使用消息队列来处理数据更新,使得写操作与缓存更新异步进行。这种方式可以解耦数据写入和缓存更新的操作。

示例流程

  1. 写请求将更新操作发送到消息队列。
  2. 消费者从消息队列中取出消息,进行数据库和缓存的更新操作。

优点

  • 解耦了写操作和缓存更新,可以灵活处理失败重试。

缺点

  • 增加了系统的复杂性和延迟,适用于对一致性要求不那么严格的场景。

解决方案总结

在高并发场景下,确保 Redis 缓存和 MySQL 数据库的一致性需要结合多种策略:

  1. Cache Aside Pattern 适用于大多数场景,但需要结合缓存击穿的处理。
  2. 延迟双删 可以减少缓存和数据库的不一致问题,适用于较高要求的场景。
  3. 分布式锁 适用于需要严格一致性的场景,通过锁机制确保数据更新的一致性。
  4. 异步更新 解耦写操作与缓存更新,适用于业务复杂度较高的场景。

根据实际业务需求和并发压力选择合适的策略,并在生产环境中进行充分的测试和优化,以确保系统的稳定性和数据一致性。

标签:缓存,Redis,数据库,redis,更新,value,key,Mysql
From: https://blog.csdn.net/Crazy_shark/article/details/142053422

相关文章

  • Redis分布式锁查看机制与实现解析
    分布式系统中,锁的使用是保证资源一致性与并发控制的重要手段。Redis作为一个高效的内存存储工具,通过其简单的命令操作和快速响应机制,被广泛用于实现分布式锁。本文将深入探讨Redis中查看分布式锁的机制,包括如何查询锁的状态、使用何种命令进行锁操作,以及如何确保锁的有效性和正确性......
  • 基于SpringBoot+Vue+MySQL的足球俱乐部管理系统
    系统展示用户前台界面管理员后台界面系统背景  如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解决一些老技术的弊端问题。因为传统足球俱乐部管理系统信息管理难度大,容错率低,管理......
  • 基于SpringBoot+Vue+MySQL的牙科医就诊管理系统
    系统展示用户前台界面管理员后台界面系统背景  当前社会各行业领域竞争压力非常大,随着当前时代的信息化,科学化发展,让社会各行业领域都争相使用新的信息技术,对行业内的各种相关数据进行科学化,规范化管理。这样的大环境让那些止步不前,不接受信息改革带来的......
  • docker 安装mysql8.0
    1、window安装dockerdesktop查询镜像:dockerimages删除镜像:dockerrmi[镜像id]查询容器:dockerps-a删除容器:dockerrm[容器id]停止容器:dockerstop[容器id]重启容器:dockerrestart[容器id]启动命令:dockerrun-it-vD:/docker/mysql/data:/var/lib/mysql-vD:/doc......
  • 3.6 MySQL基本查询大全(select、子查询、Distinct、group by分组,order排序、limit限制
    文章目录3.6.1MySQL的基本查询1.SELECT语句基本语法2.DISTINCT3.指定列,去除重复列4.给列设置别名5.使用WHERE子句查询指定条件比较判断范围判断字符串模式匹配错误判断空值判断6.使用ORDER子句对查询结果排序7.使用LIMIT限制查询结果数量3.6.2分组查询1.聚......
  • 3.5 MySQL数据的维护详解(插入数据、更新数据、删除数据)
    文章目录MySQL插入数据3.5.1基本的INSERT语句插入单个值插入多列值插入多行数据2.使用子查询向表中插入数据插入子查询结果作为单行插入子查询结果作为多行注意事项3.5.2MySQL更新数据基本语法示例更新单列更新多列更新所有行使用函数更新数据基于其他表更新数据注......
  • mysql主从复制
    mysql主从复制描述:MySQL数据库的主从复制方案,是其自带的功能,并且主从复制并不是复制磁盘上的数据库文件,而是通过binlog日志复制到需要同步的从服务器上。MySQL数据库支持单向、双向、链式级联,等不同业务场景的复制。在复制的过程中,一台服务器充当主服务器(Master),接收来自用户的内......
  • redis入门
    redis入门Nosql与sqlNosql是什么NoSQL,指的是非关系型的数据库。NoSQL有时也称作NotOnlySQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。sql是什么sql指的是关系型数据库。关系型数据库要满足4大特征,也就是我们常提的ACID原则(A原子性、C一致性、I独立性、D......
  • redis订阅者和进阶
    Redis进阶redis订阅者模式简介redis存在订阅者模式。就像是一个广播系统。存在三种角色:订阅者、发布者、频道。在redis当中,它们表示为subscriber(订阅者)、publisher(发布者)、channel(频道)其中的行为大抵是:订阅者订阅频道-->发布者针对于特定频道发布信息-->特定订阅者......
  • redis主从备份
    redis主从备份主从复制介绍redis主从复制原理:从服务器向主服务器发送SYNC命令。接到SYNC命令的主服务器会调用BGSAVE命令,创建一个RDB文件,并使用缓冲区记录接下来执行的所有写命令。当主服务器执行完BGSAVE命令时,它会向从服务器发送RDB文件,而从服务器则会接收并载......