这里介绍了两种解决方案。这两个提议的解决方案都是 mysql-only,并且可以被任何编程语言作为消费者使用。 PHP 在这方面太慢了,但它可能是它的消费者。
更快的解决方案:我可以使用更高级的编程技术在大约十分之二秒内从 1900 万行的表中提取 1000 行。
较慢的解决方案:使用非功耗编程技术大约需要 15 秒。
顺便说一句,两者都使用我写的HERE 看到的数据生成。这就是我的小模式。我使用它,继续在那边看到 两个 自插入,直到我有 19M 行。所以我不会再展示了。但是要获得这 1900 万行,去看看,再做 2 次这样的插入,你就有了 1900 万行。
较慢的版本优先
首先,较慢的方法。
select id,thing from ratings order by rand() limit 1000;
这会在 15 秒内返回 1000 行。
对于刚接触 mysql 的任何人,甚至不要阅读以下内容。
更快的解决方案
这描述起来有点复杂。它的要点是您预先计算随机数并生成随机数的in clause 结尾,用逗号分隔,并用一对括号括起来。
它看起来像(1,2,3,4),但其中包含 1000 个数字。
然后您存储它们,并使用它们一次。就像密码学的一次性便笺簿。好吧,这不是一个很好的类比,但我希望你明白了。
将其视为 in 子句的结尾,并存储在 TEXT 列中(如 blob)。
为什么要这样做呢?因为 RNG(随机数生成器)速度非常慢。但是用几台机器生成它们可能能够相对快速地生产出数千个。顺便说一句(你会在我所谓的附录的结构中看到这一点,我记录了生成一行需要多长时间。使用 mysql 大约需要 1 秒。但是 C#、PHP、Java,任何东西都可以把它放在一起。重点不是你如何将它组合在一起,而是你想要它时拥有它。
这种策略,总而言之,当它与获取未用作随机列表的行相结合时,将其标记为已使用,并发出诸如
之类的调用
select id,thing from ratings where id in (a,b,c,d,e, ... )
in 子句中有 1000 个数字,不到半秒就可以得到结果。 有效地使用 mysql CBO(基于成本的优化器)而不是将其视为 PK 上的连接索引。
我将其保留为摘要形式,因为它在实践中有点复杂,但可能包含以下粒子
- 包含预先计算的随机数的表格(附录 A)
- 一个mysql创建事件策略(附录B)
- 使用 Prepared Statement(附录 C)的存储过程
- 一个 mysql-only 存储过程,用于演示 RNG
in 子句(附录 D)
附录 A
保存预先计算的随机数的表
create table randomsToUse
( -- create a table of 1000 random numbers to use
-- format will be like a long "(a,b,c,d,e, ...)" string
-- pre-computed random numbers, fetched upon needed for use
id int auto_increment primary key,
used int not null, -- 0 = not used yet, 1= used
dtStartCreate datetime not null, -- next two lines to eyeball time spent generating this row
dtEndCreate datetime not null,
dtUsed datetime null, -- when was it used
txtInString text not null -- here is your in clause ending like (a,b,c,d,e, ... )
-- this may only have about 5000 rows and garbage cleaned
-- so maybe choose one or two more indexes, such as composites
);
附录 B
为了不把它变成一本书,请参阅我的回答HERE,了解运行重复性 mysql 事件的机制。它将使用附录 D 中介绍的技术和您想要构想的其他想法来推动附录 A 中的表格的维护。例如重复使用行、归档、删除等等。
附录 C
存储过程让我简单地获得 1000 个随机行。
DROP PROCEDURE if exists showARandomChunk;
DELIMITER $$
CREATE PROCEDURE showARandomChunk
(
)
BEGIN
DECLARE i int;
DECLARE txtInClause text;
-- select now() into dtBegin;
select id,txtInString into i,txtInClause from randomsToUse where used=0 order by id limit 1;
-- select txtInClause as sOut; -- used for debugging
-- if I run this following statement, it is 19.9 seconds on my Dell laptop
-- with 19M rows
-- select * from ratings order by rand() limit 1000; -- 19 seconds
-- however, if I run the following "Prepared Statement", if takes 2 tenths of a second
-- for 1000 rows
set @s1=concat("select * from ratings where id in ",txtInClause);
PREPARE stmt1 FROM @s1;
EXECUTE stmt1; -- execute the puppy and give me 1000 rows
DEALLOCATE PREPARE stmt1;
END
$$
DELIMITER ;
附录 D
可以与附录 B 概念交织在一起。但是,您想这样做。但它让您了解 mysql 如何在 RNG 方面独自完成所有工作。顺便说一句,参数1和2分别为1000和19M,在我的机器上需要800毫秒。
这个例程可以用开头提到的任何语言编写。
drop procedure if exists createARandomInString;
DELIMITER $$
create procedure createARandomInString
( nHowMany int, -- how many numbers to you want
nMaxNum int -- max of any one number
)
BEGIN
DECLARE dtBegin datetime;
DECLARE dtEnd datetime;
DECLARE i int;
DECLARE txtInClause text;
select now() into dtBegin;
set i=1;
set txtInClause="(";
WHILE i<nHowMany DO
set txtInClause=concat(txtInClause,floor(rand()*nMaxNum)+1,", "); -- extra space good due to viewing in text editor
set i=i+1;
END WHILE;
set txtInClause=concat(txtInClause,floor(rand()*nMaxNum)+1,")");
-- select txtInClause as myOutput; -- used for debugging
select now() into dtEnd;
-- insert a row, that has not been used yet
insert randomsToUse(used,dtStartCreate,dtEndCreate,dtUsed,txtInString) values
(0,dtBegin,dtEnd,null,txtInClause);
END
$$
DELIMITER ;
如何调用上述存储过程:
call createARandomInString(1000,18000000);
生成并保存 1 行,包含如上所述的 1000 个数字。大数字,1 到 18M
作为一个简单的说明,如果要修改存储过程,请取消显示“用于调试”的底部附近的行,并将其作为最后一行,在运行的存储过程中,然后运行这个:
call createARandomInString(4,18000000);
...生成 4 个最大 18M 的随机数,结果可能如下所示
+-------------------------------------+
| myOutput |
+-------------------------------------+
| (2857561,5076608,16810360,14821977) |
+-------------------------------------+
附录 E
现实检查。这些都是一些先进的技术,我不能辅导任何人。但我还是想分享它们。但是我教不了。一遍又一遍。