蛋疼的 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 太他么的蛋疼了

9095 次点击
所在节点    程序员
71 条回复
lijialong1313
2020-12-17 14:47:23 +08:00
读写 redis,然后用 worker 丢到数据库里。
moonblog
2020-12-17 15:20:52 +08:00
这不就是 spring redis cache 做的事情吗
service 层,几个注解解决的事
query 后(由于是 service 层,是否 joint 表随意),缓存到 redis
update 后,evict redis cache
dawniii
2020-12-17 15:33:46 +08:00
@Jrue0011 有可能是的。这种方法,感觉不是后台全场景都那么适用吧,后台一般有各种事务和查询条件。。。如果不是全数据都在 redis 里,在后台查询的时候也挺恶心,热的从 redis 查,冷的从数据库查?
Jrue0011
2020-12-17 16:07:04 +08:00
mybatis 缓存是什么情况,你们是用 redis 实现 mybatis 的二级缓存吗?如果上 mybatis 本地缓存的话,一个服务器的修改也没法清除另一个服务器上的 mybatis 缓存啊
securityCoding
2020-12-17 16:12:08 +08:00
@dswyzx 233 ,就是一个缓存策略问题 .
cache aside , write/read through 最大区别是 write through 会代理数据操作 , 业务层面直接跟缓存组件交互.
mtrec
2020-12-17 18:02:41 +08:00
cap,cache aside pattern
读 缓存有直接用 没有就查数据库然后更新缓存
写 写完 db 清对应的缓存
yzbythesea
2020-12-17 18:10:30 +08:00
说实话 QPS 没上 10,真的不用 redis 缓存。
Vegetable
2020-12-17 18:19:43 +08:00
@sadfQED2 这个读写分离可以的
laminux29
2020-12-17 18:26:46 +08:00
上面一堆人还没搞清楚原因就给建议..

1.Mysql 支持事务但性能不够,Redis 性能够但不支持事务。

2.Redis 性能之所以够用,本质是因为相对于 Mysql,Redis 砍掉了数据安全与事务功能,这样全跑在内存里,又不要考虑事务,速度不快才怪。

3.题主的需求:Mysql 数据更新之后,要求 Redis 必须和数据库一致,本质上是要给 Redis 增加事务,还要让 Redis 接受 Mysql 的控制,这是不现实的。

================
几种方案:

1.Mysql 数据只做新增,不查不改不删,然后推送到 Redis,Redis 做只查,然后允许 Mysql 与 Redis 存在短期内的不一致。这是大厂,包括谷歌的标准玩法。

2.有钱能增加机器,并且业务支持并行写入或并行读取,则可以根据业务,把系统设计为对并行写入优化但会增加读取时间,或者设计为对并行读取优化但会增加写入时间。

3.非常有钱,直接上 Oracle 最新版,支持内存表,虽然没 Redis 快,但比 mysql 快得多,还支持事务。
sadfQED2
2020-12-17 18:50:08 +08:00
@liudaolunhuibl 1.并不是缓存表的数据,而是像楼主那样缓存接口数据,每次表更新重新掉接口拿
2.我们没有连表的操作
sadfQED2
2020-12-17 18:52:55 +08:00
@Aruforce 你连 binlog 转 redis 这点延时都忍受不了,那你就不能考虑缓存了啊
libook
2020-12-17 18:53:54 +08:00
写程序难题 Top2:起名和维护缓存。

把缓存和数据库的操作封装在一起,对业务功能仅提供统一的接口,接口被调用后内部管理缓存和数据库的操作。

基本思路就是有读的话直接去 Redis 里读,有写的话先删缓存,再写数据库。

可以在写入的时候使用事务,确保写入的时候数据库里的值没有被其他应用实例修改,如果遇到了就尝试二段提交。

考虑到缓存刚刚被删除就有可能有读请求进来,为了确保一致性,可以在删缓存之前在数据库记录中加一个锁(类似排他锁),等更新完再解锁。

数据库层级的事务在引入微服务思想和各种中间件、数据库之后,会有较大的局限性,此时就要更多考虑分布式事务方案。
corningsun
2020-12-17 19:25:25 +08:00
难点是什么时候删除。
表和接口不多的话,维护所有表和查询接口的关联关系。
然后哪张表更新了,就删除对应查询接口的缓存即可。
kaneg
2020-12-17 21:44:07 +08:00
你既要 mysql 的实时存储,还要 redis 的高速缓存,这是鱼翅和熊掌不可兼得的。
除非你的接口非常简单和少量,明确知道哪个操作会造成缓存和数据库不一致,然后让其失效。
理想要落地必然要做取舍,否则追求的结果必然是镜中花,水中月。
vindurriel
2020-12-18 01:00:56 +08:00
读取全部走 redis key 分两种:列表和单行,其中列表的 values 能对应到单行的 keys,对应数据库每一行的 pk

写入单行:在 mysql transaction 的最后一步写 redis 如果写入不成功 db 回滚 redis 不是集群的话就强一致了

写入列表:batch 或者 stream 进行联表查询 只能最终一致
wangritian
2020-12-18 02:03:16 +08:00
赞同 21 、29 、35L
缓存是用在数据层的,而不是接口层,你自己也感觉到了
其次也不能滥用,像复杂条件查询或是 join 查询等等,应该去优化索引和提高 mysql 硬件配置,然后考虑分表
不建议你在错误的方案下解决问题
black11black
2020-12-18 03:00:56 +08:00
@sadfQED2 所以一次 redis 请求还要附带一个 binlog 检查操作?感觉有点本末倒置啊
YouLMAO
2020-12-18 08:06:23 +08:00
删除 Redis,MySQL 换成 cloud spanner
gosansam
2020-12-18 09:48:01 +08:00
@laminux29 请问数据的更新是通过 binlog 完成的吗?
seth19960929
2020-12-18 10:16:52 +08:00
我觉得你可以找一个支持 tag 方式的缓存库(类型 laravel 的 tags cache)
然后你继续在 API 层面做缓存.
针对 API 写好 tag, 比如 API 这样:
接口 1 依赖 users, points 表, 就给这个接口增加 users, points 标签(用 model 的名字更好反射)
接口 2 依赖 points 表, 给这个接口打上 points 标签

当 users 表发生变化, 直接清空 users tag 的接口 1
当 points 表发生变化, 直接清空 points tag 的接口 1, 2

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

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

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

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

© 2021 V2EX