SELECT INTO OUTFILE 与 INSERT INTO ... SELECT

Mik*_*ell 6 mysql innodb locking isolation-level

在我粗略的研究过程中,我无法找到有关SELECT INTO OUTFILE超过INSERT INTO ... SELECT. 在阅读与InnoDB 表上的相关锁相关的文档时INSERT INTO ... SELECT它指出:

在插入到 T 的每一行上设置一个没有间隙锁的排他索引记录。 如果事务隔离级别为 READ COMMITTED 或启用了 innodb_locks_unsafe_for_binlog,并且事务隔离级别不是 SERIALIZABLE,则 InnoDB 将 S 上的搜索作为一致读(不锁)。否则,InnoDB 会在来自 S 的行上设置共享的 next-key 锁。

为了避免锁定INSERT INTO ... SELECT似乎我必须确保隔离级别是READ COMMITTED为了避免在查询过程中锁定源表。

但是,我无法找到任何有关锁和使用的权威答案SELECT INTO OUTFILE,甚至没有找到MySQL文档参考锁信息。

我的目标是避免在查询运行时锁定源表以避免连接堆叠。

Rol*_*DBA 5

您应该使用SELECT ... LOCK IN SHARE MODE。为什么 ?

SELECT ... LOCK IN SHARE MODE 在读取的任何行上设置共享模式锁。其他会话可以读取这些行,但在您的事务提交之前无法修改它们。如果这些行中的任何一行被另一个尚未提交的事务更改,您的查询将等到该事务结束,然后使用最新值。

在你的情况下,你可以尝试这个

START TRANSACTION;
SELECT ... LOCK IN SHARE MODE;
SELECT ... INTO OUTFILE;
ROLLBACK;
Run Code Online (Sandbox Code Playgroud)

这会做两个SELECT查询

  • 首先SELECT锁定你想要的表中的行
  • 第二次SELECT执行SELECT ... INTO OUTFILE

就我个人而言,我认为您不必如此严厉。事务隔离应该足够聪明,可以取消这个原子SELECT并为INSERT. 我知道我说过should be这就是你首先问你问题的原因。

无论您是SELECT ... INTO OUTFILE作为一个命令执行还是以我建议的严厉方式执行,源表的行数据都将完全可读。

试一试 !!!

更新 2014-12-10 15:12 美国东部时间

你的评论

感谢答案,它确实有帮助,但 OP 的主要观点是确定使用 SELECT INTO OUTFILE 优于 INSERT INTO ... SELECT 是否有好处?

它们在操作上是不同的

  • SELECT INTO OUTFILE 创建一个文本文件
  • INSERT INTO SELECT 从结果中加载一张表 SELECT

更新 2014-12-11 12:21 美国东部时间

在这种情况下,我唯一能想到的是数据的时间点以及您何时使用它。对于这两种类型的操作,都会有一些隐式共享锁定。

使用SELECT INTO OUTFILE,您正在准备结果并将其保存在外部。使用LOAD DATA INFILE将数据加载到表中不会在加载过程中涉及任何共享锁定。请记住,这SELECT INTO OUTFILE会导致磁盘 I/O,并且在此过程中仍会施加一些缓存。

使用INSERT INTO SELECT,共享锁可能必须在 InnoDB 中存活更长时间,因为您正在锁定行并使用这些相同的行 INSERT 到另一个表中。

因此,如果我正在寻找性能奖励,我会给予优势,INSERT INTO SELECT因为您正在执行相同数量的共享行锁定,单个操作的磁盘 I/O 必须小于单独的SELECT INTO OUTFILE和后续的LOAD DATA INFILE. 当然,您必须将这两种方法与您的数据集进行比较。一个数据集的性能奖励可能是另一个数据集的性能成本。

更新 2014-12-17 00:00 EST

你的评论

我没有收到通知你更新了你的答案,所以我做了赏金假设它不是。从理论上讲,您的解释确实有意义,但是我正在寻找更权威的回应,希望单独文件的开销(如您正确提到的)可能值得在复杂性上进行权衡以提高性能。

