redis+mysql有几种用法?

Redis与MySQL结合使用,常用于数据缓存场景。
MySQL作为企业数据存储,Redis缓存经常访问的数据以提高系统响应速度。
数据一致性是关键。
读操作流程如下:读取数据时,首先从Redis获取。
如果不存在,则查询MySQL。
更新操作过程需要谨慎,以保证数据的一致性。
直接删除Redis中的对应项或者更新Redis,需要考虑并发访问的复杂度。
并发场景下,先更新MySQL,再删除Redis,可能会导致数据不一致。
因此,建议每次MySQL更新后立即清除Redis,以保证数据一致性。
高并发场景下,使用Redis进行缓存,减轻数据库压力。
读取数据时先去Redis,可以得到快速响应。
然而,数据更新必须解决缓存和数据库之间的一致性问题。
解决方案包括:延迟双删除策略:在写入数据库前后删除Redis缓存,并设置合理的超时时间,保证数据一致性。
该方法需要评估业务逻辑时间和数据库主从同步时间来确定合理的睡眠时间。
设置缓存过期时间:所有写操作都提交到数据库,过期后缓存会自动更新,保证最终一致性。
然而,这种方法可能会导致暂时的数据不一致。
异步更新缓存:MySQL通过订阅binlog将增量数据更新到Redis,实现数据实时同步。
该解决方案结合了Redis缓存和MySQL数据库来提供高可用性。
总结:Redis与MySQL结合使用时,应合理设计缓存策略和数据更新流程,以保证系统数据的一致性和高性能。
通过上述方法,可以有效解决并发访问下的数据一致性问题,提高系统整体性能。

如何保证redis与mysql数据最终一致性

有几种方案可以保证redis和mysql数据最终一致性

先更新redis再更新mysql流程图

最后mysql进行查询1,而redis查询数据2就是这样了。
,无法验证最终一致性

先更新mysql再更新redis流程图

最后mysql是查询2条数据,redis是查询1条数据。
并且无法检查最终一致性

先删除红色,然后更新mysql流程图

最后mysql是新数据,redis是旧数据,无法检查最终一致性

先更新mysql再删除redis流程图。

最后mysql是新数据,redis是旧数据

延迟删除:先更新mysql,然后休眠一段时间,再删除redis流程图

休眠时间由业务方确定,最好长于查询接口所需的时间。
这个方案存在一个问题:更新mysql之后,删除redis之前,查询会从redis获取旧数据,虽然可以检查最终一致性,但是获取旧数据的时间会很长。

选项6看起来是所有选项中最好的,但实际上仍然存在问题,比如下面的场景(发生概率很低)等待密钥过期,或者重试重新除失败为安装mq等中间件。

最后友情提醒,这个问题在面试中经常被问到,但面试时很难画图,所以很难把不同的情况解释清楚。

Redis与MySQL数据一致性问题

Redis具有高性能的数据读写功能,广泛应用于缓存场景一是可以提高业务系统的性能二是可以承受数据库的高并发流量请求Redis为何如此快揭秘。

使用Redis作为缓存组件时,需要避免以下问题,否则可能会导致生产崩溃。

Redis缓存满了怎么办?

如何解决缓存穿透、缓存碎片、缓存雪崩?

Redis数据过期后会立即删除吗?

如何排查和解决Redis突然变慢的问题?

如何处理Redis和MySQL之间的数据一致性问题?

在本文正式开始之前,我认为我们需要就以下两件事达成一致:

缓存必须有一个过期时间

确保数据库和最终一致性缓存就足够了,不需要追求强一致性。

目录如下:

什么是数据库和缓存一致性

缓存使用策略

2.1Cache-Aside(绕过缓存)

2.2Read-Through(直接读取)

2.3Write-Through立即同步编写

2.4Write-Behind

问题分析缓存绕过下的一致性

3.1先更新缓存,再更新数据库

3.2先更新数据库,然后更新缓存

3.3先删除缓存,再更新数据库

3.4先更新数据库,再删除缓存

什么是一致性解决方案?

4.1多次删除延迟缓存

4.2缓存重试机制删除

4.3读取binlog异步删除

