一亿行的 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 了

5658 次点击
所在节点    程序员
46 条回复
huabalance
2021-02-19 11:37:45 +08:00
不断 rand 直到取慢三十个 可能还要去重
huabalance
2021-02-19 11:38:26 +08:00
我是说 php 生成随机数。。。
ahhui
2021-02-19 11:38:36 +08:00
随机生成 30 个数字,WHERE ID >= 你的某个随机数字 LIMIT 1,执行 30 次查询,这样呢?
honeycomb
2021-02-19 11:38:51 +08:00
另外建一个索引表,从那个表里取随机值?
leeyuzhe
2021-02-19 11:40:45 +08:00
@ahhui 要去重啊,跟等于实际上是一样的,不断去取然后去重直到取满
caola
2021-02-19 11:56:43 +08:00
```
SELECT * FROM XXX
WHERE id >= ((SELECT MAX(id) FROM XXX)-(SELECT MIN(id) FROM XXX)) * RAND() + (SELECT MIN(id) FROM XXX)
limit 30;
```

在网上抄来的
Rocketer
2021-02-19 11:59:04 +08:00
@caola 你这不是随机 30 个,而是自然排序取连续 30 个,只是起点随机而已
codingadog
2021-02-19 12:05:53 +08:00
我觉得只要 sql 里有 rand 就会慢。
还是维护个 set,保存生成的随机数,然后生成满 30 个随机数每次取一个最快。
bthulu
2021-02-19 12:09:38 +08:00
每次随机三十个数, 数字间大小至少相差十万万, 比如[5001, 235555, 1053052, ...], 然后查询 id>5001 limit 1 union id>23555 limit1 union id>1053052 limit1 ...
dafsic
2021-02-19 12:11:35 +08:00
随机生成 100 个数,然后 query,结果中大概率会有 30 个
caola
2021-02-19 12:11:51 +08:00
@Rocketer #7 如果一定要很随机的话,可以把所有的 ID 保存到 redis 并维护这个 ID 列表, 然后 srandmember 命令去取
rahuahua
2021-02-19 12:27:07 +08:00
@leeyuzhe 这个方式去重不是问题,问题是要读取三十次数据库
siweipancc
2021-02-19 14:00:35 +08:00
不上 redis 还有个折衷的方法,额外生成一列 hash,生成索引,取 30 行列值>=随机生成的 hash 的就行了
shyling
2021-02-19 14:13:28 +08:00
增加一个存随机数的列 a 加 index,查询的时候再生成一个随机数 b,找 a 在 b 附近的 30 条
Amayadream
2021-02-19 14:16:05 +08:00
一般这种不符合常理的需求都不是原始需求,可以说一下你的原始需求是什么。
aeli
2021-02-19 14:35:06 +08:00
取最大最小 id,然后计算出差值,再在这个差值中,取 60 个随机数后排序,select * from id in( ( a1> and < a2 )or( a3 > < a4) ....)... 大概的思路吧,取两个值之间的一个。
lovecy
2021-02-19 14:50:06 +08:00
假设只有 100 条数据
```sql
SELECT * FROM (SELECT * FROM xxx WHERE id > FLOOR(RAND()*100) LIMIT 1) AS n1
UNION
SELECT * FROM (SELECT * FROM xxx WHERE id > FLOOR(RAND()*100) LIMIT 1) AS n2
UNION
SELECT * FROM (SELECT * FROM xxx WHERE id > FLOOR(RAND()*100) LIMIT 1) AS n3
#写 30 遍,查出来有重复的再查一次
```
FLOOR(RAND()*100),这一步可以先在代码里算出来,整个方法前提是要知道 id 的最大范围
laminux29
2021-02-19 14:53:54 +08:00
这种问题明显应该用空间去换时间,比如在数据录入时,就概率性地取出某些行,直接作为最终数据。
xxxyh
2021-02-19 15:01:38 +08:00
order by rand 全表扫描,还排序,如果对随机性要求不是很高的话,可以在取最大和最小 id,在应用层随机取 30 个 id,然后 select * from table where id > (随机出来的 id) limit 1,重复 30 次
xxxyh
2021-02-19 15:08:44 +08:00
如果一定要非常精确的随机的话,可以 select count(*) from table,然后在应用层随机出来 30 个 id,把这 30 个 id 排序,从最小的 id@1 开始,select * from table limit id@1,1,从第二条开始 select * from table limit (id@2 - id@1),1

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

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

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

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

© 2021 V2EX