缓存数据一致性 1)、双写模式 2)、失效模式
缓存数据一致性-双写模式![](https://img-blog.csdnimg.cn/7b0c0703c88044d1ad996e2cd942656b.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAdG9tY2F05bOw5bOw,size_20,color_FFFFFF,t_70,g_se,x_16)
当两个线程同时去写数据库,由于卡顿等原因,导致写缓存2在最前,写缓存1在后面就出现了不一致。
这是暂时性的脏数据问题,但是在数据稳定,缓存过期以后,又能得到最新的正确数据 读到的最新数据有延迟:最终一致性。
缓存数据一致性-失效模式
一致性解决方案:
1、缓存的所有数据都有过期时间,数据过期下一次查询触发主动更新。
2、读写数据的时候,加上分布式的读写锁。 经常写,经常读。
加上分布式的读写锁代码实现:
保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁、独享锁),读锁是一个共享锁 写锁没释放读锁必须等待。 读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功。 写 + 读 :必须等待写锁释放。 写 + 写 :阻塞方式。 读 + 写 :有读锁。写也需要等待。 只要有读或者写的存都必须等待。
写数据的时候加上写锁实现代码:
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("catalogJson-lock");
//创建写锁
RLock rLock = readWriteLock.writeLock();
try {
rLock.lock();
this.baseMapper.updateById(category);
categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
rLock.unlock();
}
//同时修改缓存中的数据
//删除缓存,等待下一次主动查询进行更新
读数据的时候加上读锁实现代码:
//1、占分布式锁。去redis占坑
//(锁的粒度,越细越快:具体缓存的是某个数据,11号商品) product-11-lock
//RLock catalogJsonLock = redissonClient.getLock("catalogJson-lock");
//创建读锁
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("catalogJson-lock");
RLock rLock = readWriteLock.readLock();
Map<String, List<Catelog2Vo>> dataFromDb = null;
try {
rLock.lock();
//加锁成功...执行业务
dataFromDb = getDataFromDb();
} finally {
rLock.unlock();
}
//先去redis查询下保证当前的锁是自己的
return dataFromDb;
缓存数据一致性-解决方案
无论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例同时更新会出事。怎么办?
1、如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加 上过期时间,每隔一段时间触发读的主动更新即可
2、如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式。
3、缓存数据+过期时间也足够解决大部分业务对于缓存的要求。
4、通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心 脏数据,允许临时脏数据可忽略);
总结: 我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保 证每天拿到当前最新数据即可。 我们不应该过度设计,增加系统的复杂性 遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。