当前位置: 首页 > 科技观察

村长们都能看懂的延时双删攻略

时间:2023-03-21 23:18:19 科技观察

大家好,我说的是北军。今天给大家介绍一下MySQL和Redis的延迟双删策略。背景在目前的环境下,我们通常更喜欢使用redis缓存来减轻我们访问数据库的压力。但是也会遇到下面的情况:当大量用户访问我们的系统时,会先查询缓存,如果缓存中没有数据,再查询数据库,然后更新数据到缓存中,然后如果数据库中的数据发生变化,需要同步到redis。在同步过程中,需要保证MySQL和redis之间的数据一致性。这个同步过程中出现短暂的数据延迟是正常的,但是最终还是要保证mysql和缓存的一致性。//我们通常使用redis逻辑//通常我们先查询reidsStringvalue=RedisUtils.get(key);如果(!StringUtils.isEmpty(value)){返回值;}//从数据库中获取数据value=getValueForDb(key);if(!StringUtils.isEmpty(value)){RedisUtils.set(key,value);返回值;}1.什么是延迟双删?延迟双删策略是分布式系统中数据库存储和缓存数据一致性维护的常用策略,但不是强一致性。事实上,无论采用哪种方案,Redis的脏数据问题都无法避免,只能得到缓解。要彻底解决,就需要使用同步锁和相应的业务逻辑层来解决。2、为什么延迟双删?一般我们在更新数据库数据的时候,需要同步缓存在redis中的数据,所以我们一般会给出两种解决方案:第一种解决方案:先进行更新操作,再进行缓存清空。方案二:先进行缓存清空,再进行更新操作。但是这两种方案在并发请求中容易出现以下问题。第一种方案有缺点:请求1执行完数据库更新操作后,缓存还未清空,请求2进来查询缓存。这时候缓存中的数据还是旧数据,没有机会删除,造成数据问题。但是t1执行完缓存删除操作后,后续请求无法查询缓存,再查询数据,再更新到缓存中。这个影响比较小t1线程先更新db;t2线程查询命中缓存并返回旧数据;假设t1线程更新了db,估计5毫秒内会删除缓存key,5毫秒内其他线程查询缓存的结果还是旧数据,但是5毫秒后,查询缓存的结果为空,重新同步最新的db结果到redis。一个项目延期是很正常的,所以这种情况延期对业务的影响其实是很小的。但是如果发生删除缓存失败怎么办?1.不断重试----如果是在http协议界面,界面响应会变慢。调用该接口会导致响应超时。2.或者使用mq异步形式同步第二种方案。缺点:执行请求1时,缓存被清空最后在数据更新操作还没有执行的时候,请求2进来查询数据库的旧数据,写入redis,导致数据库不一致和redis数据。t1线程先删除缓存;t2线程读取缓存为null,同步db数据到缓存;t1线程更新数据库中的数据;t3线程查询缓存中的旧数据;需要使用延迟双删策略,先清缓存,再更新,最后(延迟N秒)清缓存。执行两次删除,中间需要有一个延时RedisUtils.del(key);//先删除缓存updateDB(user);//更新db中的数据Thread.sleep(N);//延时为一段时间,在删除缓存key之前RedisUtils.del(key);//先删除缓存4.注意事项上面(延迟N秒)的时间大于一次写操作的时间。原因:如果延迟时间小于写入redis的时间,会导致请求1清空缓存,而请求2缓存还没有写入的尴尬。..5、延迟时间如何确定?业务程序运行时统计业务逻辑执行读数据和写缓存的运行时间,并据此估算。因为这种方案会在第一次删除后延迟一段时间后删除缓存值,所以称为“延迟双删”。总结延迟双删策略只是一种同步数据库和缓存的手段。当系统并发度不高时可以使用该方法。如果并发度高,我们还可以寻找其他解决方案如:【数据库和缓存一致性方案canal】