【Redis系列笔记】双写一致性

本文涉及的产品
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 MongoDB,通用型 2核4GB
简介: 本文讨论了缓存不一致问题及其后果,如价格显示错误和订单计算错误。问题主要源于并发和双写操作的异常。解决方案包括使用分布式锁(但可能导致性能下降和复杂性增加)、延迟双删策略(通过延迟删除缓存来等待数据同步)以及异步同步方法,如通过Canal和MQ实现数据的最终一致性。面试中,可以提及这些策略来确保数据库和缓存数据的一致性。

1. 概念

缓存不一致问题是指当发生数据变更后该数据在数据库和缓存中是不一致的,此时查询缓存得到的并不是与数据库一致的数据。

1.1. 缓存不一致会导致什么后果?

比如:查看商品信息的价格与真实价格不一致,影响用户体验,如果直接使用缓存中的价格去计算订单金额更会导致计算结果错误。

造成缓存不一致的原因可能是在写数据库和写缓存两步存在异常,也可能是并发所导致。

写数据库和写缓存导致不一致称为双写不一致,比如:先更新数据库成功了,更新缓存时失败了,最终导致不一致。并发导致缓存不一致举例如下:

执行流程(后来的线程先执行完):

  • 线程1先写入数据库X,当去写入缓存X时网络卡顿
  • 线程2先写入数据库Y
  • 线程2再写入缓存Y
  • 线程1 写入缓存旧值X覆盖了新值Y

即使先写入缓存再写数据在并发环境也可能存在问题,如下图:

流程:

线程1先写入缓存X,当去写入数据库X时网络卡顿

线程2先写入缓存Y

线程2再写入数据库Y

线程1 写入数据库旧值X覆盖了新值Y

2. 解决方案

2.1. 使用分布式锁

流程:

  • 线程1申请分布式锁,拿到锁。此时其它线程无法获取同一把锁。
  • 线程1写数据库,写缓存,操作完成释放锁。
  • 线程2申请分布锁成功,写数据库,写缓存。
  • 对双写的操作每个线程顺序执行。

对操作异常问题仍需要解决:写数据库成功写缓存失败了,数据库需要回滚,此时就需要使用分布式事务组件。

使用分布式锁解决双写一致性不仅性能低下,复杂度增加。

2.2. 延迟双删

既然双写操作存在不一致,我们把写缓存改为删除缓存呢?

先写数据库再删除缓存,如果删除缓存失败了缓存也就不一致了,那我们改为:先删除缓存再写数据库,如下图:

执行流程:

  • 线程1删除缓存
  • 线程2读缓存发现没有数据此时查询数据库拿到旧数据写入缓存
  • 线程1写入数据库

即使线程1删除缓存、写数据库操作后线程2再去查询缓存也可能存在问题,如下图:

线程1向主数据库写,线程2向从数据库查询,流程如下:

  • 线程1删除缓存
  • 线程1向主数据库写,数据向从数据库同步
  • 线程2查询缓存没有数据,查询从数据库,得到旧数据
  • 线程2将旧数据写入缓存

解决上边的问题采用延迟双删:

线程1先删除缓存,再写入主数据库,延迟一定时间再删除缓存。

上图线程1的动作简化为下图:

延迟多长时间呢?

延迟主数据向从数据库同步的时间间隔,如果延迟时间设置不合理也会导致数据不一致。

延迟时间设置的不合理,可能会带来更多问题

  • 过短:脏数据
  • 过长:更新失效不及时

2.3. 异步同步

延迟双删的目的也是为了保证最终一致性,即允许缓存短暂不一致,最终保证一致性。

保证最终一致性的方案有很多,比如:通过MQ、Canal、定时任务都可以实现。

Canal是一个数据同步工具,读取MySQL的binlog日志拿到更新的数据,再通过MQ发送给异步同步程序,最终由异步同步程序写到redis。此方案适用于对数据实时性有一定要求的场景。

通过Canal加MQ异步任务方式流程如下:

