一个 abtest 实验中 redis 设计的思考

2023-06-05 17:21:29 +08:00
 ben548
一个关于 abtest 需求的 redis 场景问题,
需求:需要将用户分成 abcd 四个桶,不同桶内用户看到的信息不同,第一次分桶时需要将分桶结果发送给数据部门。
实现:将用户 id 经过 murmurhash 计算出来的哈希结果按 4 取模,得到分桶信息,将该结果保持到 redis 中,每个用户一个 redis key ,字符串类型存储,如 abresult_11111(user_id):1,程序开始执行时先读取 redis ,如果存在分桶记录,那么直接返回,不存在则进行分桶操作,并将分桶结果发给数据部门

疑问:
目前这个设计是一个好的设计吗?百万级别的用户量的话,是不是会生成百万的 redis key ,印象中 redis_key 过多不是一个好的设计,比如不好管理等。
我能想到的几个问题:
1.印象中用 hash 结构来存储,会被这种存储方式节约内存,但是用 hash 来存储必然导致 big key 问题,当然在这种场景下面,不涉及像 getall 那样的 O(n)操作,是不是 big key 问题可以基本忽略不计?印象中 big key 可能导致的问题不止是性能问题,还有像数据倾斜导致的访问倾斜问题等,所以用 hash 来存储的话是不是也要那用户 id 来做分片才是比较合适的方案?
2.redis 的删除是惰性删除+定时删除,定时删除基于取样,取样的话如果失效的数据过多,印象中会一直不断的循环删除,指定取样的结果不满足为止,想知道这个定时删除是在主进程上完成的吗?如果过多的 key 失效,会阻塞 redis 进程吗?

大佬们,一起讨论下啊?一个是上面提出的一些问题,还有就是如果是你们来设计的话会怎么设计?
2131 次点击
所在节点    Redis
29 条回复
xiaofan2
2023-06-05 17:57:35 +08:00
你们的 id 是 long 类型的吗?如果是 long 类型的话为什么要 hash ?
ben548
2023-06-05 17:59:44 +08:00
@xiaofan2 是 long 类型,目前设计不是用 hash ,考虑用 hash 是想着 100w 的 string key-value 数据内存占用应该是大于一个 100 万 key-value 的 hash 数据的(没做测试,只是靠印象和经验)
nicreve
2023-06-05 18:08:54 +08:00
Murmur 也没有这么吃性能吧,为什么不每次都实时计算而是要存在 Redis 里呢?如果单纯为了 ID 分布均匀,还有很多比 Murmur 更快的非标 Hash 算法,反正这个场景也不在乎 Hash 冲突。
matrix1010
2023-06-05 18:13:04 +08:00
value 是什么, 每个用户都不一样吗?
octobersnow
2023-06-05 18:16:06 +08:00
每次都 hash ,不用存 redis
ben548
2023-06-05 18:21:09 +08:00
@nicreve 因为需要记录是不是第一次生成,只有第一次分配桶,才触发同步数据部门,我需要记录这样一个是否已经分配过的状态,来判断是否需要同步分桶结果给到数据部门
ben548
2023-06-05 18:21:49 +08:00
@matrix1010 就是 0 ,1 ,2 ,3 这种数字,分到对应桶的用户都是一个 value
ben548
2023-06-05 18:21:56 +08:00
@octobersnow 因为需要记录是不是第一次生成,只有第一次分配桶,才触发同步数据部门,我需要记录这样一个是否已经分配过的状态,来判断是否需要同步分桶结果给到数据部门
nicreve
2023-06-05 18:24:25 +08:00
@ben548 问题是不是搞复杂了,你和数据部门的 Hash 及分桶规则保持一致不就可以了么,不需要进行同步啊。别告诉我你们的数据部门只会写 SQL ,这种基本的工程能力都没有?
ben548
2023-06-05 18:26:28 +08:00
@nicreve 目前是需要同步给他们的,我理解是这样会更好一些吧,因为这样数据部门不与业务绑定,相互隔离会是更好的设计吧,不然我们这边的规则改了,他们也要跟着一起改吗?很多时候没有及时通知容易出问题
seth19960929
2023-06-05 18:27:27 +08:00
上策: 让数据部分自己判断是否同步过
中策: 别存分组, 实时计算, 用个 bitmap 来记录是否同步过
下策: 代码能跑. 百万级你怕什么. 千万级的 hash 都见过
cloudzhou
2023-06-05 18:28:45 +08:00
在维护 key 数量和足够分散做一个均衡就好了:
1. murmurhash(uid) % 4 = 分桶信息
2. murmurhash(uid) % 1024 = 分桶信息存储的 key
awalkingman
2023-06-05 18:49:44 +08:00
key:user_id ,value:1|2|3|4. 不用 hash 或者别的容器,有记录就是分完的,没记录就是没分过的。就当数据库用好了,不是大 key ,就算几千万个 key 也能跑就是耗点空间而已。
删除是异步删的,但是删大 key 会影响性能(也就是阻塞)。
或者直接放 db ,根据主键查一个字段,也是 10ms 以内的时间。
ben548
2023-06-05 20:05:28 +08:00
@cloudzhou 这个差不多就是我的想法了,第二次做我应该会直接存 hash 里面,然后再做分片,晚点去写个测试案例,来试试是不是用 hash 存储会比用几十万个 string 存更省内存
ben548
2023-06-05 20:07:51 +08:00
@awalkingman big key 还是不太建议的,带来的不仅仅是 getall 的 O(n)操作导致的阻塞问题还有很多其他的问题,要用 hash 还是会考虑分片
ben548
2023-06-05 20:11:52 +08:00
@seth19960929
中策 bitmap 的方案有一个比较大的弊端,如果 userid 不是那种连续递增的类型,会导致大量空间浪费,目前我们的用户 id 是雪花算法生成的 18 位长 id ,我感觉不太适合用 bitmap 了
下策的方案 big key 还是不太建议的,带来的不仅仅是 getall 的 O(n)操作导致的阻塞问题还有很多其他的问题,要用 hash 还是会考虑分片
blessingsi
2023-06-05 21:02:29 +08:00
不管是 hash 还是 bitmap 都分片存就行了吧
worldOnlyYou
2023-06-05 23:07:11 +08:00
百万 key 应该还好,每个 key 也不大。换成 hash 的话,除了能节省点内存,如果后期有性能问题,不是很好解决(扩分片没有效果)
worldOnlyYou
2023-06-05 23:08:29 +08:00
定时删除是在主进程上完成,如果过多的 key 失效,redis 会有定时机制。印象中是最多 10ms?
xuanbg
2023-06-06 02:59:56 +08:00
取个模而已,为什么还要存起来?

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

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

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

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

© 2021 V2EX