慢 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();
这是执行方式:
- 从本质上是平面文件(MyISAM table)中获取所有 'random' 字符串。
- 使用unix排序打乱它们。
INSERT
它们进入 real
table,创建顺序 ids
。
注意:这将创建一个巨大的撤消table,所以一定要有很多磁盘space。
至于我对放弃 id
、UNIQUE
等的评论,请提供有关您打算如何使用 real
的信息,以便我同意或反对, 他们的需要。
另一个计划
不要预先生成值。相反,从大约 14T 个可能值中生成一个新值,检查重复项,必要时生成另一个值。在这个计划中,table 会根据需要逐渐增长,而不是一开始就必须努力构建它。相反,只要需要一个新值,就会花费一点点努力(毫秒)。这可以包装在存储函数中,以方便用户使用。
table 将只有一列,unique_code CHAR(10) CHARSET ascii PRIMARY KEY
。
我正在从事一个需要我生成数十亿个唯一代码的项目。目前,我将 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();
这是执行方式:
- 从本质上是平面文件(MyISAM table)中获取所有 'random' 字符串。
- 使用unix排序打乱它们。
INSERT
它们进入real
table,创建顺序ids
。
注意:这将创建一个巨大的撤消table,所以一定要有很多磁盘space。
至于我对放弃 id
、UNIQUE
等的评论,请提供有关您打算如何使用 real
的信息,以便我同意或反对, 他们的需要。
另一个计划
不要预先生成值。相反,从大约 14T 个可能值中生成一个新值,检查重复项,必要时生成另一个值。在这个计划中,table 会根据需要逐渐增长,而不是一开始就必须努力构建它。相反,只要需要一个新值,就会花费一点点努力(毫秒)。这可以包装在存储函数中,以方便用户使用。
table 将只有一列,unique_code CHAR(10) CHARSET ascii PRIMARY KEY
。