db和缓存双写不一致
多线程访问环境下,在更新完db后再去更新缓存,不加锁显而易见的就会出现缓存被覆盖的问题。
线程1
修改完db
去更新缓存的时候慢了一拍。
此时线程2
在线程1
之后修改完db
更新成功了缓存。
此时线程1
更新缓存的操作恢复了,然后去更新了缓存。那么此时的缓存其实是个脏数据。
有什么办法呢?
方案一
直接加redission
的普通Rlock
分布式锁,比如对一个sku
的缓存更新都排着队串行操作。这样也行,但是性能损失大。
方案二
Redisson
其实还有读写锁.
操作过db
的都知道,写锁是互斥的,读锁是可以大家一起加的。生产中实际80%
大多都是读操作,剩下20%
才是写。
下面来上demo
redission依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.8.2</version>
</dependency>
配置类
package com.fchan.espractice.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyRedissionConfig {
@Bean
public RedissonClient redissionClient() {
Config config = new Config();
//单机模式
config.useSingleServer()
.setAddress("redis://192.168.56.101:36379")
//.setPassword("111")
.setDatabase(1);
//集群模式
/*config.useClusterServers()
.addNodeAddress("redis://192.168.56.101:36379")
.addNodeAddress("redis://192.168.56.102:36379")
.addNodeAddress("redis://192.168.56.103:36379")
.setPassword("1111111")
.setScanInterval(5000);*/
//哨兵模式
/*config.useSentinelServers().addSentinelAddress("redis://ip1:port1",
"redis://ip2:port2",
"redis://ip3:port3")
.setMasterName("mymaster")
.setPassword("password")
.setDatabase(0);*/
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
测试读写锁
测试发现同一个key
读锁可以重复添加,但是在读锁还没释放的时候,加写锁是会被阻塞的。当这个key
的读锁都释放完了写锁才能加上。一旦加上写锁,那么其他的写锁和读锁就会被阻塞了。
比如扣减库存场景,并发读下来,然后并发扣减,可能会有扣减失败的,之前读的时候还有,扣减的时候就没有库存了。
@GetMapping("addReadLock")
public void addReadLock(@RequestParam("key") String key){
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(key);
RLock lock = readWriteLock.readLock();
lock.lock();
}
@GetMapping("releaseReadLock")
public void releaseReadLock(@RequestParam("key") String key){
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(key);
RLock lock = readWriteLock.readLock();
lock.unlock();
}
@GetMapping("addWriteLock")
public void addWriteLock(@RequestParam("key") String key){
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(key);
RLock lock = readWriteLock.writeLock();
lock.lock();
}
@GetMapping("releaseWriteLock")
public void releaseWriteLock(@RequestParam("key") String key){
RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(key);
RLock lock = readWriteLock.writeLock();
lock.unlock();
}