咨询一个 redis 穿透的问题,看看大佬有什么解决方案没有

2021-03-08 17:41:30 +08:00
 BinYang

背景:在被访问数据库中没有的数据时,此时会访问 db 。线上 90%的数据都是数据库没有的(业务场景限定)。此时为了解决缓存穿透的问题,引入了 redis 设置空值。此时导致的结果是(数据是 n x n )的。导致线上 redis 有 3 亿的空值 key 。

解决思考:如果使用布隆过滤器,受限制于不能删除元素限制。不能使用,有什么更好的解决方案吗?

8081 次点击
所在节点    Java
97 条回复
coreki
2021-03-08 22:54:31 +08:00
redis 没有 key 就是没关系。业务层面,如果用户之间添加了关系就主动把 key 加到 redis 。这样就节省了 redis 的 90%内存
Orlion
2021-03-08 23:04:20 +08:00
7 楼回复应该是个很常见的设计,我司 redis 就分为缓存(不需要持久化,所有 key 都有过期时间)与持久化数据两种实例,楼主可以做个参考。如果麻烦的话,可以考虑该场景能否使用 bitmap 来做过滤
nagatoism
2021-03-08 23:17:55 +08:00
@BinYang 表结构感觉设计有问题,不如你要查 A 和 B 有没有关系,你应该问 A 的那个表有没有 B,而不是 A-B 这条边是不是存在在图里。
既然是一个稀疏图,为什么要用邻接矩阵而不是 neighborhood vector 来存储查询,这不是给自己找事?
murmur
2021-03-08 23:20:04 +08:00
如果有人恶意访问冷数据,应该考虑风控直接干掉他而不是在你的数据上做文章

而这个情况基本就是爬虫,照着列表一顿遍历不管时间新旧
Aidenboss
2021-03-08 23:49:51 +08:00
1. 继续使用 bloomfilter,如果发生关注行为,则写入到 bloomfilter ;如果发生取消关注行为,则不对 bloomfilter 做处理。这样,bloomfilter 可以过滤掉大部分不存在关系的数据(因为 bloomfilter 是必然不存在),需要定期重建。

3. redis uid -> bitmap,控制每个用户的 bitmap 长度控制在 [0, N)。如果 A 关注了 B,则 B 对应的 bitmap[ hash(A uid) % N ] = 1,相当于对 B 的关系链分桶。如果 bitmap[ i ] = 1,说明可能存在关注关系,再去 mysql 表回溯。 前面还可以将 {A_uid}_{B_uid} 作为 key 写入到 redis 中,防止回溯到 mysql 的请求打穿。
Aidenboss
2021-03-08 23:50:11 +08:00
1. 使用 uid -> redis set 的方式来判断关系。这个是最容易搞的。微博每人关注上限是 5k,一个用户也就 5k 个关注,存储上可以接受。

2. 继续使用 bloomfilter,如果发生关注行为,则写入到 bloomfilter ;如果发生取消关注行为,则不对 bloomfilter 做处理。这样,bloomfilter 可以过滤掉大部分不存在关系的数据(因为 bloomfilter 是必然不存在),需要定期重建。

3. redis uid -> bitmap,控制每个用户的 bitmap 长度控制在 [0, N)。如果 A 关注了 B,则 B 对应的 bitmap[ hash(A uid) % N ] = 1,相当于对 B 的关系链分桶。如果 bitmap[ i ] = 1,说明可能存在关注关系,再去 mysql 表回溯。 前面还可以将 {A_uid}_{B_uid} 作为 key 写入到 redis 中,防止回溯到 mysql 的请求打穿。
luozic
2021-03-08 23:52:52 +08:00
@linvon
这只是一个思路,并且工程上误删率这个可以通过选择适当的 bloom filter 变体来调整误删率,并且是可控制的。
----最后表示场景的描述和问题的建模这两部分有问题。
wangluofansi
2021-03-09 00:17:20 +08:00
思路一开始就错了,这种判存在的问题肯定是布隆过滤器或者位图。你说的不能删除元素问题,首先想想是否一定要到元素级别的删除?是否能接受批量级别的删除?如果可以,建立一千个布隆过滤器,每个负责千分之一的 uid (须选择足够随机的哈希方法),过期时间为一天加随机时间( 60 分钟内)。这是简单方案,仍可能有 0.1%级别的流量穿透,所以查询数据库限流也是必须的。更可靠的方案就复杂了,有必要再考虑。
yzbythesea
2021-03-09 06:37:46 +08:00
@BinYang 那这个就是类比一个好友功能,是吗?这本来就是挺难的一个问题。我的意思是你优化的方向错了,我觉得 redis 空值只要 QPS 能顶住,就水平扩容好了。但是从工程角度讲,如果你们有足够的时间,不应该用 redis 空值这个思路,而是去建立一个服务器专门返回这个用户以及和他有关系的人(类似于好友)。
sadfQED2
2021-03-09 08:24:51 +08:00
1.布隆过滤器不能删除,你可以搜一下布谷鸟过滤器,这个可以删除

2.所有数据落一份到 redis,redis 使用集群部署,几亿数据都不是什么大事
wuqingdzx
2021-03-09 08:27:31 +08:00
定期重建布隆过滤器就好了,搞那么复杂干嘛。
sampeng
2021-03-09 08:33:18 +08:00
明显是设计问题…
90%/数据库都没有的数据,你却要求缓存有…你品…你细品
whileFalse
2021-03-09 08:50:18 +08:00
3 亿 key 才能占多少内存啊,加内存就是了。
布隆过滤器有错误率,不能用。
whileFalse
2021-03-09 08:52:02 +08:00
@BinYang 以 Redis 为准,数据库做持久化有什么问题吗?甚至都可以不用数据库,直接 Redis 持久化。
shyrock
2021-03-09 09:29:30 +08:00
水平不够,说实话没看懂 lz 的需求。
数据库和 db 是不同的术语吗?
既然是访问数据库,为什么又 90%的线上数据在数据库中没有?
Dganzh
2021-03-09 09:42:25 +08:00
@shyrock 数据库里存了好友数据,业务发来两个用户查询两个人是否好友,大概率不是好友
BinYang
2021-03-09 09:47:53 +08:00
@Dganzh 对的,场景大概是这个兄弟说的场景。
BinYang
2021-03-09 09:48:44 +08:00
@sadfQED2 好的,研究一下。感谢
BinYang
2021-03-09 09:49:40 +08:00
@Aidenboss 这个建议很好,感谢感谢
BinYang
2021-03-09 09:52:52 +08:00
@wuqingdzx 数据要求实时的,如果定期的话,中间的空档时间。相当于还是没有关系(变更必须在定时重建之后)。

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

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

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

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

© 2021 V2EX