目录
概要
问题:我看你做的项目中,都用到了Redis, 你在最近的项目中哪些场景使用了redis呢?
一般回答:
缓存 缓存三兄弟(穿透、击穿、雪崩)、双写一致、持久化、数据过期策略、数据淘汰策略
分布式锁 setnx、redisson
消息队列、延迟队列 何种数据类型
这一章节我简单描述一下双写一致性的回答,写的可能不太好,大家仅供参考,谢谢。
什么是双写一致性
双写一致性:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致。
读操作:缓存命中,直接返回;缓存未命中中查询数据库,写入缓存,设定超时时间
写操作:延迟双删
关于先删除缓存还是先修改数据库在多线程的情况下都会存在数据不一致的问题,如下图
简单描述一下:一开始,缓存和数据库的值V=10,当更新线程1进来更新值时,先把缓存值删掉。然后查询线程2查询缓存未命中,那么查询线程2就开始查询数据库,然后将数据库的v=10再次写入缓存中。此时更新线程1才完成对数据库写的操作v=20,那么此时,V的值在缓存和数据库中就会出现不一致。
简单描述一下:查询线程1开始查询数据未命中缓存,那么线程1将直接查询数据库,在此时。更新线程2来更新数据V的值(v=20),更新完成之后删除缓存。这个时候查询线程1其实之前已经查到了v=10的结果,然后线程1就会把它查到的值再次写入缓存,就导致了数据库和缓存数据不一致的问题。
Redis和MySql怎么保证数据的一致性
解决方案一:加分布式锁
共享锁:读锁readLock,加锁之后,其它线程可以共享读操作
排他锁:独占锁writeLock也叫,加锁之后,阻塞其它线程写操作。
描述如下:在线程操作时,加分布式锁,可以保证数据的强一直性。效率低。
如下代码(共享锁)
public Item getById(Integer id){
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(s:"ITEM_READ_WRITE_LOCK"); //读之前加读锁,读锁的作用就是等待该Lockkey释放写锁以后再读
RLock readLock=readWriteLock.readLock();
try {
//开锁
readLock.lock();
System.out.println("readLock...");
Item item = (Item) redisTemplate.opsForValue().get("item:"+id);
if(item !=nulL){
return item;
}
//查询业务数据
item=new Item(id,name:“华为手机",desc:"华为手机",price:5999.00);
//写入缓存
redisTemplate.opsForValue().set("item:"+id,item);
//返回数据
return item;
} finally{
readLock.unlock();
}
}
如下代码(排他锁)
public void updateById(Integer id) {
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(s:"ITEM_READ_WRITE_LOCK");
//写之前加写锁,写锁加锁成功,读锁只能等待
RLock writeLock = readWriteLock.writeLock();
try {
//开锁
writeLock.Lock();
System.out.println("writeLock...");
//更新业务数据
Item item=new Item(id,name:"华为手机",desc:"华为手机",price:5299.00);
try {
Thread.sleep(millis:10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//删除缓存
redisTemplate.delete(key:"item:" + id);
} finally {
writeLock.unlock();
}
}
解决方案二:基于异步通知保证数据的最终一致性
解决方案三:基于Canal的异步通知(支持mysql)
总结:
问:redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)
1.介绍自己简历上的业务,我们当时是把文章的热点数据存入到了缓存中,虽然是热点数据,但是实时要求性并没有那么高,所以,我们当时采用的是异步的方案同步的数据。
2.我们当时是把抢券的库存存入到了缓存中,这个需要实时的进行数据同步,为了保证数据的强一致,
我们当时采用的是redisson提供的读写锁来保证数据的同步
问:那你来介绍一下异步的方案(你来介绍一下redisson读写锁的这种方案)
一:允许延时一致的业务,采用异步通知
① 使用MQ中间中间件,更新数据之后,通知缓存删除
② 利用canal中间件,不需要修改业务代码,伪装为mysql的一个从节点,canal通过读取binlog数据更新缓存(推荐)
二:强一致性的,采用Redisson提供的读写锁
① 共享锁:读锁readLock,加锁之后,其他线程可以共享读操作
② 排他锁:独占锁writeLock也叫,加锁之后,阻塞其他线程读写操作