总结

1.什么是数据库和缓存一致性?

数据一致性是指:

缓存中有数据,缓存数据值=数据库中的值

数据缓存中没有数据,数据库中的值=最新值

缓存反转与数据库不一致:

缓存数据值≠数据库中的值;

缓存或数据库中存在旧数据,导致线程读到旧数据数据。

为什么会出现数据一致性问题?

使用Redis作为缓存时,当数据发生变化时,我们需要进行两次写入,以保证缓存与数据库中的数据一致。

数据库和缓存是两个系统,如果要保证强一致性,就必须引入2PC或者Paxos等分布式一致性协议,或者分布式key等。
这很难实施并且。
肯定会影响性能。

如果对数据一致性的需求非常高,是否有必要引入缓存?2.缓存使用策略

使用缓存时,通常有以下几种缓存使用策略来提高系统性能:

Cache-AsidePattern(缓存旁路,通常用于业务统)

ReadPatternVia

Writee-ThroughPattern

Write-BehindPattern

2.1Cache-Aside

所谓“缓存旁路”是指操作读取缓存,读取数据库和更新缓存都是为了在应用系统中完成,业务系统中最常用的缓存策略。

2.1.1读取数据

读取数据的逻辑如下:

当应用程序需要从数据库读取数据时,首先检查是否存在缓存数据被命中。

如果缓存丢失,会查询数据库取回数据,并将数据写入缓存,以便后续读取相同数据会命中缓存,最后将数据返回给调用者。

如果缓存命中,则直接返回。

时序图如下:

绕过缓存读取时序图

优点

缓存只包含实际请求的数据应用程序有助于维持经济的缓存大小。

它易于实现并且可以提高性能。

实现的伪代码如下:

String?cacheKey?=?"公众号:码哥字节";String?cacheValue?=?redisCache.get(cacheKey);//缓存pressif?(cacheValue?!=?null)?{return?cacheValue;}?else?{//缓存缺失,?从数据库获取数据cacheValue?=?getDataFromDB();//?写入数据cacheredisCache.put(cacheValue)}缺点

因为data是在缓存未命中后才加载到缓存中的,所以初始调用的数据请求响应时间会增加一些开销额外的缓存填充和数据库查询时间。

2.1.2更新数据

使用“cache-aside”模式写入数据时,流程如下。

绕过缓存写入数据

将数据写入数据库;

使缓存中的数据无效或更新数据缓存;

>

使用缓存端时,最常见的写入策略是直接将数据写入数据库,但缓存可能与数据库不一致。

我们应该为缓存设置一个过期时间,这是保证最终一致性的解决方案。

如果超时太短,应用程序将继续从数据库查询数据。
同样,如果过期时间太长,并且在没有使缓存失效的情况下更新缓存,则缓存数据很可能是脏的。

最常见的方式是清除缓存,使缓存数据失效。

为什么更新缓存?

性能问题

当缓存更新成本较高,需要访问多张表进行联合计算时,建议直接删除缓存,而不是更新缓存数据,以保证一致性。

安全问题

在高并发场景下,查询到的数据可能是旧值,稍后你的代码会分析细节,所以不用担心。

2.2ReadLater(直接读取)

当缓存丢失时,数据也会从数据库中加载,写入缓存并返回给应用系统。

尽管通读和缓存未命中非常相似,但在缓存未命中中,应用程序负责从数据库检索数据并填充缓存。

Read然后将获取数据存储中的值的责任转移给缓存提供者。

Read-Through

Read-Through实现了关注点分离的原则。
代码只与缓存交互,缓存组件管理自身与数据库之间的数据同步。

2.3Write-Continue直接同步写入

与Read-Continue类似,当发生写请求时,Write-Continue将写入责任转移给缓存系统,由缓存抽象层完成缓存数据和数据库的更新数据,流程图时间如下:

Write-On

Write-On的主要好处是应用系统?无需考虑错误处理和重试逻辑,将其留给缓存抽象层来管理执行。

优缺点

直接单独使用该策略没有任何意义,因为该策略需要先写入缓存,再写入数据库,这给写入操作带来了额外的延迟。

