前言
mysql与Redis一般不会使用强一致性因为不仅设计复杂并且性能差(典型的吃力不讨好类型),而是使用最终一致性
如果想了解mysql与Redis数据一致性问题可以看之前这篇:
https://blog.csdn.net/dengjiayue/article/details/145120778?fromshare=blogdetail&sharetype=blogdetail&sharerId=145120778&sharerefer=PC&sharesource=dengjiayue&sharefrom=from_link
mysqlRedis_10">mysql与Redis能实现数据的强一致?
我之前提了一个方案是,使用本地事务实现一致
伪代码逻辑
//开启事务
tx:=db.Start()//更新 mysql 数据
err = tx.Update(data)
if err!=nil{tx.Rollback()return
}//清除 Redis 数据
err = redis.Delete(key)
if err!=nil{tx.Rollback()return
}//提交事务
err = tx.Commit()
if err!=nil{tx.Rollback()return
}
- 事务是强一致方案?
事务(Redis 删除)不是强一致方案,
a. 对于 mysql 与 Redis 的数据来讲是最终一致性方案(要等到下一次查询,mysql 才与 Redis 的数据是一致的)
b. 对于数据查询来讲是查询到的一定是最新数据(Redis 清除后无法命中,只能查询 mysql 最新数据)
ⅰ. 只要用户点击更新数据接口并收到更新成功的返回,那么查询的数据就是更新的数据,不会查询到旧数据
c. 并且只适用于清除Redis数据, 如果更新数据将会有极大的 bug
如果是更新会怎么样?为什么只能删除?
- 如果 Redis 更新成功了,但是在第二阶段 mysql 发送 commit 请求的时候失败了(比如mysql 宕机)那么 Redis 的数据与 mysql 的数据将会不一致(mysql 没有提交,所以是旧数据,而 Redis 是新数据),所以只适用与 Redis 清除的方案
mysql__Redis__57">分布式实现数据的强一致的方案是什么?mysql 与 Redis 能强一致吗?
分布式如何实现强一致
ⅰ. 共识算法(比如 raft)
ⅱ. 分布式事务
参考:
https://cn.pingcap.com/article/post/13082.html
https://juejin.cn/post/7316967969917009971(分布式事务 2pc/3pc实现强一致)
https://cloud.baidu.com/article/3076675
Redis 的事务不适合实现强一致
Redis 事务没有原子性,而是一个批量脚本,并不具备 像 mysql 那样的start 与 commit 的功能
ⅰ. 所以无法使用单纯的分布式事务(两阶段提交)来实现强一致性(Redis 无法持有数据的资源)
mysql_Redis__72">那怎么办?(mysql 与Redis 有强一致方案吗?)
mysql 与 Redis 实现强一致的方案: 分布式锁+补偿机制
- 强一致的概念: mysql 与 Redis 数据同时生效,并且是相同的.
- 分布式锁解决的问题: Redis 无法持有数据资源的问题
- 补偿机制:解决Redis 无法回滚的问题(mysql 的回滚是通过 undo log 实现的 ,但是Redis 没有,只能记录数据之前 的状态来回滚)
相当于给 Redis 赋予与 mysql 一样的事务的功能,然后再使用分布式事务两阶段提交的逻辑.
具体做法
● 更改时加分布式锁,拦截所有的读写请求
● 记录数据之前的状态(用于第二阶段提交失败的 Redis 补偿(恢复到没更新前的状态)
● 更新数据(先更新 mysql(不提交),再更新 Redis,再根据操作的结果决定是提交还是回滚)
● 事务 提交/回滚(Redis 补偿)
- 提交/回滚 没有问题,释放分布式锁
这样做虽然能保持 mysql 与 Redis 的数据强一致,但是对于性能的影响非常大,所以一般不会使用
mysql 与 Redis 一般都是采用最终一致的方案(清除 Redis 数据等到下一次查询才最终一致)
不仅仅是 mysql 与 Redis,就算是mysql 主从之间,Redis 主从之间,以及其他分布式数据一致性的场景都不会使用强一致的方案,为了保证服务的性能都会采用最终一致的方案
思考:共识算法能实现强一致吗?怎么工作的?