假设系统有 N 万个用户,每个用户都有一个 score 。这个 score 经常发生变化。
需要完成以下功能:
现在没有太好的思路,现在想的是
假设数据存在 MySQL 里面的
create table user (
id bigint not null primary key,
name varchar(255) not null,
score int not null
);
create table user_relation (
user1_id bigint not null,
user2_id bigint not null,
primary key(user1_id, user2_id)
);
1
siweipancc 2020-09-09 18:30:36 +08:00 via iPhone
mysql 可以用内存数据库,等个最优解
|
2
qwerthhusn OP @siweipancc 这东西要持久化的,内存数据库肯定不行
|
3
siweipancc 2020-09-09 18:48:14 +08:00 via iPhone
@qwerthhusn 可以晚点同步。我指的是开另一个内存中间表,在一个活动结算周期切换的时候再写入实体表
|
4
netty 2020-09-09 19:09:08 +08:00 via Android 1
用 Redis 的 SortedSet 轻松搞定,key 排行榜名称,member 用户 ID,score 为用于排行的值
|
5
ffLoveJava 2020-09-09 19:13:48 +08:00
等一个大佬
用户少的话无所谓。我想知道大佬对于超量用户量有什么解决方案? |
6
qwerthhusn OP @netty 这个道理都明白,但是数据库和 redis 的同步咋做呢?
首先 redis 不能事务,可能就存在 redis 与真实数据不一致的地方。 我能想到的就是定时全量刷新了,不知道这样弄合理不合理,全量刷新间隔时间也不好定 |
7
qwerthhusn OP @ffLoveJava 期望来个大厂做过类似场景的的大佬,分享一下 Best Practice 呢。。
|
8
smartwusir007 2020-09-09 19:15:44 +08:00
@netty 这样总用户排行有了,怎么做每个用户的好友排行呢
|
9
xiaoliu926 2020-09-09 19:18:26 +08:00 1
@smartwusir007 用户好友排行前端来实现就行了,只需要把用户好友数据丢给前端,前端自己排序
|
10
qwerthhusn OP @smartwusir007 其实我突然一想,好友那个反而好弄一点,首先拿到好友 ID 列表,然后直接数据库或者 ZSCORE 读取好友们的 score 信息,直接应用排序。毕竟一个人的好友不会太多,
|
11
optional 2020-09-09 19:34:32 +08:00 via iPhone
总排行榜和我的位置不是实时的,只要根据分数估算排行就行。
|
12
limbo0 2020-09-09 19:48:58 +08:00 via Android
海量数据可以用图数据库,技术 janusgraph,保存关系和分数应该问题不大
|
13
LostPrayers 2020-09-09 19:49:18 +08:00
简单实现就是 4L 说的 SortedSet,不能事务没有关系, 排行是通过 score 自动计算的, zrank 即可取到 index.
好友 score 排行榜, 就取出所有好友 id 对应的 zrank 再排个序 |
14
firefox12 2020-09-09 20:04:46 +08:00
总排行 定期算,拿出 1 个数组 ,长度为 n, 比如 0-10000 最多 10000 点, 然后统计的时候 把你的分数-> index, 在 index 上加 1
最终结果 就是 0 分的 100 代表 100 人 0 分,1 分 200 代表 200 人 1 分。 从 n->0 开始 统计, 把结果写在数组里, 比如 n 里面 10 个人,n-1 里面 100 人, 那么 n 就是 10, n-1 就是 110, 这个 x 的值,就是 n 到 x+1 的和,这个数组的坐标里的值就是 你的排名。 最后拿你的分数 直接在这张表里查 你就知道 你多少名了。 |
15
Rheinmetal 2020-09-09 20:07:07 +08:00
十分钟刷新一次排名可破
|
16
kusya 2020-09-09 21:33:00 +08:00 via Android
数据如果不要求实时性,可以按照 score 索引查询。数据太多还可以分表。
|
17
hejw19970413 2020-09-09 21:55:54 +08:00
@Rheinmetal 支付宝几亿的用户,怎么处理?
|
18
zhgg0 2020-09-09 22:17:20 +08:00
可以利用桶排序,记录每个分数段的用户数量,根据当前用户的分数能直接算出大概排名。
最高分数段的用户特殊处理,可以计算他们的准确排序。 用户好友数一般不多,直接排就好了,可以把好友的分数缓存到当前用户里并且不用实时更新,只需要实时更新自己的分数就行了。 如果分数跨度不大的话,假设分数只有 0 到 1000 这种,直接记录每个分数有多少用户,这种情况可以 O(1)算出每个人的准确排名。 |
19
zxCoder 2020-09-09 22:38:39 +08:00
收藏
|
20
yeqizhang 2020-09-09 22:53:50 +08:00 via Android
赞同楼上有人说的把用户的好友 ID 去查出来排序就行了。总排名位置这东西实时性应该不高而且是个大概的位置
|
21
proger 2020-09-10 00:28:44 +08:00
粗浅一点想的话,我感觉一群人分数打架,排出一个总排名表存在一个表内,每当有人分数达到表内到最低分数时,去做表内数据的替换。
这样就变成用户主动去踢榜,可能会比较轻松点。 显示的话,直接显示这个表给所有用户就好了,比较不会需要显示几千上万条数据给用户看,盲猜是百来条的样子。 |
22
Olament 2020-09-10 03:13:32 +08:00
一个大小为 N 的最小堆,每当有人的分数达到最小堆顶用户的分数时,弹出最小堆的一个元素,插入这个新用户到最小堆中
|
24
cassyfar 2020-09-10 04:18:11 +08:00
我个人以前做过推荐,每个商品有一个 score 。当时是线下计算,因为 score 变化不大,每一小时算一次就足够了。
如果 score 变化大而且你需要特别精确的话,可以参考这个: https://aws.amazon.com/blogs/database/building-a-real-time-gaming-leaderboard-with-amazon-elasticache-for-redis/ |
25
xuanbg 2020-09-10 08:56:21 +08:00
凡是排行,统统 Redis 。用一个 ZSet 就行了
|
26
SeanLari 2020-09-10 09:35:54 +08:00
这个东西,和游戏的技能使用技能冷却一个意思吧?也就是时间轮?(我不懂我瞎说的
|
27
SmiteChow 2020-09-10 09:59:20 +08:00
分数和用户绑定存 mysql,榜单存 redis,定时打榜即可
|
28
zdt3476 2020-09-10 09:59:48 +08:00
第 1 点和第三点,redis zset 轻松搞定。至于同步问题也不用担心。更新 mysql 数据的时候同步更新 redis 数据就行了。排行榜这东西本来就不会要求很高的一致性,而且你说的才 N 万用户,那就更不会有问题了。 至于第二点,返回好友列表的 score 给前端,让前端排序。
|
30
promise2mm 2020-09-10 10:05:49 +08:00
Redis: 好友排行应该一个 Zset 管够,全部用户的话,考虑数量问题,我的想法是先来个 hash,存 n 个分段的 Zset 的索引 key
比如:key1 -> 1~100 分:Zset1 key2 -> 101~200 分:Zset2 keyN -> X~Y 分:ZsetN 当然,按场景可以调整或者冗余分组的策略,比如按城市(如果需要城市排名的话) |
32
qiyuey 2020-09-10 10:17:59 +08:00
推、拉的区别,这种一般拉就行
|
33
Citrullus 2020-09-10 10:19:01 +08:00
等一个最优解决方案
|
34
676529483 2020-09-10 10:19:47 +08:00
1:大顶堆,每隔一段时间同步下就行
2:因为查询频率不会太高,觉得直接查数据库+缓存就行了 3:跳表,或者直接把 1 的堆分多个,只用找出大概位置就行,比如 5k+ 在更新内存数据结构的同时,同步更新数据库 以上瞎说,期待大厂兄弟 |
36
sdushn 2020-09-10 11:17:14 +08:00
@xiaoliu926 我想到的也是这样,前端做这个更合适,之前遇到一个类似场景,后端推来所有好友数据,前端根据某个字段排序,毕竟这个场景下,可以接受实时性比较差,如果要实时性强的话可能要考虑其他方案了
|
37
pepesii 2020-09-10 14:24:11 +08:00
@limbo0 #12 我感觉也是图数据库呢,蚂蚁金服用的是自研的数据库应该是 ocean base,好像有个 gae base
|
40
wangyzj 2020-09-10 14:33:25 +08:00
redis zset
|
41
yangyaofei 2020-09-10 15:02:43 +08:00
同意 #14 楼的思路 但是这个其实是存储 n * 10^4 区间中的个数,就可以接近实时的了,然后每次直接算每个区间的计数之和和对应的小区间的排名就好了.
记得最早看见这个问题还是好久之前迅雷算积分有离线下载的时候,大体意思就是算某个迅雷用户的积分的排名 |