业务场景在高并发业务场景下,数据库在大多数情况下是用户并发访问最薄弱的环节。所以需要使用redis做一个缓冲操作,让请求先访问redis,而不是直接访问mysql等数据库。该业务场景主要解决从Redis缓存中读取数据的问题,一般按照下图的流程进行业务操作。读取缓存这一步一般没有问题,但是一旦涉及到数据更新:数据库和缓存更新,就容易出现缓存(Redis)和数据库(MySQL)的数据一致性问题。无论是先写入MySQL数据库,再删除Redis缓存,还是先删除缓存,再写入库,都可能出现数据不一致的情况。举个例子1.如果删除了Redis缓存,另一个线程在写入MySQL之前来读取,发现缓存为空,然后从数据库中读取数据写入缓存。这时候,缓存中就包含了脏数据。2.如果先写库,删除缓存前,写库的线程挂了,不删除缓存,也会出现数据不一致的情况。因为写和读是并发的,没办法保证顺序,会出现缓存和数据库数据不一致的问题。如来解决?下面介绍两种解决方案,先简单后难,结合业务和技术成本来选择使用。缓存与数据库一致性解决方案1、第一种方案:采用延迟双删策略,在写入数据库前后分别执行redis.del(key)操作,并设置合理的超时时间。伪代码如下:publicvoidwrite(Stringkey,Objectdata){redis.delKey(key);db.updateData(data);Thread.sleep(500);redis.delKey(key);}2、具体步骤为:1)先删除缓存2)再次写入数据库3)休眠500毫秒4)再次删除缓存那么,如何确定这500毫秒,应该休眠多长时间呢?需要评估自己项目读取数据业务逻辑的耗时。这样做的目的是保证读请求结束,写请求可以删除读请求造成的缓存脏数据。当然这个策略也考虑到了redis和数据库主从同步的耗时问题。最后写数据休眠时间:在读数据业务逻辑耗时的基础上,增加几百ms。例如:睡眠1秒。3、设置缓存过期时间理论上,设置缓存过期时间是保证最终一致性的一种解决方案。所有写操作都以数据库为准。只要到了缓存过期时间,后续的读请求自然会从数据库中读取新的值,然后回填缓存。4、该方案的缺点结合双删策略+缓存超时设置,所以最坏的情况是超时时间内数据不一致,增加了写入请求的耗时。二、方案二:异步更新缓存(基于binlog订阅的同步机制)1、整体技术思路:MySQLbinlog增量订阅消费+消息队列+增量数据更新到redis1)读取Redis:热点数据基本都在Redis2)写MySQL:增删改查都是操作MySQL3)更新Redis数据:MySQ数据操作binlog更新到Redis2。redis)一种是增量(实时更新)这里是增量,指的是对mysql的更新、插入、删除变更数据。2)读取binlog后进行分析,利用消息队列推送更新各站的redis缓存数据。这样,一旦MySQL中产生新的写入、更新、删除等操作,就可以将binlog相关的消息推送到Redis,Redis可以根据binlog中的记录更新Redis。这个机制其实和MySQL的主从备份机制很像,因为MySQL的主从备份机制也是通过binlog实现数据一致性的。这里可以结合使用Canal(阿里的一个开源框架),通过它可以订阅MySQL的binlog,canal只是模仿mysql从库的备份请求,这样Redis的数据更新就达到了同样的效果影响。当然你也可以使用其他第三方作为这里的消息推送工具:kafka、rabbitMQ等来推送和更新Redis。
