慢 MySQL/MariaDB 亿条数据后插入

Slow MySQL/MariaDB insert after hundreds of million data

我正在从事一个需要我生成数十亿个唯一代码的项目。目前,我将 MariaDB 与 InnoDB 引擎和 python 一起用于生成随机唯一代码,每个生成周期插入一批 5000 个唯一代码。

我的table结构:

row_id int              --primary key + autoincrement
unique_code varchar(10) --unique

问题: 当我遇到 500.000.000 左右的唯一代码时,插入变得非常慢,而且我仍然需要生成多达 30 亿个代码。在创下这么多记录之前,我可以在几个小时内插入 300-4 亿个唯一代码。

任何帮助将不胜感激,谢谢!

更新(2019 年 1 月 22 日) 回答 Rick James' 解决方案。 以下是生成代码的一些示例:

RLXT$CPS1Y
Y4PK70WO
PKSTY9M$FR
T[=12=]VEFL2B1
RX4$MEKVQL

我的服务器有 32GB 的 RAM 和相对较快的 SAS 硬盘,我认为这足以满足我的需要(或者不是?)。

根据我的经验,TokuDB 的插入速度较慢,并且在达到 100m 记录之前很挣扎,所以我当时选择了 InnoDB。

至于我之前提到的事务:是的,一次插入5000条记录。直到 150m 的代码,它是如此之快,之后我注意到随着记录的增长,速度逐渐下降。现在我打了 800m 的代码,插入周期需要 10 到 15 秒(5000 个记录)。

我使用自动增量 ID 对记录进行排序和标记,因为这些代码将被传输到另一个数据库进行打印(生产)。所以我需要知道哪些代码已经转移,哪些没有。

我会等待进一步的答复,同时我会尝试。谢谢!

尝试MySQL INDEXES(如果您的服务器配置不是很好必须升级内存大小等)

向我们展示前 10 个值的样本。

这就是为什么您可能 "hit the wall"... 索引可以(在一个级别上)分为两种类型:

  • 连续,例如 AUTO_INCREMENT 值,或 TIMESTAMPs,您在其中按时间顺序或什至大致按时间顺序插入行。这些值被插入到 table 或索引的 "end" 处,并且只命中 BTree 的最后一个块(或几个块)。通过将所有 activity 都放在几个块中,几乎没有 I/O 可以执行。

  • 随机,例如 UUID、MD5 和其他 "random" 值,可能包括您的值。在这种情况下,要插入到 table/index 中的 'next' 值不太可能仍缓存在 RAM 中。所以需要I/O。虽然 table 不是太大,但所有索引块都可以保存在 RAM 中,因此需要很少的 I/O。但是在索引增长大于缓存之后,更多时候添加 'next' 值的行为将需要做 I/O。你的进程会越来越慢。

怎么办?

计划 A:在 插入所有行后添加 'random' 索引 。添加索引会很慢,但在 long 运行 中可能会更快,因为它可以使用不同的算法。

B 计划:不要预先创建所有值。相反,在需要时创建下一个。

C 计划:购买足够的 RAM 以在 RAM 中完全容纳 'random' 索引。 (计划拥有大约 2 倍的索引大小。)

计划D:你试过TokuDB?我希望它能在陷入严重麻烦之前存活更长时间。你的经历是怎样的。

您提到了交易。请详细说明。您的意思是每 5000 个代码在一笔交易中 INSERTed?这可能是最优的。

您的唯一编号使用什么字符集和排序规则?您可能应该使用 ascii 和 ascii_bin——为了提高速度并避免大小写折叠问题。

还有...这是关于如何生成它们的另一种想法。无需检查唯一性,因为它们将生成唯一性:

将 10 个字符的字符串想象成以 base-95 整数编码方式编码的数字。 (或者您允许的许多不同字符)。我们将按顺序生成数字,将它们转换为字符串,然后将它们随机化。

'next' 值计算为超过 'current' 值的随机值。随机值需要介于 1 和某个可能约为十亿的增量之间(这取决于您最终想要多少个数字、字符集等)

INSERT 批 5K(或其他)到没有索引的 MyISAM table。

完成后,执行此操作:

CREATE TABLE real (
    id ... AUTO_INCREMENT, -- do you really need this??
    random CHAR(10), NOT NULL CHARSET ascii COLLATE ascii_bin,
    PRIMARY KEY(id),   -- what for?
    INDEX(random)   -- uniqueness has been checked
INSERT INTO real (random)
    SELECT random FROM myisam_table
        ORDER BY RAND();

这是执行方式:

  1. 从本质上是平面文件(MyISAM table)中获取所有 'random' 字符串。
  2. 使用unix排序打乱它们。
  3. INSERT 它们进入 real table,创建顺序 ids

注意:这将创建一个巨大的撤消table,所以一定要有很多磁盘space。

至于我对放弃 idUNIQUE 等的评论,请提供有关您打算如何使用 real 的信息,以便我同意或反对, 他们的需要。

另一个计划

不要预先生成值。相反,从大约 14T 个可能值中生成一个新值,检查重复项,必要时生成另一个值。在这个计划中,table 会根据需要逐渐增长,而不是一开始就必须努力构建它。相反,只要需要一个新值,就会花费一点点努力(毫秒)。这可以包装在存储函数中,以方便用户使用。

table 将只有一列,unique_code CHAR(10) CHARSET ascii PRIMARY KEY