【问题标题】:generate 16M unique random numbers生成 16M 唯一随机数
【发布时间】:2012-06-13 11:43:26
【问题描述】:

我正在尝试生成 16 000 000 个唯一随机数(10 位:范围 1 000 000 000 - 9 999 999 999)并将它们插入一个空表(如果不为空,则填满此表)。

桌子:

CREATE TABLE `codes` (
`code_id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`code` bigint(20) UNSIGNED NOT NULL,
`is_used` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`code_id`),
UNIQUE KEY `code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

...和功能:

DELIMITER $$

CREATE DEFINER=`root`@`localhost` FUNCTION `codes`(`minRange` BIGINT UNSIGNED, `maxRange` BIGINT UNSIGNED, `_amount` BIGINT UNSIGNED) RETURNS tinyint(1)
MODIFIES SQL DATA
BEGIN
DECLARE pick BIGINT;


while (SELECT count(*) FROM codes) < _amount do
begin
SET pick = minRange + FLOOR(RAND() * (maxRange - minRange + 1));
INSERT IGNORE INTO codes (code) VALUES (pick);

end;
end while;

RETURN 1;
END$$

DELIMITER ;

-- call: SELECT codes(1000000000,9999999999,16000000);

这个函数非常慢:生成 20k 行需要 2.5 分钟,所以 16M - 大约 33 小时...... 有什么办法可以优化吗?

【问题讨论】:

  • 实际上,表中的记录越多,插入的速度就越慢,因为随着表填满,您将逐渐遇到更多的冲突,因此您对 33 小时的线性估计过于乐观。
  • 你说得对,它非常乐观。 lanzz 提到的更快的解决方案需要 0.9 秒和 20k 条记录。所以 16M - 15 分钟,但最终需要 3 小时。
  • 您应该查看事务 - 开始事务、循环并插入,然后在最后提交或每 400 万行左右提交一次。此外,您应该避免执行 select count(*) 并使用变量作为计数器,例如而我 dev.mysql.com/doc/refman/5.0/en/commit.html

标签: mysql performance


【解决方案1】:

您要的是矛盾修饰法。如果这 16M 个数字是唯一的,那么它们就不是随机的。想一想:一个真正的 10 位随机数有 1/9E9 的概率是任何给定的数字。然而,您的第 16M 号数字有 0 概率是 15,999,999 个数字之一,有 1/983,000,001 概率是其余数字之一。你应该总是期待重复。 1B 中的 16M 大约占 1%。

所以我的建议是生成(16M +一点)随机数,做一个唯一的排序;截断到 16M 然后随机排序。我的问答:

php -r 'for( $i=0;$i<16500000;$i++) echo mt_rand(100000000, 999999999),"\n";'\
  | sort -u | head -16000000 | sort -R > /tmp/loadfile.lst

在我用了 4 年的笔记本电脑上花了 7 多分钟。在现代四核桌面上,它会更快很多。我使用 PHP-CLI 是因为我的笔记本电脑上有它,而且 mt_rand 例程是一个很好的例程。您可以将其作为 3 liner-C prog 或任何可用的语言来执行。正如 Zercms 所说,只要在加载之前禁用索引并在之后重新启用,加载就会很快。您也只需要加载列code

享受吧。

【讨论】:

  • 谢谢!这是我认为最快的解决方案。
  • 如果是这样,那么你就勾选答案 ;-)
【解决方案2】:

我能想到的最快方法是生成具有唯一编号的纯文本文件并使用LOAD DATA INFILE

您还需要在开始加载数据之前使用 ALTER TABLE 禁用键,然后再启用

【讨论】:

  • 谢谢,看起来很有趣。我稍后再试。
【解决方案3】:

您正在为插入的每个随机数运行SELECT count(*)。一个简单的优化是尝试在不检查的情况下填充 16000000 个随机数,然后查看实际插入了多少个,从头开始重复要添加的剩余数字的数量。

【讨论】:

  • 谢谢,这是现在正在运行的解决方案。插入 16M 条唯一记录需要 3 个小时...更好,但并不完美 :) 我稍后会在没有索引的情况下尝试。
【解决方案4】:

如果您安装了 PHP,也许您应该尝试不通过 MySQL 而是通过 PHP 来生成您的号码。 PHP mt_rand() 确实生成了更好的随机值,您可以将值填充到数组中,并通过 in_array() 直接检查它是否已经存在。

另外,你的 MySQL 会因为每次都更新表索引而减慢了很多,如果你需要在 MySQL 中进行,也许你应该在生成数字时禁用它。

【讨论】:

  • in_array 和里面的 16M 值...需要大量内存。我不知道有多少,但256M是不够的。
  • link => 每个值占用 68 字节,结果为 68*16.000.000 = 1.088.000.000 字节 => 1.062.500 KB => 1037,59765625 MB。这就是该数组所需的实际内存量。我不知道你的机器上是否有足够的空间来设置它,比如说 2048M 或更好的 4096M,但我确信这 1600 万的生成速度将比任何仅 MySQL 的解决方案快得多。
猜你喜欢
  • 2020-05-27
  • 1970-01-01
  • 1970-01-01
  • 2018-05-03
  • 2017-09-24
  • 2013-03-13
  • 2014-04-26
相关资源
最近更新 更多