1.是什么
数据一致性呢,讲的就是缓存中的数据和db中的数据是否能一致。
2.为什么
先看我这篇文章,了解下缓存策略:Redis-6-三种缓存读写策略
在文章中,介绍了并发场景下的一致性问题,我们已经确定了一个基本思路:
先更新db,再删除缓存。
这个方案,奠定了我们在读写操作时的基本思路。
2.1 主从复制延迟
在我们Mysql读写分离+主从复制延迟的场景下,缓存重建用了一个错误的值。
我们主库更新完数据并删除缓存后,缓存确实不存在了。
但是,db的从库读数据有稍许延迟,在这个时间段我们读取,缓存没数据,确实触发了缓存重建。
但是由于读取的从库还没有同步到主库更新过来的数据,导致我们重建的缓存值还是从库中的旧数据,仍然会有问题。
2.2 缓存服务宕机
假设我们的缓存服务宕机了,无法删除缓存。
这个直接就跟没删除缓存的操作一样,缓存的完全是个错误的旧数据。
3.解决
3.1 延时双删
针对主从同步未完成的情况,我们简单的方案就是,等一小会再删除。
来一个demo。
我们RedisService中存放操作缓存的方法,没啥特殊的,就是正常的操作缓存。
@Service
public class RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}
public void set(String key, String value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
public void delete(String key) {
redisTemplate.delete(key);
}
}
DataService呢,记录我们完整的操作步骤。
嗯,你可以看到,实际上,我们在首次删除后,小小休息了下,又触发了次 redisService.delete(key);
@Service
public class DataService {
@Autowired
private RedisService redisService;
public void updateData(String key, String value) {
updateDatabase(key, value);
// 首次删除
redisService.delete(key);
// 延迟双删
deleteCacheWithDelay(key);
}
// 模拟数据库操作
public void updateDatabase(String key, String value) {
}
@Async
public void deleteCacheWithDelay(String key) {
try {
TimeUnit.SECONDS.sleep(5); // 延迟5秒
redisService.delete(key);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
问题是,这个延迟删除缓存,延迟时间到底设置要多久?
- 延迟时间要大于主从复制的延迟时间。确保删除后,新的读请求重建出来的是正确的数据值。
- 延迟时间要大于一次完整的缓存重建时间。即大于一个读请求查询db再更新缓存的时间,这样才能确保哪怕真有某个线程重建了个错误的,咱们还是能给它删除掉。
好了,你已经懂延时双删的概念了,现在,请你给出删除时间。
哈哈,懵了吧,我咋知道?嗯,问题就是,我咋知道?
实际情况下,这个时间是很难把控的,咱们就是完全凭借经验来设置个值,尽可能的去保证,极端情况下呢,它还是会有问题。
3.2 消息队列
这种方式,核心上还是一个再次删除的方案。
嗯,不就是延迟删除下缓存嘛,我们当然可以新建个延时消息。
mq消费端,收到消息后,删除对应的缓存。
这就是延时双删的升级版,通过咱们mq的机制,确保肯定能删除掉(消费成功),这样就算缓存宕机or代码报错了也能ok。
那么,还是会存在一个问题,我咋知道时间具体设置多久啊?
还是那句话,很难把控。
3.3 分布式事务
如果要做到强一致性,常见的解决方案就是分布式事务了:吃透此文,分布式事务会被你玩的出神入化
但是,分布式事务的引入,必然又涉及到复杂性的增加,也带来了性能的损失。
那我们引入缓存的初心是什么,不是为了性能吗,所以,这其实是个难以平衡的点。
需要你结合自己的实际场景,权衡使用。
标签:11,缓存,String,删除,Redis,key,Mysql,public,延迟 From: https://www.cnblogs.com/yang37/p/18239864