解决MySQL与Redis缓存一致性的问题

背景

考试系统中,教师会在后台发布一场考试,考试会存储在MySQL和Redis里面,考试有时候是会出错的,我们需要后台修改,如果多个教师在后台并发修改(概率不大),可能会出现数据库缓存不一致的问题,我们需要解决一下,刚好联系一下这一块的知识

基础知识理论

四种解决办法:

  1. 先更新数据库,再更新缓存
  2. 先更新缓存,后更新数据库
  3. 先删除缓存,后更新数据库
  4. 先更新数据库,后删除缓存

这四种情况,都会出现缓存不一致的问题,但是第四种出现缓存不一致的情况概率上比较低,因为缓存的写入回远远快于数据库的写入,我们可以使用【先更新数据库+再更新缓存】的方式来解决数据库不一致的问题,为了确保万无一失,我们还可以加上一个过期时间。

但是有两个问题

  1. 我们需要保证先更新数据库+再更新缓存是一个原子操作,不然如果更新缓存失败了,导致缓存中的数据还是旧值
  2. 「先更新数据库,再删除缓存」的方案虽然保证了数据库与缓存的数据一致性,但是每次更新数据的时候,缓存的数据都会被删除,这样会对缓存的命中率带来影响。

对于第一个问题,解决办法是消息队列重试机制、订阅MySQL binlog,再操作缓存

对于第二个问题是,我们使用**「更新数据库 + 更新缓存」**的方式来实现,但是这个方案本身固有一些问题,解决办法是,分布式锁和过期时间

另外先删除缓存,再更新数据库的解决方案是延迟双删

实战-先更新数据库,后删除缓存

此种方案存在问题是两个操作不是原子性的,如何保证两个操作都能成功呢?

方案一:gin框架+消息队列

我们实现一个中间件

Go 复制代码
func Canal() gin.HandlerFunc {
	//封装成一个中间件,当我们更新考试的时候,会自动监测到
	return func(c *gin.Context) {
		c.Next()
		redisDB := global.GVA_REDIS
		get, exists := c.Get("DeleteRedisKey")
		DeleteKey := get.(string)
		if exists {
			err := redisDB.Del(context.Background(), DeleteKey).Err()
			if err == redis.Nil {
				//无需删除
				c.Next()
			} else if err != nil {
				//删除失败,加入到redis异步队列中,再次删除
				addToDelayedQueue(context.Background(), redisDB, DeleteKey, time.Now().Add(10*time.Second))
			}else {
				global.GVA_LOG.Info("更新数据成功")
			}
		} else {
			zap.L().Info("redis中不存在该键,无需删除")
		}
	}
}

我们在继承这个中间件的接口中的使用c.Set方法传递要删除的redisKey,然后我们在上述中间件中使用c.Get方法来接收要删除的redisKey,然后加入到异步队列中删除

现在来模拟一下是否能够成功,项目启动之后,把redis停机,然后删除redis的key就会失败,然后该key就会加入延时队列

方案一:消息队列订阅binlog

里面有两个难点,一个是订阅binlog服务,一个是消息队列

如何订阅binlog服务,我们使用阿里巴巴的canal ,支持Go语言,具体参考Golang通过alibabaCanal订阅MySQLbinlog_大杯无糖的博客-CSDN博客

接下来是用redis的zset实现一个异步消息队列

相关推荐
b78gb1 小时前
电商秒杀系统设计 Java+MySQL实现高并发库存管理与订单处理
java·开发语言·mysql
奥尔特星云大使1 小时前
CentOS 7 安装 MySQL 8
mysql·centos·mysql 8
lang201509282 小时前
MySQL FIPS模式:安全合规全解析
mysql
一只叫煤球的猫4 小时前
建了索引还是慢?索引失效原因有哪些?这10个坑你踩了几个
后端·mysql·性能优化
迎風吹頭髮5 小时前
Linux内核架构浅谈49-Linux per-CPU页面缓存:热页与冷页的管理与调度优化
linux·缓存·架构
21号 15 小时前
9.Redis 集群(重在理解)
数据库·redis·算法
爬山算法5 小时前
Redis(73)如何处理Redis分布式锁的死锁问题?
数据库·redis·分布式
jason.zeng@15022076 小时前
centos中安装redis
linux·redis·centos
码农多耕地呗6 小时前
力扣146.LRU缓存(哈希表缓存.映射+双向链表数据结构手搓.维护使用状况顺序)(java)
数据结构·leetcode·缓存
yumgpkpm7 小时前
华为鲲鹏 Aarch64 环境下多 Oracle 、mysql数据库汇聚到Cloudera CDP7.3操作指南
大数据·数据库·mysql·华为·oracle·kafka·cloudera