双写一致性:
当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致。

- 读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,返回数据。
- 写操作:延迟双删。
删除缓存 ----> 修改数据库 --
延时
--> 删除缓存
① 为什么要删除两次缓存? 为了减少脏数据的出现。
② 为什么要延时双删? 一般数据库为主从模式,是读写分离的,需要延时一会等数据从主节点同步到从节点。
③ 延时双删有什么问题?
- 延时时间不太好确定。
- 延时的过程中可能会出现脏数据。
使用异步方案同步数据(最终一致性)
- 使用MQ中间件,更新数据之后,通知缓存删除。
- 利用canal中间件,不需要修改业务代码,伪装为MySQL的一个从节点,canal通过读取binlog数据更新缓存。
使用读写锁同步(强一致性)
- 采用Redisson提供的读写锁。
kotlin
// 共享锁:读锁readLock,加锁之后,其他线程可以共享读操作。
//读操作
public Object getById(Long id){
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("TEST_LOCK");
// 读锁
RLock readLock = readWriteLock.readLock();
try{
readLock.lock();
System.out.println("readLock...");
Object result = (Object) redisTemplate.opsForValue().get("goods:" + id);
if (result != null){
return result;
}
// 数据库查询
result = goodsMapper.selectGoodsByGoodsId(id);
// 写入缓存
redisTemplate.opsForValue().set("goods:" + id, result);
return result;
}finally {
readLock.unlock();
}
}
csharp
// 排他锁:独占锁writeLock,加锁之后,阻塞其他线程读写操作。
// 写操作
public void updateById(Goods goods){
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("TEST_LOCK");
// 写锁
RLock writeLock = readWriteLock.writeLock();
try{
writeLock.lock();
System.out.println("writeLock...");
// 更新数据库
goodsMapper.updateById(goods);
try {
Thread.sleep(5000);
}catch (InterruptedException e){
e.printStackTrace();
}
// 删除缓存
redisTemplate.delete("goods:" + goods.getGoodsId());
}finally {
writeLock.unlock();
}
}