mysql级缓存数据库_亿级流量高并发下,缓存与数据库不一致,咋办

缓存预留模式

让我们谈谈经典的缓存+数据库读写模式,即”缓存备用模式”。

读取时,请先读取缓存。 ,读取数据库,然后取出数据并将其放入高速缓存中,并返回响应。 更新时,请先更新数据库,然后再删除高速缓存。

为什么要删除缓存而不是更新缓存? 原因很简单,很多时候,在复杂的缓存场景中操作系统,缓存不仅仅是直接从数据库获取的值。

例如,某个表的字段可能会更新,然后其对应的缓存需要查询其他两个表的数据并执行计算以计算最新的缓存。值得。

此外,更新缓存的成本有时会很高。每次修改数据库时都需要更新相应的缓存吗?

有些场景可能是这样,但对于更复杂的缓存数据计算方案却并非如此。

如果您频繁修改缓存中涉及的多个表,则缓存也将频繁更新。 但问题是,是否会经常访问此缓存?

对于栗子,与缓存相关的表的字段在1分钟内被修改了20次或100次,然后将缓存更新了20次和100次。

但是此缓存仅在1分钟内读取一次,并且有很多冷数据。 实际上,如果只删除缓存,则在1分钟内,缓存将仅被重新计算一次,开销被大大减少,并且缓存用于计算缓存。

实际上,删除缓存而不是更新缓存是一个惰性计算想法。无论是否使用,都不要每次都进行复杂的计算,而要在需要使用时重新计算。像Mybatis,Hibernate一样,他们都有懒惰的加载想法,查询部门,并且部门带来了员工列表,所以不必说每次查询部门,1000同时也发现了每个员工的数据。

在80%的情况下,只需检查该部门的信息即可检查该部门。首先检查部门,然后访问其中的员工,然后仅当要访问其中的员工时,将转到数据库以查询1000名员工。

一级缓存不一致问题和解决方案

问题: 首先修改数据库,然后删除缓存。 如果删除缓存失败,将导致数据库中的新数据和缓存中的旧数据,并且数据将不一致。

解决方案: 首先删除缓存,然后修改数据库。 如果数据库修改失败,则旧数据在数据库中,并且缓存为空,因此数据不会不一致。

因为读取时没有缓存,所以读取数据库中的旧数据,然后更新到缓存。

分析更复杂的数据不一致问题

数据已更改,请先删除缓存,然后转到修改数据库。

但是还没来得及修改,一个请求来了,读取缓存,发现缓存为空,去查询数据库,找到了旧数据修改之前,并将其放入缓存中。

随后的数据更改过程完成了数据库修改。 已经结束,数据库和缓存中的数据不同。

为什么在亿万流量和高并发的情况下,缓存中会出现此问题? 仅当同时读写一条数据时,才会出现此问题。

如果您的并发性很低,尤其是读取并发性很低,那么每天的访问次数是10,000次,那么在极少数情况下,将仅描述那种不一致的场景。

但是问题是,如果每天有数亿流量,则每秒的并发读取数为数万。只要每秒有一个数据更新请求,以上情况就可能发生数据库+高速缓存不一致。

解决方案如下: 更新数据时,根据数据的唯一标识符,将操作路由并发送到JVM内部队列。

读取数据时,如果发现数据不在高速缓存中,则将根据数据路由后重新读取数据+更新高速缓存操作唯一标识符,还发送相同的JVM在内部队列中。

队列对应一个工作线程,每个工作线程依次获取相应的操作,然后一个接一个地执行。

在这种情况下,进行数据更改操作,请先删除缓存,然后再更新数据库,但更新尚未完成。

如果读取请求结束并且读取了空的缓存,则可以先将缓存更新请求发送到队列,然后将队列积压,然后同步等待使缓存更新完成。

有一个优化点。实际上,在队列中,将多个更新缓存请求串在一起是没有意义的,因此可以对其进行过滤。

如果队列中已存在更新缓存的请求,则无需在其中放入更新请求操作,只需等待上一个更新操作请求即可去完成。

