一.问题简述
在并发低、用户少的情况下,每次查询都能去Mysql查询数据返回,但在高并发情况下,每一个读请求都到Mysql去查询会导致数据库压力太大。
所以一般会使用Redis做一个缓冲,减轻数据库的压力:
正常情况下,使用Redis缓存数据流程如下:
正常的读请求该模式不会有问题,但是如果数据库信息有改动,那么数据库和Redis的数据一致性如何保证?
按我们常规的逻辑,数据库修改后去删除Redis的key即可。
其实不管是先删Redis再修改库,还是先修改库再删Redis都会有问题,因为读线程和写线程是并发的,无法保证其执行顺序。
1.如果先删Redis缓存,再写库。在删除Redis缓存后,写线程还没来得及修改数据,这时读线程发现缓存为空,则取读取了脏数据。
2.如果先修改数据,在删Redis缓存。在修改数据后如果线程宕机,那么Redis缓存删除失败,也会出现脏数据。
二.延时双删策略+设置缓存过期时间
所谓双删策略,就是在更新库的前后都进行Redis缓存的删除,但是更新后的删除时延时删除,伪代码如下:
public void update(){
//1.删除Redis缓存
redisTemplate.delete(key);
//2.更新数据库
mysqlConnection.update();
//3.延时删除
Thread.sleep(200);
redisTemplate.delete(key);
}
等待的时间如何确定
等待的时间为读线程读取数据的一个耗时,包括Redis主从同步,网络耗时等。
为什么要使用延时删除
为了防止在更新数据的过程中有读线程进来缓存数据,所以更新后估算一个时间再删除Redis缓存。
为什么要设置缓存过期时间
设置缓存时间是保证数据一致性的最终解决方案,因为在最坏情况下,脏数据的时间最多为设置的过期时间。过期后读线程会到数据库读取新数据。
该方案有什么弊端
1. 最坏情况是在一个过期时间内会存在脏数据。
2. 因为有延时,所以增加的写线程的执行时间。
三.基于订阅binglog的同步机制
后续完善