流程如下:

  • 线程1写数据库
  • canal读取binlog日志,将数据变化日志写入mq
  • 同步程序监听mq接收到数据变化的消息
  • 同步程序解析消息内容写入redis,写入redis成功正常消费完成,消息从mq删除。

定时任务方式流程如下:

专门启动一个数据同步任务定时读取数据同步到redis,此方式适用于对数据实时性要求不强更新不频繁的数据。

线程1写入数据库(业务数据表,变化日志表)

同步程序读取数据库(变化日志表),根据变化日志内容写入redis,同步完成删除变化日志。

3. 面试话术

3.1. 如何实现数据库与缓存数据一致?

实现方案有下面几种:

  1. 本地缓存同步:当前微服务的数据库数据与缓存数据同步,可以直接在数据库修改时加入对Redis的修改逻辑,保证一致。
  2. 跨服务缓存同步:服务A调用了服务B,并对查询结果缓存。服务B数据库修改,可以通过MQ通知服务A,服务A修改Redis缓存数据(推荐)
  3. 通用方案:使用Canal框架,伪装成MySQL的salve节点,监听MySQL的binLog变化,然后修改Redis缓存数据
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
29天前
|
缓存 NoSQL 安全
【Redis系列笔记】缓存三剑客
缓存穿透是指请求一个不存在的数据,缓存层和数据库层都没有这个数据,这种请求会穿透缓存直接到数据库进行查询。它通常发生在一些恶意用户可能故意发起不存在的请求,试图让系统陷入这种情况,以耗尽数据库连接资源或者造成性能问题。 缓存击穿发生在访问热点数据,大量请求访问同一个热点数据,当热点数据失效后同时去请求数据库,瞬间耗尽数据库资源,导致数据库无法使用。 缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。
77 1
|
29天前
|
存储 缓存 NoSQL
蚂蚁金服P7私藏的Redis原理与实践内部笔记
Redis 是完全开源免费的,是一个高性能的key-value类型的内存数据库。整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。
66 1
|
29天前
|
NoSQL Redis
Redis事务:保证数据操作的一致性和可靠性
Redis事务:保证数据操作的一致性和可靠性
|
29天前
|
NoSQL Java 关系型数据库
【Redis系列笔记】分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
389 2
|
15天前
|
存储 消息中间件 缓存
Redis - 笔记
Redis是开源的内存数据结构存储系统,兼备数据库、缓存和消息中间件功能。它支持字符串、哈希、列表、集合、有序集合等数据结构,以及地理空间、基数统计和位图等特殊类型。Redis具备复制、LUA脚本、LRU事件、事务、持久化、哨兵和集群等高级特性,以实现高可用性。应用场景包括计数器、存储用户信息、消息队列、共同关注等。字符串最大容量为512M。
|
29天前
|
缓存 NoSQL 关系型数据库
Redis 缓存 一致性
Redis 缓存 一致性
27 0
|
29天前
|
存储 NoSQL Redis
【Redis系列笔记】Redis总结
Redis是一个基于内存的 key-value 结构数据库。 Redis 是互联网技术领域使用最为广泛的存储中间件。 Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。 它存储的value类型比较丰富,也被称为结构化的NoSql数据库。
99 0
|
29天前
|
缓存 NoSQL Java
【Redis系列笔记】Redis入门
本文介绍了Redis常用命令,以及SpringBoot集成Spring Data Redis和Spring Cache。Spring Data Redis 提供了对 Redis 的操作方法,而 Spring Cache 则提供了基于注解的缓存功能,可以方便地将方法的返回值缓存到 Redis 中,以提高性能和减少对数据源的访问次数。这样的集成可以帮助开发者更便捷地利用 Redis 来管理应用程序的数据和缓存。
125 4
|
29天前
|
存储 缓存 NoSQL
Redis笔记 | 青训营
Redis笔记 | 青训营
|
29天前
|
缓存 NoSQL Java
【Redis系列笔记】Redis事务
Redis事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
113 3

相关产品

  • 云数据库 Redis 版