什么时候进行直写?与通读结合使用?通读法的优点?可以在保证数据一致性的同时充分利用,而无需考虑如何使缓存设置失效。

WriteThrough

此策略反转缓存在Cache-Aside中的填充顺序,而不是在缓存未命中后延迟加载到缓存中。
而是先将数据写入缓存,然后缓存组件将数据写入数据库。

优点

缓存和数据库数据始终是最新的;

查询性能最好,因为要查询的数据可能已经写入缓存了。

缺点

很少请求的数据也会写入缓存,导致缓存更大且更昂贵。

2.4Write-Back

这张图乍一看似乎和“Write-Forward”一样,但不同之处在于最后一个箭头:它从实线变成了线。

这意味着缓存系统会异步更新数据库数据,应用系统只会与缓存系统交互。

应用程序不必等待数据库更新完成,提高了应用程序的性能,因为对数据库的更新是最慢的操作。

![Image]()

Write-Behind

这种策略下,缓存和数据库的一致性不强,对于一致性高的系统则不然受到推崇的。
3.绕过缓存下的一致性问题分析

业务场景中最常用的是Cache-Aside策略,该策略下客户端读取数据先读Cache,如果命中则返回;如果出现错误,则从数据库读取数据并写入缓存,因此读取操作不会导致缓存和数据库不一致。

最主要的是,对于一个写操作,数据库和缓存需要修改,而且两者之间会有顺序,可能会导致数据不一致。
写起来,我们需要考虑两个问题:

我们应该先更新缓存还是数据库?

当数据发生变化时,选择修改缓存(update)还是删除缓存(delete)?

结合这两个问题串行合并有四种解决方案:

先更新缓存,再更新数据库

先更新数据库,再更新缓存

先删除缓存,再更新数据库;

先更新数据库,再删除缓存。

你不需要记住下面的分析,主要是在切割过程中,你只需要考虑以下两种情况是否会导致严重的问题:

其中如果第一次操作成功,第二次失败,会出现什么问题?

高并发情况下读取数据是否不一致?

为什么不考虑第一个失败而第二个成功的情况呢?

你觉得怎么样?

既然第一个失败了,那么就不需要实现第二个了,直接在第一步返回50x等异常信息就可以了,不会出现不一致的问题。

只有第一个成功,第二个失败很头疼如果要保证它们的原子性,就涉及到分布式事务范畴。

3.1先更新缓存,再更新数据库

![图片]()

先更新缓存,再更新数据库

如果缓存是先更新成功,写入数据库失败,会导致缓存是最新数据而数据库是旧数据,那么缓存就会是脏数据。

之后,其他查询将在以下情况下获取此数据:他们立即请求,但数据库中不存在该数据。

对于数据库中不存在的数据,缓存并返回给客户端是没有意义的。

这个方案直接Pass。

3.2先更新数据库,再更新缓存

一切正常如下:

先写入数据库,成功

再次更新缓存,成功;。

缓存更新失败

至此,我们总结一下,如果这两个操作的原子性被破坏了:如果第一步成功了,第二步失败了,会出现什么问题呢?

这会导致数据库有最新的数据,而缓存有旧的数据,造成一致性问题。

这张图我就不画了,和上一张图一样,只是改变一下Redis和MySQL的位置。

高对齐场景

谢八哥经常做996,腰颈都疼,写的bug越多,他就越想去按摩,提高编程能力。

受疫情影响,豪华会所的技术人员抢着接这个订单,订单很难拿到,兄弟们。

进店后,前台将顾客信息录入系统,执行?setxx服务技师=待确定,初始值表示无人接收并保存。
到数据库和缓存,然后由技术人员安排按摩服务。

如下图:

![图片]()

高并发先更新数据库,再更新缓存

<没有。
98技术员首先行动,向系统发送命令“设置谢巴格的服务技术员=98”写入数据库,此时系统网络波动停止,数据来不及写入缓存。

接下来是技术员No.520还向系统发送“设置服务人员谢八哥=520”并将其写入数据库,并且还将该数据写入缓存。

目前,技术人员1号之前的写入缓存请求已完成。
98开始执行,数据?设置谢巴格的维修技师=98?成功写入缓存。