与该队列相对应的工作线程完成对上一个操作的数据库的修改后,它将执行下一个操作,即缓存更新操作。从数据库中读取最新值并将其写入高速缓存。

如果请求仍在等待时间范围内,并不断轮询并发现可以获取该值,则直接返回;否则,直接返回。 如果请求等待,如果它超过了一定的时间长度,则这次直接从数据库中读取当前的旧值。

在高并发情况下,解决方案应注意:

读取请求被长时间阻止

由于读取请求非常不同步,因此必须注意读取超时的问题,每个读取请求必须在超时时间内返回。

该解决方案的最大风险点是数据可能会频繁更新,从而导致队列中进行大量更新操作,然后将发生大量超时发生读取请求。最后,大量请求直接进入数据库。

因此,请确保通过一些实际测试以查看数据更新的频率。

另一点,因为在队列中,可能存在对多个数据项的更新操作的积压,因此您需要根据业务情况进行测试,您可能需要部署多个服务每个服务共享一些数据更新操作。

如果在内存队列中累积了100种商品的库存修改操作,则每个库存修改操作将需要10毫秒才能完成。

然后最后一个产品读取请求可能要等待10 * 100 = 1000ms = 1s才能获取数据,这将导致读取请求长时间阻塞。

因此,我们必须根据实际的业务系统操作进行一些压力测试并模拟在线环境,以查看最繁忙的时间,可能会积压内存队列多少次更新操作可能会导致与上次更新操作相对应的读取请求以及将花费多长时间。

如果读取请求在200毫秒内返回,则即使是最繁忙的时间,如果您计算得出该请求,则积压10次更新操作,最多等待200毫秒,还可以

如果内存队列中的更新操作可能过多,则必须添加计算机,以使部署在每台计算机上的服务实例处理的数据更少,然后更新的次数更少操作在每个内存队列中积压。

根据以前的项目经验,通常来说,数据写入频率非常低,因此实际上,通常来说,队列中更新的积压应该非常少。

类似于此类用于读取高并发性和读取缓存体系结构的项目,通常来说,写入请求很少,并且每个QPS最好有几百个第二。

实际上是粗略的计算,如果在一秒钟内有500个写操作,分成5个时间片,每200ms 100个写操作,则放入20个内存队列中。 5个写操作的积压。

每次写操作性能测试后,通常在20ms左右完成,那么对每个内存队列中数据的读取请求将在200ms内挂起一会儿绝对可以回来。

刚才经过简单的计算,我们知道编写数百台单机支持的QPS并没有问题。如果写入QPS扩展了10倍,则扩展机器并扩展容量。 10台计算机,每台计算机20个队列。

并发读取请求太高

在这里,您还必须进行压力测试以确保碰巧遇到在上述情况下,也有可能突然大量服务读取请求挂起,并延迟数十毫秒,具体取决于是否可以承载服务以及需要承载多少台机器才能达到最大值极限情况。

但是,由于并非所有数据都在同一时间更新,因此缓存不会在同一时间过期,因此每次可能是少量数据的缓存,然后出现那些与数据相对应的读取请求,并且并发量不应特别大。

请求路由以进行多服务实例部署

该服务可能部署了多个实例,因此必须确保实现数据更新操作和执行缓存更新操作的请求通过Nginx服务器路由到同一服务实例。

例如,对同一产品的读写请求都路由到同一台计算机上。 您可以根据服务之间的某个请求参数进行哈希路由,也可以使用Nginx的Hgin路由功能,等等。

热门产品路由问题导致请求偏斜

如果对产品的读写请求特别高,全部击中进入同一台机器的同一队列可能会对某台机器造成过大的压力。

因为仅当产品数据更新时,缓存才会被清除,然后会导致并发读写,因此根据业务系统来查看,是否更新频率不是太高这个问题的影响不是特别大,但是某些机器的负载可能会更高。

这是设计的结尾。实际上,有很多解决方案,但这只是我使用过的一种,感觉最轻巧。 如果您有兴趣,可以去找我 Github 查看重建的完整代码。

(全文结尾)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值