Mysql 和 Redis 数据一致性问题
Mysql 和 Redis 数据一致性问题
数据库的数据,放入缓存后,立马又被更新了,那么该如何更新缓存呢?
1. 先写缓存,再写数据库【缓存中数据可能为脏数据】
如果刚写完缓存,突然网络出现异常,导致写数据库失败了 ===> 其结果是缓存更新成了最新的数据,但数据库没有
2. 先写数据库,再写缓存(低并发)
问题很明显,并发请求下,A请求(写请求)先到达Mysql,但卡了一会儿,B请求(写请求)后到达,但是处理比A快,此时最新的数据应该是B,但是由于更新redis的操作A比B晚一点儿,导致redis数据为A的数据。
3. 先删缓存,再写数据库
redis 删除后,mysql写数据库的时间内,所有的读请求都打到mysql上了
A请求(写)删除缓存后,更新mysql数据,此时B请求(读)也进入了Mysql,B先完成查询操作,回写redis,A请求完成更新,此时正常的数据应该为A数据,但redis中写的B数据。 (主要是因为,写请求比较耗时,查询请求耗时较短,出现上述问题的可能性较大)
4. 先写缓存,再写数据库,再删除Redis
经典的缓存双删问题,在上一个方案上有一些优化,让请求A等待一段时间,再删除缓存,保证蓝色的删除缓存在回写之后
- “缓存双删”不要用无脑的 sleep 500 ms;
- 通过消息队列的异步&串行,实现最后一次缓存删除;
- 缓存删除失败,增加重试机制
5. 先写 数据库,再删除 Redis
可能存在的问题:当请求A更新Mysql为11后 ,回写redis之前,请求B读取到了缓存中的数据,出现不一致
也就是在A请求更新Mysql后且删除redis之前的读取请求会有不一致问题(发生概率低)
另外考虑缓存刚好失效的情况
请求A 查询缓存,缓存失效,直接查询mysql,并回写缓存,在回写缓存前,请求B更新了mysql数据,删除缓存(此时没有缓存),然后A再回写缓存。 此时数据不一致,可能持续较久。
上述需要满足两个条件 缓存刚好过期、B请求更新mysql的时间,比A请求读取mysql+回写缓存 时间还长。(条件很难达到)
总结:读写并发,缓存不失效的情况下,会出现A(写请求)删除缓存之前 读取到的数据不对,持续时间短
读写并发,缓存恰好失效,会出现B(读请求)查询mysql - [A(写请求)更新了mysql,删除过期缓存] 回写redis之间