在并发场景下,同时操作数据库和缓存会存在数据不一致的情况。
本文通过模拟实现并发场景缓存与数据库双写不一致的情形,并采用分布式读锁解决此问题。
为实现并发场景,通过jmeter模拟50个线程在1秒内访问接口。
1.读取缓存,更新数据库和缓存,其余代码忽略。
@PostMapping("/updateScore")
public String updateScoreByStudentId (@RequestParam Long studentId, @RequestParam String subject) {
String key = "student_"+studentId;
Integer score = Integer.parseInt(stringRedisTemplate.opsForValue().get(key));
SysScore sysScore = new SysScore();
sysScore.setScore(score - 1);
sysScore.setStudentId(studentId);
sysScore.setSubject(subject);
stringRedisTemplate.opsForValue().set(key,String.valueOf(sysScore.getScore()));
studentService.updateScoreByStudentId(sysScore);
return "success";
}
结果
缓存
127.0.0.1:6379> get student_3
"72"
数据库
3 数学 91
出现缓存与数据库双写不一致的场景
2.加入分布式读写锁(不能用需要读写的key来实现锁),读取缓存前加锁,更新完缓存和数据库后释放锁。
@PostMapping("/updateScore")
public String updateScoreByStudentId (@RequestParam Long studentId, @RequestParam String subject) {
String key = "student_"+studentId;
RReadWriteLock readWriteLock = redisson.getReadWriteLock("student_score_3");
RLock wLock = readWriteLock.writeLock();
wLock.lock();
Integer score = Integer.parseInt(stringRedisTemplate.opsForValue().get(key));
SysScore sysScore = new SysScore();
sysScore.setScore(score - 1);
sysScore.setStudentId(studentId);
sysScore.setSubject(subject);
stringRedisTemplate.opsForValue().set(key,String.valueOf(sysScore.getScore()));
studentService.updateScoreByStudentId(sysScore);
wLock.unlock();
return "success";
}
结果
缓存
127.0.0.1:6379> get student_3
"50"
数据库
3 数学 50