MySQL Cron 作业中的事务
MySQL Transaction in Cron job
我的 Ubuntu 服务器上有一个 PHP DAEMON,用于将大量数据插入到 InnoDB 中。使用该平台的人也在使用完全相同的 table。
当非 运行 处于 TRANSACTION 模式时,DAEMON 使用大约 60-70 秒进行 100.000 次插入。当 运行 处于 TRANSACTION 模式时,BEGIN .... COMMIT 使用 15-20 秒。
然而,TRANSACTION 模式是否会锁定 tables,并阻止用户在 DAEMON TRANSACTION 正在执行时使用该平台进行插入?
锁定用户正在操作的 tables 超过 20 秒当然是不可取的:)
好吧,我正在以 500 和 500 的批次进行插入,即一个 FOR 循环 INSERT INTO (col1, col2) VALUES (a,b) 等。这很好,运行平稳,但是我能够如果我在循环之前发出 BEGIN 并在循环之后发出 COMMIT ,则可以显着加快进程,但这意味着 BEGIN/COMMIT 之间的时间超过 60 秒。但是当系统进行几十万次插入时,使用该平台的人可以对相同的 table 进行插入。系统会为用户插入生成插入帐户,还是用户必须等待 XX 秒才能处理插入?
根据您的描述,您使用启用了默认 autocommit 模式的 innodb,并在循环中一条一条地插入记录。自动提交模式意味着每个插入都被封装到它自己的事务中,这很好,但是非常慢,因为每个记录都单独保存到磁盘中。
如果您将在 begin
- commit
语句中插入记录的循环包装起来,则所有插入都是 运行 在单个事务中并且仅在磁盘中持久化一次,当commit
已发布 - 这就是您体验速度提升的原因。
无论你以何种方式插入记录,innodb都会使用锁。然而,innodb only locks the record being inserted:
INSERT sets an exclusive lock on the inserted row. This lock is an
index-record lock, not a next-key lock (that is, there is no gap lock)
and does not prevent other sessions from inserting into the gap before
the inserted row.
Prior to inserting the row, a type of gap lock called an insert
intention gap lock is set. This lock signals the intent to insert in
such a way that multiple transactions inserting into the same index
gap need not wait for each other if they are not inserting at the same
position within the gap. Suppose that there are index records with
values of 4 and 7. Separate transactions that attempt to insert values
of 5 and 6 each lock the gap between 4 and 7 with insert intention
locks prior to obtaining the exclusive lock on the inserted row, but
do not block each other because the rows are nonconflicting.
这意味着,打开一个只插入记录的事务较长时间不会干扰其他用户将记录插入到同一个 table。
请注意,在循环中发出单个插入语句是将大量数据插入 MySQL.
中效率最低的方法
要么使用 bulk insert (build a single insert statement in the loop and execute it after the loop, paying attention to max_allowed_packet 设置:
INSERT statements that use VALUES syntax can insert multiple rows. To
do this, include multiple lists of column values, each enclosed within
parentheses and separated by commas. Example:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
或使用load data infile语句。
这两种解决方案都可以显着加快数据插入速度,并且不会造成table锁定。
计划 A:LOAD DATA
。缺点:这需要将数据写入文件。如果它已经在文件中,那么这是最好的方法。
B 计划:"Batched INSERTs
" -- 构建 INSERT INTO t (a,b) VALUES (1,2), (3,4), ...
并执行它们。以 100-1000 个为一批进行。这将比 BEGIN
..COMMIT
在很多 1 行 INSERTs
周围更快。有autocommit=ON
。 Locking/blocking 将是最小的,因为每个 'transaction' 将只有 100-1000 行的价值。
让我们看看SHOW CREATE TABLE
。 INDEXes
,尤其是UNIQUE
索引对性能有影响。我们可以提供进一步的建议。
如果这是一个 "Data Warehouse" 应用程序,那么我们应该谈谈 "Summary Tables"。这些将显着减轻 'readers' 的负载并减少对事实 table 索引的需求并防止 locking/blocking 因为它们会读取不同的 table.
此外,UUID 的性能很糟糕。
table有多大?你有多少内存? innodb_buffer_pool_size
的值是多少?
我的 Ubuntu 服务器上有一个 PHP DAEMON,用于将大量数据插入到 InnoDB 中。使用该平台的人也在使用完全相同的 table。
当非 运行 处于 TRANSACTION 模式时,DAEMON 使用大约 60-70 秒进行 100.000 次插入。当 运行 处于 TRANSACTION 模式时,BEGIN .... COMMIT 使用 15-20 秒。
然而,TRANSACTION 模式是否会锁定 tables,并阻止用户在 DAEMON TRANSACTION 正在执行时使用该平台进行插入? 锁定用户正在操作的 tables 超过 20 秒当然是不可取的:)
好吧,我正在以 500 和 500 的批次进行插入,即一个 FOR 循环 INSERT INTO (col1, col2) VALUES (a,b) 等。这很好,运行平稳,但是我能够如果我在循环之前发出 BEGIN 并在循环之后发出 COMMIT ,则可以显着加快进程,但这意味着 BEGIN/COMMIT 之间的时间超过 60 秒。但是当系统进行几十万次插入时,使用该平台的人可以对相同的 table 进行插入。系统会为用户插入生成插入帐户,还是用户必须等待 XX 秒才能处理插入?
根据您的描述,您使用启用了默认 autocommit 模式的 innodb,并在循环中一条一条地插入记录。自动提交模式意味着每个插入都被封装到它自己的事务中,这很好,但是非常慢,因为每个记录都单独保存到磁盘中。
如果您将在 begin
- commit
语句中插入记录的循环包装起来,则所有插入都是 运行 在单个事务中并且仅在磁盘中持久化一次,当commit
已发布 - 这就是您体验速度提升的原因。
无论你以何种方式插入记录,innodb都会使用锁。然而,innodb only locks the record being inserted:
INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.
Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6 each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.
这意味着,打开一个只插入记录的事务较长时间不会干扰其他用户将记录插入到同一个 table。
请注意,在循环中发出单个插入语句是将大量数据插入 MySQL.
中效率最低的方法要么使用 bulk insert (build a single insert statement in the loop and execute it after the loop, paying attention to max_allowed_packet 设置:
INSERT statements that use VALUES syntax can insert multiple rows. To do this, include multiple lists of column values, each enclosed within parentheses and separated by commas. Example:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
或使用load data infile语句。
这两种解决方案都可以显着加快数据插入速度,并且不会造成table锁定。
计划 A:LOAD DATA
。缺点:这需要将数据写入文件。如果它已经在文件中,那么这是最好的方法。
B 计划:"Batched INSERTs
" -- 构建 INSERT INTO t (a,b) VALUES (1,2), (3,4), ...
并执行它们。以 100-1000 个为一批进行。这将比 BEGIN
..COMMIT
在很多 1 行 INSERTs
周围更快。有autocommit=ON
。 Locking/blocking 将是最小的,因为每个 'transaction' 将只有 100-1000 行的价值。
让我们看看SHOW CREATE TABLE
。 INDEXes
,尤其是UNIQUE
索引对性能有影响。我们可以提供进一步的建议。
如果这是一个 "Data Warehouse" 应用程序,那么我们应该谈谈 "Summary Tables"。这些将显着减轻 'readers' 的负载并减少对事实 table 索引的需求并防止 locking/blocking 因为它们会读取不同的 table.
此外,UUID 的性能很糟糕。
table有多大?你有多少内存? innodb_buffer_pool_size
的值是多少?