您现在的位置是:网站首页 > 心得笔记

如何保证Redis与MySQL的数据一致性?

盛悦2025-02-24100人围观
简介如何保证Redis与MySQL之间的数据缓存一致性问题,特别是在高并发、高性能的应用场景下,如何才能既保证性能,又保证数据的最终一致性

缓存系统本质上是牺牲的强一致性,以换取更高的性能。因此,我们需要在缓存与数据库之间找到一种合适的平衡,保证数据的最终一致性

读取数据:旁路缓存策略

  • 当缓存中有数据时,直接从缓存中读取;当缓存中没有数据时,我们会从数据库加载数据到缓存中。

  • 这种策略的优点是减少了数据库的访问频率,显著提升了性能。然而,这种方式并不保证缓存和数据库中的数据保持一致。

写入数据:先更新数据库,再删除缓存

  • 在写操作中,我们采用“先更新数据库,再删除缓存”的策略。写数据时,首先更新数据库,然后删除缓存中的数据。这样做的目的是确保下一次访问时,缓存中能重新加载到最新的数据,避免缓存中的脏数据。


问题1:更新数据库成功,删除缓存失败了怎么办?那如何应对缓存删除异常?

答案1:删除缓存的操作异常,可能会导致缓存和数据库之间的数据不一致。为了应对这种情况,我们可以引入以下两种解决方案:

1. 消息队列重试机制(消息队列)

我们可以使用消息队列来解决删除缓存失败的问题。操作步骤如下:

  • 将缓存删除操作放入消息队列中,由消费者来执行删除操作。
  • 如果删除缓存失败,消费者会从消息队列中获取缓存删除任务,并再次尝试删除缓存。这就是所谓的重试机制
  • 如果删除操作成功,消费者会从消息队列中移除该任务,避免重复操作。
  • 如果重试超过一定次数仍然无法成功删除缓存,我们就需要报警,通知开发人员处理问题。

这种方案可以避免缓存删除操作的异常,但是它也带来了一个问题,那就是业务代码的入侵。需要在很多地方加入消息队列的逻辑,甚至可能会影响到业务代码的结构。

举个简单的例子,假设我们有一个用户信息更新操作,当用户修改自己的信息时,首先更新数据库中的数据,然后删除缓存。
如果删除缓存失败,消息队列会将该任务重新放入队列中,消费者将会继续尝试删除缓存,直到成功为止

// 更新数据库操作
public boolean updateUserInfo(User user) {
    // 1. 更新数据库
    boolean updateSuccess = updateDatabase(user);

    if (updateSuccess) {

        // 2. 将删除缓存的操作放入消息队列
        messageQueue.send(new CacheDeleteMessage(user.getId()));
    }

    return updateSuccess;
}

// 消费者处理缓存删除操作
public void handleCacheDeleteMessage(CacheDeleteMessage message) {
    try {
            // 1. 删除缓存
            boolean cacheDeleted = deleteCache(message.getUserId());
    
            if (!cacheDeleted) {
                // 2. 删除失败,重新放入消息队列进行重试
                messageQueue.send(message);
            }
        } catch (Exception e) {
            // 3. 出现异常,重新尝试删除
            messageQueue.send(message);
        }
    }

2.订阅MySQL Binlog,再删除缓存

另一种方式是通过订阅MySQL的Binlog,即数据库变更日志,来确保缓存的删除。具体做法如下:

  • 更新数据库之后,数据库会记录一条变更日志,这条日志会被写入Binlog中。
  • 我们可以通过订阅Binlog,监听数据库的变更。比如,使用Canal这个中间件,模拟MySQL的从节点,订阅并读取Binlog日志,获取到变更数据。
  • 根据Binlog中的变更信息,我们可以决定是否需要删除缓存,或者更新缓存。

Canal是阿里巴巴开源的一款中间件,它通过模拟MySQL的主从复制协议,向MySQL主节点请求数据,获取Binlog数据,并将其推送到下游系统。

通过订阅这些变更日志,我们就可以在数据库变更之后立即删除缓存,保证数据的一致性。

参考文章mp.weixin.qq.com/s/HUMu42ZYijHvPGfdEuts3A