第50篇 Redis与DB库(连续化存储)之间的数据双写同等性保证 ...

打印 上一主题 下一主题

主题 968|帖子 968|积分 2904

前言

Redis作为一款高效的内存数据存储系统,被广泛应用在业务系统中的缓存层,尤其是在与关系型数据库(如MySQL、PostgreSQL等)结合利用时,通过将热点数据存储在Redis中,可以在很大程度上缓解数据库的压力,提高团体系统的性能体现。
很紧张的题目是:如何确保Redis缓存与数据库之间的双写同等性。双写同等性,是指当数据在数据库中发生变动时,可以大概及时且准确地反映在Redis缓存中。由于网路延迟,或者并发控制,往往数据的同等性受到挑衅。
出现双写同等性题目,会造成数据丢失、脏读、重复读等题目。
可以通过以下的方法和策略办理:

1.分布式缓存中的双写同等性

分布式系统中的同等性指的是在多个节点上存储和处理数据时,确保系统中的数据在不同节点之间保持同等的特性。在分布式系统中,同等性通常可以分为以下几个类别:
1.1 强同等性

全部节点在任何时间都看到雷同的数据。任何更新操作都会立即对全部节点可见,保证了数据的强同等性。
造成影响:增长通讯开销和降低系统的可用性。
1.2 弱同等性

弱同等性下的系统允许在一段时间内,不同节点之间看到不同的数据状态。弱同等性通常用于必要在性能和同等性之间进行权衡的场景,比方缓存系统等。
1.2 最终同等性

是弱同等性的一种特例,它保证了在经过一段时间后,系统中的全部节点最终都会达到同等状态。
最终同等性通常通过一些技术手段来实现,比方基于版本向量或时间戳的数据复制和同步机制。
2.典型场景分析

2.1 写数据库后忘记更新缓存:

当直接对数据库进行更新操作而没有相应地更新缓存时,后续的读请求可能仍然从缓存中获取旧数据,导致数据的不同等。
2.2 删除缓存后数据库更新失败:

在某些场景下,为了保证数据新鲜度,会在更新数据库前先删除缓存。但如果数据库更新过程中出现非常导致更新失败,那么缓存将长时间处于空缺状态,新的查询将会直接命中数据库,加重数据库压力,并可能导致数据版本混乱。
2.3 并发环境下读写操作的交织实行:

在高并发场景下,可能存在多个读写请求同时操作同一份数据的情况。好比,在删除缓存、写入数据库的过程中,新的读请求获取到了旧的数据库数据并放入缓存,此时就出现了数据不同等的征象。
2.4 主从复制延迟与缓存失效时间窗口冲突:

对于具备主从复制功能的数据库集群,主库更新数据后,存在肯定的延迟才将数据同步到从库。如果在此期间缓存刚好过期并重新从数据库加载数据,可能会从尚未完成同步的从库读取到旧数据,进而导致缓存与主库数据的不同等。
3.办理双写同等性题目的紧张策略

3.1 Cache Aside Pattern(旁路缓存模式)

是一种在分布式系统中广泛采用的缓存和数据库协同工作策略,在这个模式中,数据以数据库为主存储,缓存作为提升读取服从的辅助手段。也是日常中比力常见的一种手段。其工作流程如下:


  • 读取操作:起首实验从缓存中获取数据,如果缓存命中,则直接返回;否则,从数据库中读取数据并将其放入缓存,最后返回给客户端。
  • 更新操作:当必要更新数据时,起首更新数据库,然后再清除或使缓存中的对应数据失效。如许一来,后续的读请求将无法从缓存获取数据,从而迫使系统从数据库加载最新的数据并重新填充缓存。
题目1:为什么操作缓存的时候是删除旧缓存而不是直接更新缓存?
我们举例模拟下并发环境下的更新DB&缓存:

  • 线程A先发起一个写操作,第一步先更新数据库,然后更新缓存
  • 线程B再发起一个写操作,第二步更新了数据库,然后更新缓存
    当以上两个线程的实行,如果严格先后顺序实行,那么对于更新缓存还是删除缓存去操作缓存都可以,但是如果两个线程同时实行时,由于网络或者其他原因,导致线程B先实行完更新缓存,然后线程A才会更新缓存。如下图:

