我试图将大量用户插入带有两个表的MySQL数据库中:
第一个表包含用户数据。示例INSERT
如下所示(id
是主键,mail
是唯一键):
INSERT INTO users (id, mail, name)
VALUES (NULL, "foo@bar.tld", "John Smith")
ON DUPLICATE KEY UPDATE name = VALUE(name)
第二个表包含用户所属的组。它只存储两个外键users_id
和groups_id
。示例查询如下所示:
INSERT INTO users_groups (users_id, groups_id)
VALUES (LAST_INSERT_ID(), 1)
此设置适用于小型数据集。当我导入大量数据(> 1M行)时,INSERT
会变慢。显然,进行批量插入会更好:
INSERT INTO users (id, mail, name)
VALUES (NULL, "foo@bar.tld", "John Smith"), (NULL, "baz@qux.tld", "Anna Smith")
ON DUPLICATE KEY UPDATE name = VALUE(name)
和
INSERT INTO users_groups (users_id, groups_id)
VALUES (LAST_INSERT_ID(), 1), (LAST_INSERT_ID(), 4)
问题当然是,LAST_INSERT_ID()
只返回批INSERT
的一个(第一个)ID。
所以,我需要的是一个“嵌套”批处理INSERT
,在MySQL中不存在IMO。
如何才能让INSERT
更快?
答案 0 :(得分:5)
默认情况下,批量插入提供顺序自动增量,有了这些知识,您可以像<; p>那样进行插入
INSERT INTO users (id, mail, name)
VALUES (NULL, "foo@bar.tld", "John Smith"),
(NULL, "baz@qux.tld", "Anna Smith"),
(...) # repeat n-times
;
SET @LASTID=LAST_INSERT_ID()
;
INSERT INTO users_groups (users_id, groups_id)
VALUES (@LASTID - n , 1), # Note n in descending sequence
(@LASTID - n-1, 1),
...
(@LASTID - 1 , 1),
(@LASTID - 0 , 4)
;
有关批量插入和自动增量的详细信息,请查看http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html
重要的是,请确保innodb_autoinc_lock_mode = 1
show global variables like 'innodb_autoinc_lock_mode'
否则请考虑将您的插页包装在LOCK TABLES
LOCK TABLES tbl_name WRITE
... sqls ...
UNLOCK TABLES
答案 1 :(得分:1)
如果您同时将数百万条已知行放入表中,请考虑使用LOAD DATA INFILE
,因为它仅适用于该类型的方案,如the docs引用所示:
LOAD DATA INFILE
语句将文本文件中的行读取到 桌子的速度非常快。
从文本文件加载表格时,请使用
LOAD DATA INFILE
。这是 通常比使用INSERT
语句快20倍。
这假设您的源数据来自或可以作为文本文件提供。如果您在文件中也有组ID,则可以执行以下操作:
CREATE TEMPORARY TABLE load_users_groups (
mail VARCHAR(60),
name VARCHAR(60),
groupid INT,
PRIMARY KEY (mail, name)
);
LOAD DATA INFILE '/path/to/file.csv'
INTO TABLE load_users_groups
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'; -- use whatever optional syntax required to parse your file
INSERT INTO users (mail, name)
SELECT mail, name FROM load_users_groups
ON DUPLICATE KEY UPDATE name = VALUES(name);
INSERT INTO users_groups (users_id, groups_id)
SELECT users.id, load_users_groups.groupid
FROM users JOIN load_users_groups USING (mail, name);
DROP TEMPORARY TABLE load_users_groups;
此方法最终是否比当前方法更快取决于您使用LOAD DATA INFILE
节省的时间是否比执行另外两个INSERT ... SELECT
语句以将数据移动到所需表中所花费的时间更长。您可能想要调整临时表上的键;我不能仅根据你问题的内容为你做基准测试。不过,我很想知道它是如何运作的。
该文档还提供了相当多的Bulk Data Loading for InnoDB Tables和Bulk Data Loading for MyISAM Tables提示。我不会详细介绍它们,尤其是因为您没有向我们提供任何DDL或服务器信息,但您可能会发现在您自己的时间阅读其中一个或另一个很有帮助。
答案 2 :(得分:0)
不得不处理类似的问题。
MySQL并没有真正提供很多方法来为此目的可靠地保留大批量的表ID。我花了半天时间研究无济于事。有一些黑客四处乱窜,但没有任何内容可以保存我的数据。
我只是使用逐个插入的用户表(比screwy更慢)并将新行的id返回给我的ORM。我有一个行ID可供使用,因此我可以将它和应该导入JSON的数据一起保存在一起。这使我更容易批量插入并保持数据匹配。
最佳。
答案 3 :(得分:-1)