最后发现数据库值=?谢巴格技师集=520,缓存值=?谢巴格技师集=98。

技术人员最新数据520缓存已被1号技术员的旧数据覆盖98.

所以,在高并发场景下,如果多个线程同时写入数据,然后写入缓存,就会出现缓存使用旧值、数据库使用旧值的不一致情况最新值。

本方案可直接批准。

如果第一步失败,直接返回50x异常,不会出现数据不一致的情况。

3.3先删除缓存,再更新数据库

按照“兄弟代码”提到的套路,假设第一次操作成功,第二次操作失败,会发生什么情况?高并发场景下会发生什么?

第二步写入数据库失败

假设有两个请求:写请求A和读请求B。

写请求A第一步是刷新缓存成功,但是向数据库写入数据失败会导致写入的数据丢失,数据库会存储旧值。

然后又一个读请求B进来,发现缓存不存在。
旧数据从数据库中读取并写入缓存。

高并发下的问题

![图片]()

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

技术号198谁攻击多首先系统收到请求,删除缓存数据当系统要向数据库写入?setServiceTechnicianXiaoCaiji=98时。
有滞后,来不及写。

此时大堂经理向系统执行读取请求,查看小菜鸡是否有技术人员接收,以方便缓存中数据技术人员服务的安排,因此读取数据库中的旧数据?设置服务技术员小菜鸡=待确定,并将其写入缓存。

此时,技术员No.本来卡住的98,将数据集服务技师小菜鸡=98写入数据库,完成操作。

这种情况下,缓存的数据将是旧数据,在缓存过期之前无法读取到最新的数据。
小菜鸡最初是由2号技术员订购的。
98,但大堂经理认为没有人接。

这个方案通过,因为第一步成功但是第二步失败,会导致数据库有旧数据,缓存中没有数据,旧值会被数据库读取并写入到缓存中,导致数据不一致。

无论是异常情况还是高一致性场景,都会出现数据不一致的情况。
错过

3.4先更新数据库,再删除缓存

经过前面三种解决方案,都通过了。
我们来分析一下最终的方案是否可行。

按照“套路”来判断异常和高并发会带来的问题。

这个策略可以知道,如果数据库写阶段失败,会直接返回客户端异常,不需要进行缓存操作。

所以如果第一步失败,不会出现数据不一致的情况。

缓存删除失败

主要是第一步要成功将最新数据写入数据库缓存删除失败怎么办?

你可以将这两个操作放在一个事务中,当缓存刷新失败时,回滚对数据库的写入。

不适合高并发场景,容易出现大事务,导致死锁问题。

如果不重启,就会出现数据库无论是新数据、缓存数据还是旧数据,数据不一致怎么办?

所以,我们需要想办法成功删除缓存,否则就只能等到有效期到期了。

使用重试机制。

比如重试3次,3次失败,日志就会记录到数据库,xxl-job分布式调度组件等。
将用于进一步处理。

在高并发场景下,最好使用异步的方式进行重试,比如向mq中间件发送消息,实现异步解耦。

或者可以使用Canal框架订阅MySQLbinlog日志,监听相应的更新请求并执行相应的删除缓存操作。

高并发场景

我们来分析一下高并发读写的问题...

![Image]()

先写入数据库再写入删除缓存

技术员号98率先行动,接管了小菜鸡的业务运营。

Candy的主管向系统执行读取请求,并检查小彩姬是否有技术人员将其信息接收到客户端,主管认为没有人收到。

技术员编号98最初接受订单但未能刷新缓存由于滞后现在删除成功了。

一次读取请求可能会读取少量旧数据,但旧数据很快就会被删除,下一次请求就可以获取最新数据,所以没什么大不了的。

还有一种比较极端的情况,当缓存自动过期时,会遇到高并发读写的情况,B执行更新操作,那么会出现以下情况:

![图片]()

缓存意外过期

缓存超时,缓存失效。

读请求线程A读取缓存,如果未命中,则到数据库查询旧值(因为B会写入新值,也就是旧值),准备向数据库写入数据缓存发送时出现网络问题。

