对 redis 的 zset 做分片的时候怎么选择分片个数 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ben548
V2EX    Redis

对 redis 的 zset 做分片的时候怎么选择分片个数

  •  
  •   ben548 2023-07-08 12:09:50 +08:00 2479 次点击
    这是一个创建于 832 天前的主题,其中的信息可能已经有所发展或是发生改变。
    背景:活动场景,用户可以订阅活动,订阅完会在活动开始前收到开始的通知,目前了解到如果是热门活动订阅人数可能超过百万,但是有些不怎么热门的活动可能只有数百人或者人数是 0 。
    现状:关注人数存在 redis 的 zset 中,每个活动有一个独立的 zset 。发送 push 消息通知的时候,是分页遍历 zset ,逐个发送通知。可能在关注数超百万的时候需要较长的处理时间(比如超过 5 分钟)。
    要求:希望在推送消息时尽可能的快,低延时。
    为了达到这个要求,选择对 zset 做分片,分片的规则是:按订阅用户的 userid 做 hash 取值,取值结果按分片数取模,在分配到相应的 zset 分片上。
    问题:zset 数据除了承担 push 消息通知的数据源之外,还用于查询用户是否订阅该活动的状态查询(这个没啥问题,找到对应的分片,然后查询 zscore 即可)和订阅总数计算的角色。如果我为了某些活动可能超过百万的关注订阅数,而设置 1024 个 zset 分片,是不是意味着绝大多数的活动,为了计算总数都要去做 1024 次 zcount 再将 result 相加才能得到结果?感觉也不是很好的设计。。。。(当然也可以总数单独维护,不通过 zset 分片结果相加获取,就是这样又增加了存储成本)
    大家觉得该怎么做比较合适呢?
    11 条回复    2023-07-08 18:42:55 +08:00
    icql
        1
    icql  
       2023-07-08 12:38:22 +08:00
    看场景为啥不考虑直接用 bitmap 呢,zset 这场景看着不大合适
    justseemore
        2
    justseemore  
       2023-07-08 13:03:24 +08:00
    在加个 bitmap 就好了. 反正都是个粗略的数字或者单独维护一个 key 来进行统计, 用 lua 脚本 zadd 或者 zdel 的时候 对这个 key 进行加或者减 .
    skyemin
        3
    skyemin  
       2023-07-08 13:06:30 +08:00
    一个 zset 里存储百万用户有可能变成 bigkey,分片存储又增加了聚合查询的成本,建议一个 key 一个 bitmap,更省空间,同时也满足用户是否订阅该活动的状态查询
    ben548
        4
    ben548  
    OP
       2023-07-08 13:40:56 +08:00
    @icql bitmap 只能查状态和总数吧,不能遍历关注用户。。。不能拿到关注的用户数据的话,push 怎么发呢?
    ben548
        5
    ben548  
    OP
       2023-07-08 13:42:49 +08:00
    @icql 而且 bitmap 如果存用户的 uid 的话,uid 是雪花算法生成的的 32 位的数字,用 bitmap 其实非常占内存,本质上用 bitmap 基本上就是为了省内存,不过感觉这种场景下也完全没有节省到内存,甚至比用 zset 占用更多
    fkdtz
        6
    fkdtz  
       2023-07-08 14:41:38 +08:00
    这是一个典型的大 key 热 key 场景,不管是否存在推送慢的情况,可能分片都是应该做的。
    楼主的思我感觉没啥问题,成本也是最低的。
    至于考虑到订阅计数器的单独维护问题,我觉得这点成本是应该的,毕竟你分片之后对于热门活动不可能在线上实时 zcount 1024 个分片吧,那也太慢了。
    尽量保持线上业务快速响应,很多活可以异步处理,比如这个计数器就可以异步执行,不需要实时增加,不影响线上业务响应。

    实际上我理解最好是特殊情况特殊处理,不需要一视同仁给所有活动都分片,可以动态地给大 key 做分片,不过一旦涉及到动态就会引入更多复杂的逻辑例如 rehash 或是引入哈希环等等,展开来就有更多东西可以讨论了,这个场景下有点舍本逐末就算了吧。

    另外我觉得这个场景用 bitmap 不太合适,用户量级在百万级,那么单个 bitmap 的大小至少要上 M ,也算是大 key 了,而且很多绝大部分的活动都是冷门的,意味着 bitmap 中绝大部分都是非常稀疏的,这么搞不太划算。
    我认知里 bitmap 更适合全体用户都参与的场景,例如是否活跃、是否在线等等吧。

    一些临时的想法可能有不太合适的地方,可以一起讨论
    ben123321
        7
    ben123321  
       2023-07-08 17:13:43 +08:00
    随便解答下,看看是否能满足条件:
    1. 增加一个大活动的 list ,用于存储哪些活动是大活动,只有进入了该 list ,才会走分片逻辑。这样其实已经保证了只有「非常小」一部分的活动需要做分片。
    2. 针对大活动的 list ,维护一个 count key 。
    3. 把大活动 list 推到 local cache 中,让判断损耗下降为 0 。
    4. 如何从小活动转换成大活动?异步任务写分片数据,然后再加入到大 list 中。
    5. 大活动如何变成小活动?不支持,因为这个概率实在太小。

    以上。
    dusu
        8
    dusu  
       2023-07-08 18:05:07 +08:00 via iPhone
    1. bitmap 也可以分片的
    每片 id 从 1 开始 分成若干个 bitmap
    拿出来的时候算下偏移就能拿到最终 id
    分片越多越省内存 但是 key 不好维护
    但是至少能解决内存太大问题
    2. 冷数据过期后从内存里剔除
    直接数据库里查归档结果就行了
    当数据穿透超过阈值就回 redis
    dusu
        9
    dusu  
       2023-07-08 18:06:30 +08:00 via iPhone
    @dusu redis 的 bitmap 可以 dump 出来自己解析的
    那个执行性能应该可以满足你要快速 scan 的效果
    xuanbg
        10
    xuanbg  
       2023-07-08 18:13:51 +08:00
    没明白活动为什么要用 zset ?
    icql
        11
    icql  
       2023-07-08 18:42:55 +08:00
    @ben548 #5 整个拿到能遍历,uid 这么大自带的确实占内存不过可以试试 roaringBitmap
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     922 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 22:28 PVG 06:28 LAX 15:28 JFK 18:28
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86