MySQL随机抽取数据的性能问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL随机抽取数据的性能问题相关的知识,希望对你有一定的参考价值。

参考技术A

在随机抽取数据,并且数据量超过1万的时候,这种写法效率太低。
写法1:

按目前各种论坛博客查到的资料,换成这种可以成倍的提升效率
写法2:

1.当数据量小于1万时,写法1与写法2在效率上没有差异。
2.当数据量大到一定程度时,写法1的性能会迅速下降。40万订单数据,随机抽取5条耗时11s。同样条件下,写法2耗费时间0.03s。
3.写法2不适用于经过一定规则生成的id为主键的表,而且取出的 数据是连续的 ,所以只能一次查询一条。但是效率上依旧完胜于写法1。

从数据集中随机抽取一定数量的数据

今天翻到一个以前回答的问题:从列表中随机抽取一定数量的数据。之前回答是使用 Fisher-Yates 洗牌算法来解决的,但是阅读了评论之后,又有了一些新想法。

先不说是什么算法,只说说随机抽取的思路。

随机抽取的算法演进

假设有 n 个数据保存在一个列表 ​​source​​​ 中(在 JavaScript 中是数组),需要随机抽取 m (m <= n) 个数据出来,结果放在另一个列表 ​​result​​​ 中。由于随机抽取是一个重复过程,可以使用一个 ​​m​​​ 次的循环来完成,循环体中每次从 ​​source​​​ 中选一个数出来(找到它,并把它从 ​​source​​​ 中删除),依次放在 ​​result​​ 中。用 JavaScript 来描述就是

function randomSelect(source, m) 
const result = [];
for (let i = 0; i < m; i++)
const rIndex = ~~(Math.random() * source.length);
result.push(source[rIndex]);
source.splice(rIndex, 1);

return result;

在多数语言中,从列表中间删除一个数据,都会造成之后的数据重排,是个较低效率的操作。考虑到从一组数据中随机抽取一个是等概率事件,与数据所在的位置无关,我们可以把选出来的数据去掉之后,不减少列表长度,而是直接把列表最后一个数据挪过来。下一次随机取找位置的时候,不把最后一个元素考虑在内。这样改进之后的算法:

function randomSelect(source, m) 
const result = [];
for (let i = 0, n = source.length; i < m; i++, n--)
// ^^^^^^^^^^^^^^^^^ ^^^
const rIndex = ~~(Math.random() * n);
result.push(source[rIndex]);
source[rIndex] = source[n - 1];
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

return result;

注意到这里 ​​n--​​​ 和 ​​n - 1​​ 是可以合并的,合并后:

for (let i = 0, n = source.length; i < m; i++) 
...
source[rIndex] = source[--n];

这时候,再次注意到,​​source​​​ 后面没用的空间,其实是和 ​​result​​​ 空间一样大的,如果把这部分空间利用起来,就不再需要 ​​result​​。

function randomSelect(source, m) 
for (let i = 0, n = source.length; i < m; i++)
const rIndex = ~~(Math.random() * n);
--n;
// 交换选中位置的数据和当前最后一个位置的数据
[source[rIndex], source[n]] = [source[n], source[rIndex]];

// 把后面 m 个数据返回出来就是随机选中的
return source.slice(-m); // -m 和 source.length - m 等效

如果保留原来的 ​​result​​​ 及相关算法,会发现 ​​result​​ 和现在返回的数组元素正好是相反序排列的。但是不重要,因为我们的目的是随机选择,不管是否 revert,结果集都是随机的。

但是这么一来,假设 ​​m = source.length​​​,整个 ​​source​​ 中的数据都被随机排列了 —— 这就是 ​Fisher-Yates 算法​​。当然,实际上只需要进行 ​​source.length - 1​​ 次处理就可以达到完全洗牌的效果。

以上是关于MySQL随机抽取数据的性能问题的主要内容,如果未能解决你的问题,请参考以下文章

MySql主要性能指标说明

MySQL性能优化

MYSQL:随机抽取一条数据库记录

mysql随机抽取数据

locust性能压测连接mysql,随机取出班级,绑定学生

mysql order by rand() 性能问题及解决方案