后端大佬请进!帮我看看这个排序方法可以行得通吗?

2022-03-18 16:41:54 +08:00
 userKamtao

兄弟萌!我这个排序方法可以行得通吗?

最近接到一个需求,技术栈 go+mysql 要给做个列表排序,一开始觉得很简单,直接加个排序号字段就 ORDER BY 好了 但是做一半我才发现,如果 100W 条数据 如果最后一条记录要排到第一位 排序号岂不是要整体记录都更新一次。

于是想了这个方案。 如图

5746 次点击
所在节点    程序员
58 条回复
hejw19970413
2022-03-18 16:44:30 +08:00
没明白你要干什么
userKamtao
2022-03-18 16:46:37 +08:00
@hejw19970413 有没有看到图 我刚刚图格式没调对,就是 mysql 列表排序
theoda
2022-03-18 16:47:06 +08:00
@hejw19970413 他的意思是在 1 前面直接插入 0.9 ,在 10001 前面插入 10000.5 ,就省得排序了
userKamtao
2022-03-18 16:48:01 +08:00
@theoda 正解,这样就不用全部都更新一次了
DarkFaith
2022-03-18 16:49:38 +08:00
记录分值,而不是记录序号,这样不需要排序。

另外 mysql 太慢,考虑 redis zset.
theoda
2022-03-18 16:59:51 +08:00
同意 DarkFaith 。不过百万数据量 mysql 通常还是够用的。
cpstar
2022-03-18 17:22:14 +08:00
这不就是吧 int 型的 id 变成了 float 型的 id 了么
MoYi123
2022-03-18 18:00:52 +08:00
float 有精度上限, 用着用着就会失效了.
cheng6563
2022-03-18 18:11:11 +08:00
精度有上限,用着用着还是得重排。。
haharich
2022-03-18 18:57:42 +08:00
业务会有 100w 这种大量数据的排序列表场景吗,且某个数据还可以任意排
haharich
2022-03-18 19:04:44 +08:00
用时间戳当作分值,代替序列号,默认时间戳降序查,最后一条排到最前就更新为当前时间戳即可,排到中间就获取后面一条的时间戳+1 之类的?
wd
2022-03-18 19:08:18 +08:00
@DarkFaith 他那个序号也可以理解为分值了。我感觉这需求好奇怪,这么大数据量非要指定顺序.. 不知道真实需求是啥,比较怀疑是 x-y problem 。
Inn0Vat10n
2022-03-18 19:08:42 +08:00
我们这有个线上系统就是这么做的,但就像上面说的精度是个问题,一段时间后可能需要重新刷序号
wd
2022-03-18 19:09:10 +08:00
如果你只是有有限的特殊数据,你也可以单独弄一个小表来记录这些特殊数据...
lujjjh
2022-03-18 19:09:49 +08:00
不知道是不是类似 teambition 拖动排序的业务场景: https://www.zhihu.com/question/55789722/answer/146650607

这种场景每个分组里要排序的项目不会特别多,如果你要排序的数据量级真有 100w ,那估计这套方案也没法满足。
oneisall8955
2022-03-18 19:15:01 +08:00
初始只有三个,A1 ,B2 ,C3
第一次:C 查到 AB 中间,于是 A1 ,C1.5 ,B2
第二次:B 插入 AC 中间,于是 A1 ,B1.25 ,C1.5
第三次:与此类推,A1 ,C1.125 ,B1.25
。。。。。
于是,N 次以后,BC 无限接近 1 ?
kzzhr
2022-03-18 19:56:13 +08:00
简单来说
有两个 index L 和 R ,现在有个 K 要插入到这俩中间,只需要 K=random(L,R) 就可以了
数值可以用 bigint ,基本不会出现相撞,出现的时候重排一下就行了
当然,最靠谱的还是单独存。。
zmxnv123
2022-03-18 20:04:20 +08:00
这种更新方式简直就是为双向链表专门设计的,对应到 MySQL 中就是存前后 item 的 id ,此时更新的时间复杂度是 O(1) 的。可惜在 MySQL 中没法范围查询。

读取的场景可以用楼主的方法把主键缓存到 redis ,排序的时候先查 redis ,然后反查 MySQL 。我理解这个值只是一个虚拟值,没有实际含义,所以丢失了也没啥问题,毕竟 MySQL 中有原始数据,等精度不够了,用 MySQL 的数据重新刷 Redis 。
xylophone21
2022-03-18 20:15:02 +08:00
每次插入的输入是什么?
比如现在是 A ,B,C ,C 差到 AB 中间。似乎输入只能是(A,C)?

那么数据结构里最适合插入的就是链表,每行存一个 next 字段,每次插入只需要插一次改一次。

链表最大的缺陷是找到要插入的这个点很慢,但通过索引数据库帮你解决了这个问题,所以应该不需要更复杂的数据结构了。
xylophone21
2022-03-18 20:20:53 +08:00
比如现在是 A ,B,C ,C 差到 AB 中间。似乎输入只能是(A,C)?
--》
比如现在是 A ,B,C ,D 差到 AB 中间。似乎输入只能是(A,D)?

当然,这个方案只能快速插入,不能输出序号,但你提的浮点数的方案也不能输出序号。这个需求目前不太明确。

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

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

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

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

© 2021 V2EX