一 什么是双写一致性?
当修改了数据库(MySQL)中的数据,也要同时更新缓存(redis)中的数据,缓存中的数据要和数据库中的数据保持一致
双写一致性,根据业务对时间上的要求,可以分为三种情况:
1、延时双删:较为准时的一致性,Redis中的数据和MySQL较为准时的一致,不会超过很长的时间
2、redissson锁:保证强一致性;准时
3、异步写入redis:如果业务允许短暂时间内redis与MySQL数据库中数据的不一致,但是保持最终一致的情况;
二 延时双删
2.1 延时双删的背景
背景:当数据库中的数据出现了变化,我是应该先删除缓存中的数据,还是先修改数据库中的数据,再删除缓存中的数据呢?
其实不管是先删除redis中的数据,还是先update数据库中的数据,都会导致数据不一致的问题
当线程1删除了缓存,尚未更新数据库时,线程2进来了,查询redis中,没有这条数据,未命中,那么线程2会去查询数据库,并将结果写入缓存中;这时候线程1才会更新数据库的数据;
如图中的redis缓存中,还是原来的数据10,数据库以及改成了20,且由于redis中有数据,所有后续的线程不会再去查询数据库更新数据,这就导致了脏数据的产生
第二种情况:先操作MySQL数据库,再更新Redis
如果线程1去查询redis缓存的时候,Redis中的该条数据以及过期了,然后去查询数据库,尚未写入缓存时,线程2更新了数据库,再删除缓存;删除了缓存之后,线程1此时再获得CPU资源,去Redis中写入数据(这时候线程1拿着的还是老数据),也会导致Redis中和MySQL中的数据不一致
虽然这两种情况很少出现,但是一旦出现,就有可能对业务造成重大损失,是不可容忍的!
2.2 什么叫延时双删
就是上述背景两个方式的集合,先删除Redis中的数据,在更新MySQL数据库中的数据,最后再次删除Redis中的数据;至于延时
为什么要延时呢?
现在大部分业务的数据库,都是主从集群的数据库;修改数据库之后,如果立马删除Redis中的数据,主库的数据尚未同步到从库,而后续有其他线程从从库中查询到尚未同步过来的数据写入redis,还是会导致脏数据的风险,所以要延时(可以用定时器,或延时队列等)再删除一次redis中的数据
注:由于你是无法绝对确认什么时候数据库进行主从同步的,所以哪怕你延时了,还是有可能在数据库同步之前删掉Redis,然后其他线程获取脏数据导致不一致的情况的!所以延时双删,无法保证强一致性
三 redisson锁
如果业务必须要求,保持Redis和MySQL数据库中的数据,实时的强一致性,那么我们可以使用分布式锁,和redisson提供的读写锁来保证数据的同步
3.1分布式锁(互斥锁)
如图所示,给资源加上一个互斥锁:当线程1要更新MySQL数据库和删除Redis中的数据前,加互斥锁,这样其他线程无法获取Redis中的数据,只能等线程1写入MySQL,并删除缓存完成释放锁后,才能读取数据;
但是很明显,这样的互斥锁虽然保证了强一致性,但是性能很低,充斥大量的获取锁和释放锁的额外开销
3.2 共享锁和排他锁
由于我们的现实情况中,对Redis中的数据,肯定是读多写少的;所以没必要使用互斥锁
这时我们可以使用redisson提供的共享锁和排他锁
当某一个线程要更新MySQL和删除Redis中的数据时,就必须先获得排他锁(其他线程无法读)这样删除完成后,其他线程才能获取共享锁以读取数据
代码实现参考如下:
使用共享锁的优缺点:
优点:强一致性
缺点:还是性能低,只比互斥锁强
四 异步写入redis
当业务不要求redis中的数据,立马从MySQL中同步出来时,我们就可以使用异步的方式来实现双写一致性了,这样既没有性能问题,也保证了双写一致性
4.1异步通知
当更新了MySQL中的数据,需要写入redis时,可以发送一个异步消息,放到MQ中,由专门的消费者去写入redis中
4.2 canal
canal是阿里巴巴出的一种中间件,基于MySQL的主从同步来实现的:
当有数据写入数据库,数据库进行主从同步时,会把所有ddl和dml的语句记录到一个binlog文件中;
而canal的作用就是伪装成一个MySQL的从节点,去监听这个binlog日志,把MySQL中我们监听的数据的变化,异步通知给缓存服务,进行写入redis中
canal的优点是:对业务代码几乎无侵入,前文中的方式多多少少对代码都有侵入,而且速度很快
写在后面:
其实,我们公司的真实方式,并不是写入MySQL后,删除redis的数据,再由下一个线程查询MySQL写入redis;
而是,直接写入MySQL后,再立马直接更新写入redis中,按我的理解,这样的方式很直接,很有效,既保证了时效性,也保证了强一致性,并没有什么缺点;
如果有大佬看到这篇文章,烦请可以给我解释一下,我们公司的这样操作的缺点是什么,感激不尽