一亿行的 MySQL 表,主键 ID 不连续,怎样随机取 30 行速度最快?

2021-02-19 11:35:17 +08:00
 kisshere

ORDER BY rand()速度太慢,主键又不连续(类似于 1,6,7,33,78,93 这种递增),要不然都可以用 php 生成 30 个随机数 id 去 query 了

5675 次点击
所在节点    程序员
46 条回复
xxxyh
2021-02-19 15:11:53 +08:00
如果一定要非常精确的随机的话,可以 select count(*) from table,然后在应用层随机出来 30 个 id,把这 30 个 id 排序,从最小的 id@1 开始,select * from table limit id@1,1,从第二条开始 select * from table where id > (第一次取出来的 id) limit (id@2 - id@1),1
eastphoton
2021-02-19 15:30:51 +08:00
既然主键 ID 没有更多规律,总得查一下或者干脆拿到所有 ID 才能得知具体某个 ID 存不存在这个信息了。。

所有 ID 单独维护一份感觉比较合适,上 redis 或者自己维护个 set 。

要么就用一些不那么随机的方案。
hannibalm
2021-02-19 15:47:11 +08:00
再建一个临时表,一列现有主键,一列从 1 开始的序号。然后随机抽 30 个。
GTim
2021-02-19 16:51:08 +08:00
@dafsic 这个目前是最优,不过 2 倍左右就可以了,如果不够再随机 2 倍去取
bertonzh
2021-02-19 16:54:25 +08:00
先加一个自增字段进去...
zlowly
2021-02-19 16:58:31 +08:00
mariadb.com 的 Knowledge Base 知识库上有一篇专门讲这个问题的文章,里面提到一些较快但没那么随机的方法,你可以搜索来参考下
data-sampling-techniques-for-efficiently-finding-a-random-row
stevenkang
2021-02-19 17:14:23 +08:00
数据库不是用来存取数据的吗,用来做这些复杂的活干嘛。

直接程序随机生成 ID 呗,如 #3 所说取满 30 个就行了。
yixiugegegege
2021-02-19 17:20:31 +08:00
SELECT *
FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2
WHERE t1.id >= t2.id
ORDER BY t1.id LIMIT 30;
Still4
2021-02-19 17:39:06 +08:00
我觉得直接说业务吧,啥需求要一亿行里面去随机啊,上面说加自增字段也好,加表也好都是空间换时间,根源上如果需求可以变通的话根本就不需要这么做
ivanMeng
2021-02-19 17:42:33 +08:00
布隆过滤器吧,可以快速判断 key 是否存在,id 乱序的 mysql 聚族索引也没发挥出,又是随机的记录地址完全不相邻,更加缓慢,还有这问题本身就是问题 。1 亿的表 是否有读写分离?上层是否有缓存冷热数据分离?
mosliu
2021-02-19 17:47:23 +08:00
看你的数据 id 有多稀疏
不是太稀疏的话
先获取 3*稀疏倍数*2 个随机数 记为 idlist1
然后按 id in idlist1 limit 30 取 架设取出 n 个
然后不够的 再按缺少数量 取随机数 rand 循环( 30-n )次 {取 id>rand and not in (已取) limit 1}
akira
2021-02-19 17:48:28 +08:00
同意 25 楼的, 加个连续的自增字段,立马简单起来了
love2020
2021-02-19 18:00:12 +08:00
众所周知,随机数是一个难点
fox0001
2021-02-19 19:19:00 +08:00
@bertonzh #25
@akira #32
如果是静态数据,这是最简单的方法。然而楼主丢下问题就没有再回复了
xcstream
2021-02-19 19:27:26 +08:00
最快当然不用 mysql 直接写程序用偏移量读取数据文件
ipwx
2021-02-19 19:34:05 +08:00
说实话,如果原始 id 不是均匀分布的,那么 id >= RAND() 这种操作去取数字,得出来的结果也不是均匀分布的。

很简单,举个例子:原始 id 是 1, 5, 6, 7 。那么用 id >= min(id) + rand() * (max(id) - min(id)) limit 1 取数字,每个数字被选取的概率为:

1 = 4/7
5 = 1/7
6 = 1/7
7 = 1/7
ipwx
2021-02-19 19:34:42 +08:00
生成第二列连续的 id 然后去取,就不会产生这种不均匀分布的问题了。
ipwx
2021-02-19 19:36:16 +08:00
1 亿 id 的 int 数组在内存中也就几个 gb 的事情。说实话,单独维护一个索引文件大概都容易搞得定。
stevefan1999
2021-02-19 19:39:52 +08:00
你要先定義什麼是隨機
譬如我可以定義 generateRandomNumber() { return 4 }
然後按照這個 random number 做 seed 開一個 LNG 然後生成一個次序...
因爲隨機不代表不能重複啊 不過如果要去重那更加要命了
xumng123
2021-02-19 19:41:26 +08:00
将 id 放在一个数组里,随机取 39 个后,然后去查一下数据库。

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

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

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

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

© 2021 V2EX