Redis 和 Mysql 数据库数据如何保持一致性

一、操作

我们在实际项目中经常会使用到Redis缓存用来缓解数据库压力,但是当更新数据库时,如何保证缓存及数据库一致性,一般我们采用延时双删策略。

目前系统中常用的做法是一个查询接口,先查询Redis,如果不存在则查询数据库,并将结果放入到Redis中。

为什么是删除缓存,而不是更新缓存呢?主要是如果缓存的内容是带有树型结构或者List,Map,那么更新其中一个内容相对较慢。

本文所讲操作,均是按照如果缓存不存在,查询数据库后,再放入Redis。

二、 常见更新策略

  • 1 先删缓存,再更新数据库

  • 2 先更新数据库,再删除缓存

  • 3 普通双删

  • 4 延迟双删

三、图解

1 、先删缓存,再更新数据库

线程A删除缓存数据,此时还没更新数据库

线程B查询缓存没有数据,查询数据库还是旧数据,放入缓存

线程C及其他线程使用旧缓存数据,缓存和数据库不一致

2、先更新数据库,再删除缓存

线程A更新数据库,此时还没有删除缓存

线程B及其他线程此时使用的还是旧缓存数据,和数据库内容不一致

3 、普通双删

线程A先删除缓存,再更新数据库,再删除缓存

线程B查询缓存没有数据,在线程A更新数据库之前,查询到旧数据,此时系统时间片切换到线程A执行删除缓存,之后又轮到线程B放入缓存旧数据

线程C针对于线程A,查询缓存没有数据,查询到旧数据,放入缓存旧数据

都不能满足缓存和数据一致性。

4、延迟双删

线程A先删除缓存,之后更新数据库

线程B和线程C发现缓存没数据,查询数据库。线程B查询到的是旧数据,线程C查询到的是新数据。之后纷纷放入缓存

线程A延时3-5秒(时间一般要大于SQL执行时间+线程切换执行时间100ms足够),再将缓存删除。之后其他线程再查询缓存,发现没数据,再次查询数据库及放入缓存都是新数据

极端情况就是线程D,所以延时双删还是不一定能保证缓存及数据一致。

四、总结:

1、加锁

在发现缓存没有数据后,在执行查询数据库前,对该Key进行加锁,查询数据库并放入缓存后再解锁,这样可以避免缓存击穿问题,当某个redis数据不存在时,大量线程并发查询数据库。

在需要执行双删前,对该Key进行加锁,之后执行删除缓存,更新数据库,放入新数据到缓存,在解锁。保证缓存和数据一致性。

加锁的Key都需要设置过期时间,避免因为宕机造成死锁。

2、使用Canal 分布式数据库同步工具,结合mq中间件

相关推荐
m***923826 分钟前
【SQL】MySQL中的字符串处理函数:concat 函数拼接字符串,COALESCE函数处理NULL字符串
数据库·sql·mysql
!停39 分钟前
VS实用调试技巧
数据库
TracyCoder1231 小时前
MySQL 实战宝典(八):Java后端MySQL分库分表工具解析与选型秘籍
java·开发语言·mysql
8***23551 小时前
在Django中安装、配置、使用CKEditor5,并将CKEditor5录入的文章展现出来,实现一个简单博客网站的功能
数据库·django·sqlite
Token_w2 小时前
openGauss:全密态数据库的金融级安全实践
数据库·安全·金融
合作小小程序员小小店3 小时前
图书管理系统,基于winform+sql sever,开发语言c#,数据库mysql
开发语言·数据库·sql·microsoft·c#
ss2733 小时前
020:共享锁深度解析:从AQS原理到高并发实践
数据库·redis·缓存
字节拾光录3 小时前
手机号存储避坑指南:从20亿级数据库实践看,为什么VARCHAR才是终极答案
java·数据库·oracle
q***46526 小时前
Win10下安装 Redis
数据库·redis·缓存
p***92488 小时前
深入理解与实战SQL IFNULL()函数
数据库·sql·oracle