蛋疼的 Redis 和需求...我想问下各位都是怎么实践 Redis 和 Mysql 数据一致性的或者怎么骂回去?

2020-12-17 12:17:08 +08:00
 Aruforce

1.目前基本技术栈

  1. SpringMVC
  2. Mybatis
  3. Redis

2.基本代码实践思路

  1. 数据更新完全在 Mysql 事务之中 不会读取更新缓存
  2. 现在 Redis 只是用来做接口缓存(数据有有效期,Key 根据 API 入口参数建立,value 就是 Mysql 查询的结果)

3. 目前提出的需求及存在的问题

  1. Mysql 数据更新之后 要求全部的接口必须和数据库一致
  2. 现在的接口返回的数据有很多连表查询,所以 mybatis 全局缓存并不好使。。同一个 namespace 的连表查询结果在表数据更新后缓存会被删除但是其他的 namespace 不行。。所以不好使
  3. 老板不懂开发
  4. 如果能做给的实践也不算长

4.我该怎么处理?

  1. 无法完成缓存强一致性,完成不了需求。。这个不太好。。不过有好的说服的理由的话我也试试
  2. 统计在任何一个表数据更新之后需要删除关联查询接口的缓存? 这个工作量太大了
  3. 或者各位给我们一个好的 Redis 缓存实践? Redis 做接口缓存自我感觉也不太好

我倾向与 1 和 3
一个是不做。。
一个是 能争取实践或者我能学点东西。。。

2 太他么的蛋疼了

9044 次点击
所在节点    程序员
71 条回复
sampeng
2020-12-17 13:06:03 +08:00
因为做的时候就没考虑高一致性的高并发。高并发和事务是相对互斥的实现方案。要想达到完美,需要对事务和缓存结构做非常好的设计。一看你说拿 api 的 key 做缓存就知道再项目开始就没设计缓存架构。这是技术债务,找老板要时间填坑或者自己加班填坑。

我司有一个服务就是这么实现的,当然没有事务,但是性能差的发指。20 台机器吃 30 万日活。然后有互联网经验的做了数据级别缓存后,3 台即可。还是为了高可用…不然一台都够了。

程序员的价值是解决问题,不用问别人,你自己是有方案的。一个一个试,这才是最有价值的地方。搞成了,这个坑你再不会进去,也有极其丰富的经验。问别人,谁能手把手教你呢
sampeng
2020-12-17 13:15:42 +08:00
@Aruforce 方案就是不要连表,redis 做分布式事务。所有子查询都是 redis 缓存,所有写入先写 redis 。工作量肯定很大,你不会选
gadsavesme
2020-12-17 13:22:21 +08:00
一般就是两种解决方案啊,延时双删,还有就是订阅 mysql 的 binlog 。不过极限情况下不同步肯定会有,要强一致那就加锁呗,数据同步过程中就等待好了,这个时间就毫秒级,应该也不大会影响。
micean
2020-12-17 13:30:22 +08:00
调整缓存:主键 = 行数据
调整接口:查询数据库返回需要数据行的主键,再从缓存中取回行数据

等数据库扛不住再说
dawniii
2020-12-17 13:35:17 +08:00
@sampeng 总感觉很多极限情况下,会有问题。比如 redis 写成功了,mysql 还没 commit 呢,机器挂了,redis 不会自动回滚吧,这时候 redis 的就是脏数据了,感觉总是要牺牲点什么。
sampeng
2020-12-17 13:38:28 +08:00
@dawniii 我刚说了,取消数据库事务实现。全部靠程序控制。
dawniii
2020-12-17 13:46:43 +08:00
@sampeng 不管怎么实现,都需要面临双写的情况吧。极限情况可能会出现,其中一个成功,另一个失败。然后再弄个分布式的事务去重试或者回滚(逆操作)吗?但是在你程序回滚的过程中,确实有脏数据了。
dawniii
2020-12-17 13:49:03 +08:00
@dawniii 这算是最终一致性?
dswyzx
2020-12-17 13:54:50 +08:00
一直以为 redis 的缓存用法是缓存热点数据,妹想到现在都是把 redis 当 mysql 用了
JasonLaw
2020-12-17 14:12:03 +08:00
"Turning the database inside out with Apache Samza" by Martin Kleppmann - YouTube

<amp-youtube data-videoid="fU9hR3kiOK0" layout="responsive" width="480" height="270"></amp-youtube>
kkkkkrua
2020-12-17 14:15:45 +08:00
如果是用的 spring cache 组件的话,可以在更新的时候删除 cache
caryqy
2020-12-17 14:17:02 +08:00
看你的第一个需求需要 Mysql 数据更新之后 要求全部的接口必须和数据库一致


更新 mysql 时先把 redis 中此类数据锁住,此时进来的请求都等待,mysql 更新完之后更新 redis 中数据完成之后删除 redis 锁, 请求返回最新的缓存数据,如果此期间 mysql 或 redis 任何一端挂掉了 /服务挂掉了,请求端得到错误响应 /超时之后需要重新请求, 数据抄送一份到 kafka 中每天的定时任务去校验当天 /时数据
caryqy
2020-12-17 14:22:45 +08:00
希望别一顿操作之后和这个类似 https://www.v2ex.com/t/735360 🐶
Jrue0011
2020-12-17 14:23:40 +08:00
@dawniii 我感觉他的意思应该是写的话不操作数据库只写缓存,通过某种方式将缓存的变化异步更新到数据库
LJ2010
2020-12-17 14:26:02 +08:00
什么是缓存? 重点缓解存储的压力, 而不是存储本身,既然是缓存必然存在数据不一致时差的问题,而且缓存目的是把热数据降温,而不是存储所有数据,换个角度就没必要纠结数据一致性的问题,什么样的数据需要一致性?我的观点涉及钱的需要,其他的不需要,所以没必要纠结数据瞬时的一致性,所以你一定要坚持 观点 1,否则就骂街:)
DoubleShut
2020-12-17 14:26:31 +08:00
读 binlog 进行同步
AA5DE3F034ACCB9E
2020-12-17 14:26:53 +08:00
不可能三角吗
pengliu
2020-12-17 14:26:53 +08:00
要求数据实时就不要加缓存,加了缓存肯定会有数据不一致,根据业务需求再定技术方案
liudaolunhuibl
2020-12-17 14:33:40 +08:00
@sadfQED2 想到过这种方案,但是如果是连表的缓存怎么办呢?貌似 binlog 都是单表的
hhyyd
2020-12-17 14:41:39 +08:00
不是很明白楼主的问题。spring 的 cache 组件,通过 cacheEnable 和 cacheEvict 注解可以很方便的控制 redis 缓存啊。 麻烦的地方是,查询涉及到多张表,修改每张表的时候都需要删除查询时涉及到的 key,可能需要你设置一组合理的 key,来解决这个问题。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/736322

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX