【问题标题】:How can I increase insert speed?如何提高插入速度?
【发布时间】:2021-11-15 19:27:29
【问题描述】:

我需要将数据从外部 Web 服务导入我的 mySQL(5.7) 数据库。 问题是,我需要将数据拆分为表格。所以例如我有表格

CREATE TABLE a (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100)
);

CREATE TABLE b (
    id INT PRIMARY KEY AUTO_INCREMENT,
    a_id INT,
    name VARCHAR(100)
);

现在我必须在表 b 中为表 a 中的一行插入多行 (1:n) 由于我在插入之前不知道表a的id,唯一的方法是在表a中插入一行,获取最后一个id,然后将所有连接的条目插入到表b中。

但是,当我逐行插入时,我的数据库非常慢。在表a中插入大约35000行,在表b中插入120000行需要1h多。如果我在表 a 上批量插入大约 1000 行(仅用于测试而不填充表 b),它的速度令人难以置信(不到 3 分钟)

我想一定有一个解决方案可以加快导入速度。

感谢您的帮助

【问题讨论】:

  • 这个答案有两个部分。第一个涉及 LAST_INSERT_ID() ,第二个涉及跨越多行插入的事务。我找不到涵盖两者的副本,所以我会回答这个。
  • 什么是a_id INT?和id INT一样吗?
  • 将数据从外部网络服务导入我的mySQL(5.7) 数据库 以什么形式提供要导入的数据?纯文本文件? JSON? XML?不是文件,是别的吗?
  • b.a_id 是表a 的外键吗?如果是这样,为什么 FK 没有在结构中定义?

标签: mysql query-optimization etl bulk-load


【解决方案1】:

我假设您正在使用一种编程语言来驱动您的插入。您需要能够对这一系列操作进行编程。

首先,您需要使用此序列将一行放入a,将相关行放入b。它使用LAST_INSERT_ID() 来处理a_id。这比查询表以找到正确的 id 值更快、更健壮。

INSERT INTO a (name) VALUES ('Claus');
SET @a_id = LAST_INSERT_ID();
INSERT INTO b (a_id, name) VALUES (@a_id, 'von');
INSERT INTO b (a_id, name) VALUES (@a_id, 'Bönnhoff');

诀窍是捕获会话变量@a_id 中的a.id 值,然后为每个依赖的INSERT 重用它。 (我把你变成贵族来说明这一点,对不起:-)

第二,你应该记住这一点:INSERT 很便宜,但transaction COMMITs 很贵。这是因为 MySQL(实际上是 InnoDB)直到 COMMIT 才真正更新表。除非您明确管理事务,否则 DBMS 会使用一种称为“自动提交”的功能,它会立即提交每个 INSERT(或 UPDATE 或 DELETE)。

更少的交易让您获得更快的速度。因此,为了提高批量加载性能,您希望将 100 个左右的 INSERT 捆绑到一个事务中。 (确切的数字并不重要。)您可以这样做:

START TRANSACTION;   /* start an insertion bundle */

INSERT INTO a (name) VALUES ('Claus');
SET @a_id = LAST_INSERT_ID();
INSERT INTO b (a_id, name) VALUES (@a_id, 'von');
INSERT INTO b (a_id, name) VALUES (@a_id, 'Bönnhoff');

INSERT INTO a (name) VALUES ('Oliver');
SET @a_id = LAST_INSERT_ID();
INSERT INTO b (a_id, name) VALUES (@a_id, 'Jones');

... more INSERT operations ...

INSERT INTO a (name) VALUES ('Jeff');
SET @a_id = LAST_INSERT_ID();
INSERT INTO b (a_id, name) VALUES (@a_id, 'Atwood');
COMMIT;               /* commit the bundle */

START TRANSACTION;    /* start the next bundle */

INSERT INTO a (name) VALUES ('Joel');
SET @a_id = LAST_INSERT_ID();
INSERT INTO b (a_id, name) VALUES (@a_id, 'Spolsky');

... more INSERT operations ...

COMMIT;               /* finish the bundle */

(除了 LAST_INSERT_ID() 之外,所有这些都适用于任何基于 SQL 的 RDBMS。每个 RDBMS 都有自己处理 ID 的方式。(

【讨论】:

  • 感谢您的回答。我已经使用了最后一个插入 ID 功能,但我从未想过将所有插入都放入一个事务中。如果有帮助,我会尝试并报告。
  • 不要将所有插入到一个事务中!如此大小的事务会耗尽服务器的 RAM,并且会崩溃。批次!
  • 当然,我现在为一笔交易添加了 500 个条目,速度很好。非常感谢
猜你喜欢
  • 2016-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-30
  • 1970-01-01
  • 2019-03-04
  • 1970-01-01
  • 2011-10-20
相关资源
最近更新 更多