这时候缓存中生存的就是线程A的数据,而数据库中生存的是线程B的数据。这时候如果读取到的缓存就是脏数据。但是如果利用删除缓存取代更新缓存,那么就不会出现这个脏数据。这种方式可以简化并发控制、保证数据同等性、降低操作复杂度,并能更好地适应各种潜在的非常场景和缓存策略。尽管这种方法可能会增长一次数据库访问的成本,但在实际应用中,思量到数据的同等性和系统的健壮性,这是值得付出的折衷。
并且在写多读少的情况下,数据许多时候并不会被读取到,但是一直被频仍的更新,如许也会浪费性能。实际上,写多的场景,用缓存也不是很划算。只有在读多写少的情况下利用缓存才会发挥更大的价值。
题目2:为什么是先操作数据库再操作缓存?
在操作缓存时,为什么要先操作数据库而不是先操作缓存?我们同样举例模拟两个线程,线程A写入数据,先删除缓存在更新DB,线程B读取数据。流程如下:

  • 线程A发起一个写操作,第一步删除缓存
  • 此时线程B发起一个读操作,缓存中没有,则继承读DB,读出来一个老数据
  • 然后线程B把老数据放入缓存中
  • 线程A更新DB数据

所以如许就会出现缓存中存储的是旧数据,而数据库中存储的是新数据,如许就出现脏数据,所以我们一般都接纳先操作数据库,在操作缓存。如许后续的读请求从数据库获取最新数据并重新填充缓存。如许的设计降低了数据不同等的风险,提升了系统的可靠性。同时,这也符合CAP定理中对于同等性(Consistency)和可用性(Availability)权衡的要求,在许多场景下,数据同等性被优先思量。
Cache Aside Pattern相对简单直观,容易理解和实现。只必要简单的判定和缓存失效逻辑即可,对已有系统的改动较小。并且由于缓存是按需加载的,所以不会浪费宝贵的缓存空间存储未被访问的数据,同时我们可以根据实际情况决定何时加载和整理缓存。
尽管Cache Aside Pattern在大多数情况下可以保证最终同等性,但它并不能保证强同等性。在数据库更新后的短暂时间内(还未开始操作缓存),如果有读请求发生,缓存中仍是旧数据,但是实际数据库中已是最新数据,造成短暂的数据不同等。在并发环境下,特别是在更新操作时,有可能在更新数据库和删除缓存之间的时间窗口内,新的读请求加载了旧数据到缓存,导致不同等。
3.2 Read-Through/Write-Through(读写穿透)

它们紧张用于缓存系统与长期化存储之间的数据交互,旨在确保缓存与底层数据存储的同等性。
3.2.1 Read-Through(读穿透)

Read-Through 是一种在缓存中找不到数据时,自动从长期化存储中加载数据并回填到缓存中的策略。具体实行流程如下:

  • 客户端发起读请求到缓存系统。
  • 缓存系统检查是否存在请求的数据。
  • 如果数据不在缓存中,缓存系统会透明地向底层数据存储(如数据库)发起读请求。
  • 数据库返回数据后,缓存系统将数据存储到缓存中,并将数据返回给客户端。
  • 下次同样的读请求就可以直接从缓存中获取数据,提高了读取服从。

    团体简要流程类似Cache Aside Pattern,但在缓存未命中的情况下,Read-Through 策略会自动隐式地从数据库加载数据并填充到缓存中,而无需应用程序显式地进行数据库查询。
3.2.2 Write-Through(写穿透)

Write-Through 是一种在缓存中更新数据时,同时将更新操作同步到长期化存储的策略。具体流程如下:

  • 当客户端向缓存系统发出写请求时,缓存系统起首更新缓存中的数据。
  • 同时,缓存系统还会把这次更新操作同步到底层数据存储(如数据库)。
  • 当数据在数据库中乐成更新后,整个写操作才算完成。
  • 如许,无论是从缓存还是直接从数据库读取,都能得到最新同等的数据。

Read-Through 和 Write-Through 的共同目标是确保缓存与底层数据存储之间的同等性,并通过自动化的方式隐蔽了缓存与长期化存储之间的交互细节,简化了客户端的处理逻辑。这两种策略经常一起利用,以提供无缝且同等的数据访问体验,特别适用于那些对数据同等性要求较高的应用场景。然而,必要注意的是,虽然它们有助于提高数据同等性,但在高并发或网络不稳定的情况下,仍然必要思量并发控制和事务处理等题目,以防止数据不同等的情况发生。
3.2.3 Write behind (异步缓存写入)