唯一的权威回应来自 MySQL 文档。

首先,MySQL 文档LOAD DATA INFILE说什么?

LOAD DATA INFILE 语句以非常高的速度将文本文件中的行读取到表中。LOAD DATA INFILE 是 SELECT ... INTO OUTFILE 的补充。(请参阅第 13.2.9.1 节,“SELECT ... INTO 语法”。)要将数据从表写入文件,请使用 SELECT ... INTO OUTFILE。要将文件读回表中,请使用 LOAD DATA INFILE。

两段后,它说

有关 INSERT 与 LOAD DATA INFILE 的效率以及加速 LOAD DATA INFILE 的更多信息,请参阅第 8.2.2.1 节,“INSERT 语句的速度”。

当您查看INSERT 语句的速度时,它说:

要优化插入速度,请将许多小操作合并为一个大操作。理想情况下,您建立一个连接,一次发送许多新行的数据,并将所有索引更新和一致性检查延迟到最后。

插入一行所需的时间由以下因素决定,其中数字表示大致比例:

连接: (3)

向服务器发送查询:(2)

解析查询:(2)

插入行:(1 × 行大小)

插入索引:(1 × 索引数)

闭幕式:(1)

这没有考虑打开表的初始开销,每个并发运行的查询都会执行一次。

假设 B 树索引,表的大小通过 log N 减慢了索引的插入速度。

您可以使用以下方法来加速插入:

如果您同时从同一客户端插入多行,请使用带有多个 VALUES 列表的 INSERT 语句一次插入多行。这比使用单独的单行 INSERT 语句快得多(在某些情况下快很多倍)。如果要将数据添加到非空表,则可以调整 bulk_insert_buffer_size 变量以加快数据插入速度。请参见第 5.1.4 节,“服务器系统变量”。

从文本文件加载表时,请使用 LOAD DATA INFILE。这通常比使用 INSERT 语句快 20 倍。见第 13.2.6 节,“LOAD DATA INFILE 语法”。

利用列具有默认值的事实。仅当要插入的值与默认值不同时才显式插入值。这减少了 MySQL 必须做的解析并提高了插入速度。

有关特定于 InnoDB 表的提示,请参阅第 8.5.4 节,“InnoDB 表的批量数据加载”。

有关特定于 MyISAM 表的提示,请参阅第 8.6.2 节“MyISAM 表的批量数据加载”。

此时事情开始变得有点模糊,因为您必须根据存储引擎调整加载过程。MyISAM 在此语句中相当直接,因为批量插入缓冲区仅用于 MyISAM,而 LOAD DATA INFILE 将利用批量插入缓冲区InnoDB 不会

看看这个 InnoDB 的图示(Percona CTO Vadim Tchachenko)

InnoDB 架构

调整选项还有其他考虑因素,LOAD DATA INFILE实际上会将所有内容都塞进 InnoDB 缓冲池,通过日志缓冲区、双写缓冲区、插入缓冲区(如果目标表具有非唯一索引)、重做日志(ib_logfile0、ib_logfile1)和表的物理文件。这就是 LOAD DATA INFILE 的好处必须取消的地方。

我写过这个

结语

正如我在之前对此答案的更新中已经说过的

因此,如果我正在寻找性能奖励,我会给予优势,INSERT INTO SELECT因为您正在执行相同数量的共享行锁定,单个操作的磁盘 I/O 必须小于单独的SELECT INTO OUTFILE和后续的LOAD DATA INFILE. 当然,您必须将这两种方法与您的数据集进行比较。一个数据集的性能奖励可能是另一个数据集的性能成本。

基本上,您必须SELECT INTO OUTFILE/LOAD DATA INFILE针对INSERT INTO SELECT. 一个数据集可能是 6 个,另一个可能是六个,另一个数据集可能是陆上胜利。

从 MySQL 文档和我过去的帖子中都可以看出,我仍然认为INSERT INTO SELECT. 您只需要测试这两种方法。