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

328 天前
 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 进程吗?

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

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

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

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

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

© 2021 V2EX