在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节。所以,可以使用redis做一个缓冲操作,让请求先访问redis,而不是直接访问数据库。。(图片来自网络)

redis HashOperations put 重复 redis高并发下数据重复_数据库

这个业务场景,主要是解决从redis缓存中读数据的,一般是按照下图来进行业务操作。

redis HashOperations put 重复 redis高并发下数据重复_redis_02

 

对缓存读取一般没有什么问题,但是涉及到数据的更新,如何做到缓存同步,来确保redis缓存和数据库间的数据的一致性呢?

无论是先写MySQL数据库,再删除redis缓存;还是先删除缓存,再写数据库,都可能出现数据不一致性的情况。如:

1. 假如先删除redis中的缓存,还没来得及写库,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。

2. 先写MySQL数据库,再删除redis缓存前,写库的线程宕机了,没有删除redis缓存,则也会出现数据不一致情况。

因为写和读是并发的,没法保证顺序,就会出现缓存和数据库的数据不一致的问题。如何解决?还需要结合业务场景和技术代价选择使用。

方案一:延时双删策略

1. 在写库前后都进行redis.del(key)操作,并且设置合理的超时时间

     先删除缓存数据,再写入数据库,然后让线程休眠适当的时间,再次删除缓存。需要我们根据自己的项目的读数据业务逻辑的耗时情况,及redis和数据库主从同步的耗时,来确定休眠时间。如此能确保读请求结束,写请求可以删除读请求造成的缓存脏数据。

2. 设置缓存过期时间

从理论上讲,给缓存设置过期时间是保证最终一致性的解决方案。所有的写操作以数据库为准,只要达到缓存过期时间,则后面的读请求自然会从数据库中读取数据并存入缓存中。

该方案存在的弊端

 双删策略+缓存超时设置,这样最差的情况是在超时时间内数据存在不一致性,而且又增加了写请求的耗时。

方案二:异步更新缓存

数据直接写入数据库中,数据库更新binlog日志,利用Canal(阿里的一款开源框架)中间件读取binlog日志,应用监控MQ通道,将MQ的数据更新到redis缓存中。

redis HashOperations put 重复 redis高并发下数据重复_redis_03

 

这样一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至redis,redis再根据binlog中的记录,对redis进行更新。

以上便是对高并发下redis缓存和MySQL中的数据做到一致性的理解,如有不足,欢迎留言指正。。。