网站推广产品怎么做,软件商城官方下载,广西南宁市网站建设服务中心,做社交网站的预算文章目录 0. 前言1. 补充知识#xff1a;CP和AP2. 什么情况下会出现Redis与数据库数据不一致3. 更新缓存还是删除缓存4. 先操作缓存还是先操作数据库4.1 先操作缓存4.1.1 数据不一致的问题是如何产生的4.1.2 解决方法#xff08;延迟双删#xff09;4.1.3 最终一致性和强一致… 文章目录 0. 前言1. 补充知识CP和AP2. 什么情况下会出现Redis与数据库数据不一致3. 更新缓存还是删除缓存4. 先操作缓存还是先操作数据库4.1 先操作缓存4.1.1 数据不一致的问题是如何产生的4.1.2 解决方法延迟双删4.1.3 最终一致性和强一致性4.1.4 如何确定延迟双删的延迟时间 4.2 先操作数据库推荐使用4.2.1 数据不一致的问题是如何产生的4.2.2 解决方法删除延迟删除 5. 删除缓存失败的情况5.1 删除重试机制5.2 canal5.3 引入canal后的流程 6. 总结 阅读本文前可以先阅读我的另一篇博文
Windows环境下安装Redis并设置Redis开机自启 0. 前言
在面试的时候如果面试官看到我们有处理高并发项目的经验并且在项目中用到了 Redis面试官通常都会问 Redis 缓存怎么跟数据库保持一致我们一起来探讨一下这个问题
1. 补充知识CP和AP
在分布式系统的一致性模型中CP 和 AP 是 CAP 定理中的两个关键概念
CAP 定理也称为布鲁尔定理Brewer’s Theorem是由计算机科学家埃里克·布鲁尔Eric Brewer在 2000 年提出的
CAP 定理描述了分布式系统在设计时面临的三个基本属性即一致性Consistency、可用性Availability和分区容错性Partition tolerance并指出分布式系统在任何给定时间只能同时满足其中的两个属性 以下是 CP 和 AP 的含义
CPConsistency and Partition Tolerance 一致性Consistency指所有节点看到的数据是一致的即更新操作在所有节点上要么全部成功要么全部失败分区容错性Partition Tolerance指系统在出现网络分区即网络中的一部分节点无法与其他节点通信的情况下仍然能够继续运行CP系统在发生网络分区时会选择一致性和分区容错性可能会牺牲可用性。这意味着在分区发生时系统可能会拒绝某些操作以保证数据的一致性 APAvailability and Partition Tolerance 可用性Availability指系统在面对客户端的请求时总是能够给出响应即使是在部分节点失败或网络分区的情况下分区容错性Partition Tolerance系统在出现网络分区的情况下仍然能够继续运行AP系统在发生网络分区时会选择可用性和分区容错性可能会牺牲一致性这意味着系统在分区发生时仍然可以响应客户端的请求但可能会返回不一致的数据 总结来说CP 和 AP 是 CAP 定理中描述的两种不同的设计选择它们反映了分布式系统在不同场景下的权衡
选择 CP 还是 AP 取决于具体应用的需求例如金融系统通常需要强一致性因此会选择CP而社交媒体或某些类型的缓存系统可能会选择 AP 以提供更高的可用性
2. 什么情况下会出现Redis与数据库数据不一致
我们先来看一下使用 Redis 读取数据的场景 当客户端发起一个查询数据的请求时会先检查 Redis 中检查有没有对应的数据有的话直接返回没有的话就会查询数据库把从数据库中查询到的数据保存到 Redis 中后再返回给客户端 在将数据库中查询到的数据保存到 Redis 时一般会为数据设置一个过期时间主要目的是为了避免一些冷数据一直占用 Redis 的空间 如果只进行读操作是不会出现数据不一致的情况的只有读操作和写操作同时进行才会出现数据不一致的情况 我们再来看一下写数据的场景写数据的场景就比较多了
先更新缓存再更新数据库先删除缓存再更新数据库先更新数据库再更新缓存先更新数据库再删除缓存 总结起来就是以下两点区别
更新缓存还是删除缓存先操作缓存还是先操作数据库
3. 更新缓存还是删除缓存
我们是更新 Redis 中对应的数据还是直接删除 Redis 中对应的数据呢
推荐使用删除 Redis 中对应的数据的方式为什么呢
因为删除的逻辑非常简单删除缓存之后 Redis 中就没有对应的数据了等到下一个线程进行查询操作时会从数据库中查询数据接着将查询到的数据保存到 Redis 中
如果是更新 Redis 中的数据可能会涉及到一系列复杂的业务逻辑计算整个更新操作所需要付出的成本是比删除操作更高的
4. 先操作缓存还是先操作数据库
到底是先操作缓存好呢还是先操作数据库好呢
当出现数据不一致的时候这两种方案是怎么处理的呢我们分别探讨一下
4.1 先操作缓存
4.1.1 数据不一致的问题是如何产生的
我们先来看一下比较简单的先操作缓存的场景 当读操作和写操作并发执行的时候数据不一致的问题是如何产生的呢 首先线程 1 发起了一个修改数据的请求线程 1 删除缓存中的对应数据接着去修改数据库但是线程 1 在修改数据库时出现了网络延迟在线程 1 修改数据库前线程 2 发起了一个查询请求由于线程 1 把缓存中对应的数据删掉了线程 2 在 Redis 中找不到对应的数据线程 2 会从数据库中查询数据并将查询到的数据保存到 Redis 中
但线程 2 查询到的是一个旧数据因为线程 1 还没有将的数据保存到数据库中当线程 1 成功将新数据保存到数据库之后就出现了数据不一致的情况数据库中的是新数据Redis 中的是旧数据
线程 1 成功将新数据保存到数据库之后如果有大量的查询请求那么查到的数据都是 Redis 中的旧数据只有等到旧数据过期了才能查到数据库中的新数据
4.1.2 解决方法延迟双删
怎么解决呢其实也比较简单
我们先重演一遍出现数据不一致的过程当线程 1 成功将新数据保存到数据库之后我能不能再删除一次缓存呢当然是可以的这也就是我们平时经常听到的延迟双删
将新数据保存到数据库之后再次删除缓存如果后面又有查询请求因为 Redis 中没有对应的数据会从数据库中查询数据并将查到的数据保存到 Redis 中这样做就可以保证 Redis 和数据库的数据一致性
4.1.3 最终一致性和强一致性
但是在线程 1 成功将新数据保存到数据库之前线程 2 查询到的数据依然是旧数据会出现一次数据不一致的情况
有同学可能会说能不能把这一次数据不一致也避免掉当然可以不过要引入强一致性的概念如果要求 Redis 中的数据和数据库中的数据保持强一致性的话就需要确保操作缓存操作和操作数据库操作满足原子性
但 Redis 和数据库一般是在不同的服务器上的需要两步操作即使是在同一台服务器上也需要两步操作如果要保证同时进行两步操作的原子性就需要借助锁了
但是加锁会影响我们整个系统的吞吐量想一下我们用 Redis 的目的是什么是不是为了提高系统的性能如果为了强一致性而去加锁是不是就得不偿失了
所以说一致性跟可用性我们只能满足一个在可用性的基础上我们可以使用刚才提到的成功保存数据到数据库之后再次删除缓存中对应的数据的策略虽然会出现少量数据不一致的情况但是 Redis 和数据库是保持数据的最终一致性的
我们保证 Redis 和数据库的数据一致性一般是采用最终一致性而不是强一致性因为强一致性会影响系统的吞吐量
4.1.4 如何确定延迟双删的延迟时间
但我们得注意一点上面提到的双删策略操作数据库前删除一次缓存操作数据库后又删除一次缓存在第二次删除的时候要延迟删除 为什么要这么做呢我们重演一下发生数据不一致的过程当线程 1 删除缓存之后线程 2 发起查询请求发现 Redis 中没有对应的数据从数据库中查询数据在线程 2 将查询到的数据之前线程 1 成功将新数据保存到数据库并删除缓存在线程 1 删除缓存之后线程 2 才把查询到的数据放入到 Redis 中造成了数据不一致的情况
所以我们要延迟一定时间之后再进行删除那怎么确定延迟时间呢我们需要自行评估项目的读取数据业务的耗时即线程 2 从数据库读取数据到写入缓存的整个过程的总耗时防止线程 2 将旧数据存到 Redis 中
4.2 先操作数据库推荐使用
4.2.1 数据不一致的问题是如何产生的
我们先来看一下比较简单的先操作数据库的场景 客户端发起一个修改数据的请求先将修改保存到数据库中再删除缓存 当读操作和写操作并发执行的时候数据不一致的问题是如何产生的呢 线程 1 操作数据库将新数据保存到数据库中在线程 1 删除缓存之前线程 2 发起查询请求那么线程 2 查询到的就是旧数据等到线程 1 删除缓存之后下一个线程才能查询到新数据
先操作数据库再删除缓存能保证数据的最终一致性实现起来也比较简单所以更推荐大家先操作数据库再删除缓存
只不过先操作数据库在线程 1 删除缓存之前其它线程查询到的是脏数据但是能保证数据的最终一致性 其实先操作数据库也有可能会出现数据最终一致性被破坏的情况我们来模拟一下这个过程当线程 2 发起查询请求时缓存中对应的数据刚好过期了线程 2 从数据库中查找数据在线程 2 将数据写入缓存之前线程 1 完成了更新数据库并删除缓存的操作接着线程 2 才将数据写入缓存此时数据库中存的是新数据缓存中存的是旧数据数据最终一致性被破坏 另外在数据库做了集群的情况下先操作数据库也有可能导致数据的最终一致性被破坏的情况
在数据库集群中一般是主节点负责写操作从节点负责读操作在高并发场景下更新主库的数据并删除缓存之后如果从库没来得及同步更改后续的查询请求就会从数据库的从库中查找数据并将数据保存到缓存中但从库中的数据是旧数据从而导致数据的最终一致性被破坏
4.2.2 解决方法删除延迟删除
该怎么解决这个问题呢跟前面提到的延迟双删思想类似更新数据库并删除缓存之后再延迟删除一次缓存这样就能保证第二次删除缓存后查到的数据都是新数据
当然这个延迟时间需要自行评估项目的读取数据业务的耗时如果延迟时间过短还是会出现数据不一致的情况
但频繁删除缓存有可能会导致缓存击穿的问题也是比较严重的至于什么是缓存击穿可以参考我的另一篇博文Redis缓存面试三兄弟缓存穿透、缓存雪崩、缓存击穿 先操作缓存中提到的延迟双删的过程删除缓存→更新数据库→延迟删除缓存
此处的双删更新数据库→删除缓存→延迟删除缓存
5. 删除缓存失败的情况
无论是先操作缓存还是先操作数据库都有可能出现删除缓存失败的情况当然这种情况比较极端
如果删除缓存失败了后续线程查询到的全都是旧数据必须等待缓存中对应的数据过期了之后才能查到新数据
5.1 删除重试机制
针对这种删除失败的情况我们可以借助消息队列并采用删除重试机制比如重试 3 次如果重试 3 次后仍然失败则记录日志到数据库并发送警告给相关人员进行人工介入 高并发场景下重试最好采用异步的方式 当缓存删除失败之后发送一个异步消息到消息队列中让系统监听消息队列一旦发现 Redis 的某个 key 删除失败了就执行删除重试操作这样能在一定程度上避免删除失败所引起的数据不一致的情况 但是当我们加入了删除重试的代码之后有什么缺点呢
可以发现加入删除重试的代码之后业务代码的耦合度太高了要实现解耦的话可以采用另一个组件——canal 值得注意的是引入 canal 之后系统的复杂度也会提升毕竟 canal 是一个新的中间件需要监控 canal 的运行状态保证 canal 运行正常 5.2 canal
canal 的官网canal阿里巴巴开源的一个组件 Canal 是一个基于 MySQL 数据库增量日志解析的开源数据同步工具主要用于解决数据库间的数据同步问题特别是在大数据场景下Canal 可以帮助用户将 MySQL 数据库中的数据实时同步到其他数据存储系统中如 Elasticsearch、HBase、Kafka 等 Canal 的工作原理主要依赖于 MySQL 的主从复制机制以下是 Canal 实现数据同步的基本步骤和原理人工智能给出的回答仅供参考
MySQL 主从复制原理 MySQL 支持主从复制功能其中主库Master上的所有写操作都会记录到二进制日志Binary Log简称 binlog中从库Slave通过一个 I/O 线程连接到主库请求主库的 binlog主库将 binlog 发送给从库从库的 I/O 线程将 binlog 写入到本地的中继日志Relay Log从库的 SQL 线程读取中继日志并执行日志中的写操作从而实现数据的复制 Canal 的工作流程 模拟 SlaveCanal 模拟 MySQL 从库的行为与主库建立连接并请求主库的 binlog解析 binlog当主库有数据变更时Canal 会接收到相应的 binlog 事件。Canal 使用自己的 binlog 解析引擎来解析这些事件并将其转换为更容易理解的格式事件投递解析后的数据变更事件可以被投递到其他系统如消息队列例如 Kafka或者直接写入到目标存储系统例如 Elasticsearch 关键组件 Canal Server运行 Canal 服务负责从 MySQL 中读取 binlog解析并投递数据变更事件Canal Client负责从 Canal Server 获取数据变更事件并将其应用到目标系统 细节说明 位置记录Canal 需要记录每次同步的位置以便在服务重启后能够从上次停止的位置继续同步数据过滤Canal 支持配置过滤规则只同步特定数据库或表的数据数据格式转换Canal 可以将解析后的 binlog 事件转换为 JSON、XML 等格式
Canal 能够实现 MySQL 数据库与其他数据存储系统之间的实时数据同步广泛应用于数据备份、数据迁移、数据集成和实时数据处理等场景
5.3 引入canal后的流程
简单地来说当 MySQL 中出现了数据变动canal 能够立马感知到并通知 canal 的客户端
canal 的客户端有很多种我们可以使用 SpringBoot 应用来充当 canal 的客户端接收来自 canal 的通知一旦数据库发生了数据修改数据库的主节点会通知 canalcanal 再去通知 canal 的客户端
也就是说更新数据库后的删除缓存操作和删除重试中的删除缓存操作都交由 canal 来完成 6. 总结
如果是在并发量不高的的场景下采用删除缓存→更新数据库→延迟删除缓存方案或更新数据库→删除缓存方案都是合理的如果是在高并发的场景下无论是哪种方案即使对方案做了优化都有可能出现数据不一致的情况只不过是概率的大小问题保证 Redis 和数据库的数据一致性一般是保证数据的最终一致性而不是数据的强一致性对于读多写少的数据我们可以放在缓存中减轻数据库的压力对于读多写多的数据放入缓存中弊大于利因为放入缓存中的数据应该是对一致性要求没有那么高的数据如果数据要求强一致性就需要借助锁来确保更新数据库操作和删除缓存操作的原子性但引入锁会降低系统的吞吐量使用缓存本来就是为了提高系统的性能引入锁反而是得不偿失一致性和可用性往往不可兼得需要根据实际选择符合业务场景的方案