线程B执行写操作,将新值写入数据库。

线程B执行缓存刷新。

线程A继续,从滞后中唤醒,并将旧的查询值写入缓存。

马哥,你怎么样,还是有些不一致。

不要惊慌,这种情况发生的可能性很小。
发生上述情况的必要条件是:

步骤(3)中对数据库的写操作比步骤(2)中的读操作更短且更快。
步骤(4)之前是步骤(5)。

缓存刚刚到期。

一般MySQL单机的QPS在5K左右,TPS在1k左右(ps:Tomcat的QPS在4K左右,TPS=1k左右)。

数据库读操作比写操作快得多(正是因为这些读和写是分开的),所以步骤(3)很难比步骤(2)快。
同时,还需要配合缓存过期。

所以,在使用绕过缓存策略时,建议对于写操作:先更新数据库,再删除缓存。
4.什么是一致性解决方案?

最后,对于Cache-Aside(缓存旁路)策略,当写操作采用先更新数据库再删除缓存的方式时,我们来分析一下有哪些数据一致性解决方案?

4.1双删除延迟缓存

先删除缓存再更新数据库如何避免脏数据?

使用延迟多重删除策略。

先删除缓存。

编写数据库。

休眠500毫秒,然后清除缓存。

这样最多只会发生500毫秒的脏数据读取时间。
关键是这个睡眠时间怎么确定?

延迟时间的目的是保证读请求完成,写请求可以删除读请求造成的缓存脏数据。

所以,我们需要自行评估读取项目数据业务逻辑的时间,根据读取的时间加上几百毫秒的延迟时间即可。

4.2缓存删除重试机制

缓存删除失败怎么办?例如,如果延迟多次删除的第二次删除失败,则无法删除脏数据。

使用重试机制保证缓存删除成功。

例如,重试3次,3次失败,就会向数据库记录日志,并发出警报,需要人工干预。

在高并发场景下,最好使用异步的方式进行重试,比如向mq中间件发送消息,实现异步解耦。

尝试机制re

步骤(5)如果删除失败且未达到最大重试次数,则再次取消该消息,直到删除成功为止,否则将记录在数据库中并进行人工干预必需的。

这种方案有一个缺点,就是会导致对业务代码的侵入,所以接下来的方案就是启动一个专门订阅数据库binlog的服务来读取需要删除的数据并执行缓存删除操作。

4.3读取binlog异步删除

binlog异步删除

更新数据库;

数据库会在binlog日志中记录操作信息;

使用通道订阅binlog日志获取目标数据和key;

缓存删除系统获取通道数据,解析目标key并尝试删除缓存。

如果删除失败,则将消息发送到消息队列;

缓存删除系统从消息队列中取出数据,再次执行删除操作。

总结

缓存策略的最佳实践是CacheAsidePattern。
它们分为读缓存最佳实践和写缓存最佳实践。

读取缓存的最佳实践:先读取缓存,未命中就返回数据库,然后再写入数据库。

写缓存的最佳实践:

先写入数据库,再操作缓存;

直接删除缓存,不要修改缓存,因为更新的成本缓存很高,需要访问多个表进行联合计算建议直接删除缓存而不是更新而且删除缓存很容易,副作用只是增加了chachemiss。
建议您使用此策略。

在上述最佳实践下,为了尽可能保证缓存和数据库的一致性,我们可以使用延迟多次删除。

为了免删除失败,我们使用异步重试机制来保证正确删除。
通过异步机制,我们可以将删除消息发送到mq消息中间件,或者使用管道订阅MySQLbinlog日志。
监听写请求并刷新相应的缓存。

那如果我需要保证绝对一致性呢?首先我总结一下:

没有办法做到绝对一致性,这是CAP理论决定的,系统可以用于以下场景:弱一致性,所以属于CAP中的AP。

所以,我们必须妥协,但要达到BASE理论所说的最终一致性。

事实上,一旦在解决方案中使用了缓存,往往意味着我们放弃强数据一致性,但同时也意味着我们的系统可以获得性能上的小幅提升。

这正是所谓的权衡。

原文:https://juejin.cn/post/7103701796429758478