在高并发场景下,大量请求直接访问Mysql很容易造成性能问题。因此,我们将使用Redis作为数据缓存,减少对数据库的请求。但是,Mysql和Redis是两个不同的数据库。如何保证不同数据库之间数据的一致性是非常关键的。数据不一致的原因在高并发的业务场景中,大多数数据库是用户并发访问中最薄弱的环节。.所以需要使用redis做一个缓冲操作,让请求先访问redis,而不是直接访问mysql等数据库。读取缓存这一步一般没有问题,但是一旦涉及到数据更新:数据库和缓存更新,就容易出现缓存(Redis)和数据库(MySQL)的数据一致性问题。该业务场景主要解决从Redis缓存中读取数据的问题,一般按照下图的流程进行业务操作。缓存删除问题是否先写入MySQL数据库,再删除Redis缓存;java训练还是先删除缓存,再写入库,可能会出现数据不一致的情况。1.先删除缓存。如果先删除Redis缓存数据,还没来得及写入MySQL,另一个线程过来读取,发现此时缓存为空,再去Mysql数据库读取旧数据写入缓存。这时候缓存的是脏数据。然后更新数据库后,发现Redis和Mysql存在数据不一致的问题,于是删除了缓存。如果先写库,再删除缓存,不幸的是写库的线程挂了,导致缓存没有被删除。这时候会直接读取旧数据。缓存最终会导致数据不一致。因为写入和读取是并发的,顺序无法保证,缓存和数据库之间会出现数据不一致的情况。解决方案延迟双删策略1.1基本思路是在redis写入数据库前后执行.del(key)操作,并设置合理的超时时间。伪代码如下:publicvoidwrite(Stringkey,Objectdata){redis.delKey(key);db.updateData(data);Thread.sleep(500);redis.delKey(key);}1.2具体步骤1.先删除缓存2.重写数据库3.休眠500毫秒4.再次删除缓存问题:这500毫秒是怎么确定的,应该休眠多长时间?需要评估自己项目读取数据业务逻辑的耗时。这样做的目的是保证读请求结束,写请求可以删除读请求造成的缓存脏数据。当然这个策略也考虑到了redis和数据库主从同步的耗时问题。最后写数据休眠时间:在读数据业务逻辑耗时的基础上,增加几百ms。例如:睡眠1秒。1.3设置缓存过期时间是重点。理论上,为缓存设置一个过期时间是保证最终一致性的一种解决方案。所有写操作都基于数据库。只要达到缓存过期时间,如果后面有读请求,缓存就会被删除,如果没有,就会从数据库中读取新的值,然后回填缓存。1.4方案的缺点结合双删策略+缓存超时设置。最坏的情况是:在缓存过期时间内出现数据不一致,增加写请求的消耗。小时。2.异步更新缓存(基于Mysqlbinlog的同步机制)2.1总体思路1.涉及更新数据操作,使用Mysqlbinlog进行增量订阅消费2.发送消息到消息队列3.使用消息队列消费增量数据更新到Redis4.运行状态读取Redis缓存:热数据写入Redis上的Mysql:增删改查都在Mysql上更新Redis数据:Mysql数据操作都记录在binlog中,及时更新通过消息队列2.2Redis对Redis的更新流程(1)数据操作主要分为两种:一种是全量的(一次性将所有数据写入Redis),另一种是增量的(实时更新)。MySQL的更新、插入、删除变更数据。(2)读取binlog后进行分析,利用消息队列推送更新各站的redis缓存数据。这样,一旦MySQL中产生新的写入、更新、删除等操作,就可以将binlog相关的消息推送到Redis,然后根据binlog中的记录更新Redis。其实这种机制很像MySQL的主从备份。机制,因为MySQL的主备也是通过binlog实现数据一致性的。在这里,你还可以使用其他第三方:kafka、rabbitMQ等来推送更新Redis!在高并发的应用场景中,如果对数据一致性要求很高的情况下,需要定位数据和缓存不一致的原因。解决高并发场景数据一致性的方案有两种,延迟双删策略和异步更新缓存。另外,设置缓存的过期时间是保证数据一致性的关键操作,需要结合业务合理设置。文章来自Java面试那些事儿