Write Behind(异步缓存写入),也称为 Write Back(回写)或 异步更新策略,是一种在处理缓存与长期化存储(如数据库)之间数据同步时的策略。在这种模式下,当数据在缓存中被更新时,并非立即同步更新到数据库,而是将更新操作暂存起来,随后以异步的方式批量地将缓存中的更改写入长期化存储。其流程如下:

  • 应用程序起首在缓存中实行数据更新操作,而不是直接更新数据库。
  • 缓存系统会将此次更新操作记录下来,暂存于一个队列(如日志文件或内存队列)中,而不是立刻同步到数据库。
  • 在配景有一个独立的进程或线程定期(或者当队列积累到肯定大小时)从暂存队列中取出更新操作,然后批量地将这些更改写入数据库。

利用 Write Behind 策略时,由于更新并非即时同步到数据库,所以在异步处理完成之前,如果缓存或系统出现故障,可能会丢失部分更新操作。并且对于高度敏感且要求强同等性的数据,Write Behind 策略并不适用,因为它无法提供严格的事务性和及时同等性保证。Write Behind 适用于那些可以容忍肯定延迟的数据同等性场景,通过牺牲肯定程度的同等性调换更高的系统性能和扩展性。
4.办理双写同等性题目的3种方案

4.1 延时双删策略

延时双删策略紧张用于办理在高并发场景下,由于网络延迟、并发控制等原因造成的数据库与缓存数据不同等的题目。
当更新数据库时,起首删除对应的缓存项,以确保后续的读请求会从数据库加载最新数据。
但是由于网络延迟或其他不确定性因素,删除缓存与数据库更新之间可能存在时间窗口,导致在这段时间内的读请求从数据库读取数据后写回缓存,新写入的缓存数据可能还未反映出数据库的最新变动。
所以为相识决这个题目,延时双删策略在第一次删除缓存后,设定一段短暂的延迟时间,如几百毫秒,然后在这段延迟时间结束后再次实验删除缓存。如许做的目的是确保在数据库更新传播到全部节点,并且在缓存中的旧数据彻底过期失效之前,第二次删除操作可以消除缓存中可能存在的旧数据,从而提高数据同等性。

4.2 删除缓存重试机制

删除缓存重试机制是在删除缓存操作失败时,设定一个重试策略,确保缓存最终能被精确删除,以维持与数据库的同等性。
在实行数据库更新操作后,实验删除关联的缓存项。如果首次删除缓存失败(比方网络波动、缓存服务暂时不可用等情况),系统进入重试逻辑,按照预先设定的策略(如指数退避、固定隔断重试等)进行多次实验。直到缓存删除乐成,或者达到最大重试次数为止。通过这种方式,纵然在非常情况下也能只管保证缓存与数据库的同等性。

这种重试方式确保缓存删除操作的乐成实行,可以应对网络抖动等导致的临时性错误,提高数据同等性。但是可能占用额外的系统资源和时间,重试次数过多可能会壅闭其他操作。
监听并读取biglog异步删除缓存
在数据库发生写操作时,将变动记录在binlog或类似的事务日志中,然后利用一个专门的异步服务或者监听器订阅binlog的变化(好比Canal),一旦检测到有数据更新,便根据binlog中的操作信息定位到受影响的缓存项。讲这些必要更新缓存的数据发送到消息队列,消耗者处理消息队列中的事件,异步地删除或更新缓存中的对应数据,确保缓存与数据库保持同等。

这种方法的好处是将缓存的更新操作与主业务流程解耦,制止壅闭主线程,同时还能处理数据库更新后由于网络题目或并发题目导致的缓存更新滞后情况。当然,实现这一策略相对复杂,必要对数据库的binlog机制有深入理解和定制开发。
总结

在分布式系统中,为了保证缓存与数据库双写同等性,可以采用以下方案:
读取操作:

  • 先实验从缓存读取数据,若缓存命中,则直接返回缓存中的数据。
  • 若缓存未命中,则从数据库读取数据,并将数据放入缓存。
更新操作:

  • 在更新数据时,起首在数据库进行写入操作,确保主数据库数据的即时更新。
  • 为了淘汰数据不同等窗口,采用异步方式处理缓存更新,具体做法是监听数据库的binlog事件,异步进行删除缓存。
  • 在一主多从的场景下,为了确保数据同等性,必要等待全部从库的binlog事件都被处理后才删除缓存(确保全部从库均已更新)。
同时,还需注意以下要点:

  • 对于高并发环境,可能必要结合分布式锁、消息队列或缓存失效延时等技术,进一步确保并发写操作下的数据同等性。
  • 异步处理binlog时,务必思量非常处理机制和重试策略,确保binlog事件可以大概精确处理并实行缓存更新操作。
本文转载自:https://www.coderacademy.online/article/redismysql.html

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曂沅仴駦

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表