Redis学习——数据不一致怎么办?更新缓存失败了又怎么办?

引言

  • 最近面试快手的时候被问到了缓存不一致怎么解决?一开始还是很懵的,因为会默认将自己带到一个错误的前提下,就是数据已经不一致了,你怎么办?可能会想,遍历缓存,删除比对数据是不是一致的。停下来想一下,一般是修改数据库的时候,你应该怎么操作缓存,才能保证缓存的一致性。我就知道要么删除缓存后续重建,要么直接修改缓存,但是我对于这两种场景的方法了解的并不多,今天借这个机会,完全整理一下!

正文

数据一致分几种情况?

  • 缓存中有数据,必须要和数据库中的数据一致
  • 缓存中本身没有数据,数据库中的值必须要是最新的

常见的缓存的策略主要有两种,分别是读写缓存和只读缓存,不同策略保证缓存一致性的方式是不一样。具体如下

读写缓存的数据一致性

  • 读写缓存是对于数据的所有增删改都需要在缓存中进行,然后在采取对应的写回策略。主要有两种写回策略
    • 同步直写策略
    • 异步写回策略

在这里插入图片描述

同步直写策略

  • 写缓存的时候,同步写数据库,缓存和数据库中的数据是一致的
    • 需要通过事务机制,保证缓存和数据库更新的原子性,一般是通过tranactional修饰
      • 要么同步更新成功
      • 要么同步更新失败,下次重试

在这里插入图片描述

异步写回策略

  • 写缓存的时候,不同步写入缓存,等到数据从缓存中淘汰的时候,在写入数据库。
    • 问题
      • 一旦缓存崩溃,在缓存中保存的数据,都会崩溃丢失

在这里插入图片描述

只读缓存的数据一致性

  • 如果有数据更新(数据增加和修改),直接写入数据库&#

### 实现 Redis 缓存与 MySQL 数据一致更新的最佳实践 为了确保 Redis 缓存与 MySQL 数据之间的一致性,可以通过多种方法和技术手段来减少或消除数据一致的可能性。以下是几种常见且有效的方法: #### 1. 使用双删策略 双删策略是一种简单却高效的缓存一致性维护方式。其核心思想是在更新 MySQL 数据之前先删除对应的 Redis 缓存条目,这样可以避免因缓存未及时刷新而导致的数据陈旧问题。 ```php // 删除 Redis 中的对应键值 $redis->del('key_name'); // 更新 MySQL 数据 mysqli_query($conn, "UPDATE table SET column=value WHERE condition"); ``` 这种方法通过让后续请求重新加载最新的数据Redis 来保证一致性[^3]。 --- #### 2. 设置合理的过期时间 为 Redis 缓存设置较短的有效期限可以帮助缓解由于缓存未同步带来的风险。即使某些情况下出现了短暂的数据一致,也可以随着缓存到期自动修复。 ```php // 将数据写入 Redis 并设置有效期 $redis->setex('key_name', $ttl_in_seconds, json_encode($data)); ``` 这种方式适用于那些对实时性要求是特别高的场景。 --- #### 3. 主动更新缓存 主动更新缓存是指在每次成功修改 MySQL 后立即将新版本的数据推送到 Redis 中覆盖原有记录。此方法能够最大程度上保障两者间的数据同步状态。 ```php // 更新 MySQL 数据 mysqli_query($conn, "UPDATE table SET column=value WHERE condition"); // 获取最新数据并放入 Redis $result = mysqli_fetch_assoc(mysqli_query($conn, "SELECT * FROM table WHERE condition")); $redis->set('key_name', json_encode($result)); ``` 尽管如此,仍需注意可能出现的部分失败情况(比如 MySQL 成功但 Redis 失败),因此建议配合其他措施共同作用[^4]。 --- #### 4. 利用消息队列解耦操作流程 引入 Kafka 或 RabbitMQ 等异步通信工具可以在一定程度上简化跨组件间的协调工作量。具体做法是每当有针对数据库的操作发生时便向队列发送通知;消费者端监听这些事件并将相应动作应用于缓存层之上。 ```php // 生产者部分 - 发送消息至队列 $messageBroker->publish('update_cache_topic', ['action' => 'invalidate', 'key' => 'key_name']); // 消费者部分 - 接收消息处理逻辑 if ($message['action'] === 'invalidate') { $redis->del($message['key']); } ``` 采用该设计仅有助于提升系统的可扩展性和鲁棒性,而且还能降低直接依赖所带来的潜在隐患[^2]。 --- #### 5. 应用分布式事务协议 对于极高一致性需求的应用来说,借助两阶段提交 (2PC) 或 Saga 流程这样的高级机制或许是最稳妥的选择之一。它们允许我们将涉及多资源管理器的任务拆分为若干子步骤执行,并只有当所有环节均顺利完成之后才会正式生效整个变更过程。 ```sql -- 阶段一:准备阶段 START TRANSACTION; UPDATE mysql_table SET field=new_value WHERE id=record_id; INSERT INTO redis_transaction_log VALUES ('txid', 'pending'); COMMIT; -- 阶段二:确认阶段 IF ALL_SUBTASKS_SUCCEEDED THEN UPDATE redis_data SET value=new_redis_value WHERE key='related_key'; DELETE FROM redis_transaction_log WHERE txid='txid'; END IF; ``` 过需要注意的是这类方案通常伴随着额外开销以及复杂度增加等问题,故仅推荐用于特定场合下使用。 --- ### 总结 以上列举了几种同的技术路线供开发者们根据实际业务特点灵活选用。无论是简单的单机环境还是复杂的微服务架构体系之下,都可以找到适合自己的平衡点以达成预期目标——即尽可能接近完美的 Redis 和 MySQL 的数据一致性效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值