从较小的任务关键 table 更新大 table 而不锁定小 table
Update large table from smaller, mission critical, table without locking small table
在 MySQL 中,我有两个 innodb table,一个小型关键任务 table,需要随时可用 reads/writes。称之为 mission_critical。我有一个更大的 table(>10 万行),称为 big_table。我需要更新 big_table,例如:
update mission_critical c, big_table b
set
b.something = c.something_else
where b.refID=c.id
此查询可能需要一个多小时,但这会在 mission_critical table 上创建一个写锁。有什么方法可以告诉 mysql、"I don't want a lock on mission_critical" 以便可以写入 table?
我知道从交易的角度来看这并不理想。我现在能想到的唯一解决方法是复制小 mission_critical table 并从中进行更新(我不在乎它是否被锁定),但我宁愿不这样做如果有一种方法可以让 MySQL 在本地更优雅地处理这个问题。
不是 table 被锁定,而是 mission_critical 中的所有记录都被锁定,因为它们基本上都被更新扫描了。我不假设这一点;症状是,当用户登录在线系统时,它会尝试更新 mission_critical 中的日期时间列以更新他们上次登录的时间。这些查询由于查询时出现锁定等待超时错误而终止以上是运行ning。如果我终止上面的查询,所有未决查询立即 运行。
mission_critical.id 和 big_table.refID 都已编入索引。
每个 table 的创建语句的相关部分是:
mission_critical:
CREATE TABLE `mission_critical` (
`intID` int(11) NOT NULL AUTO_INCREMENT,
`id` smallint(6) DEFAULT NULL,
`something_else` varchar(50) NOT NULL,
`lastLoginDate` datetime DEFAULT NULL,
PRIMARY KEY (`intID`),
UNIQUE KEY `id` (`id`),
UNIQUE KEY `something_else` (`something_else`),
) ENGINE=InnoDB AUTO_INCREMENT=1432 DEFAULT CHARSET=latin1
big_table:
CREATE TABLE `big_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`postDate` date DEFAULT NULL,
`postTime` int(11) DEFAULT NULL,
`refID` smallint(6) DEFAULT NULL,
`something` varchar(50) NOT NULL,
`change` decimal(20,2) NOT NULL
PRIMARY KEY (`id`),
KEY `refID` (`refID`),
KEY `postDate` (`postDate`),
) ENGINE=InnoDB AUTO_INCREMENT=138139125 DEFAULT CHARSET=latin1
查询的解释是:
+----+-------------+------------------+------------+------+---------------+-------+---------+------------------------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------------+------------+------+---------------+-------+---------+------------------------------------+------+----------+-------------+
| 1 | SIMPLE | mission_critical | | ALL | id | | | | 774 | 100 | Using where |
| 1 | UPDATE | big_table | | ref | refID | refID | 3 | db.mission_critical.something_else | 7475 | 100 | |
+----+-------------+------------------+------------+------+---------------+-------+---------+------------------------------------+------+----------+-------------+
UPDATE 将锁定需要更改的行。它还可能会在这些行之后锁定 "gaps"。
您可以在循环中使用 MySQL 个事务
一次只更新 100 行
开始;
SELECT ...更新; -- 安排 select 包括 100 行
更新 ...; -- 更新 100 行
提交;
我首先建议使用子查询的解决方法,在内部临时文件中创建一个副本 table。但是在我的测试中,小 table 仍然被锁定以进行写入。所以我想你最好的选择是手动制作副本。
此错误报告中描述了锁定的原因:https://bugs.mysql.com/bug.php?id=72005
这是Sinisa Milivojevic在回答中写的:
update table t1,t2 ....
any UPDATE
with a join is considered a multiple-table update. In that
case, a referenced table has to be read-locked, because rows must not
be changed in the referenced table during UPDATE
until it has
finished. There can not be concurrent changes of the rows, nor DELETE
of the rows, nor, much less, exempli gratia any DDL on the referenced
table. The goal is simple, which is to have all tables with consistent
contents when UPDATE
finishes, particularly since multiple-table
UPDATE
can be executed with several passes.
简而言之,这种行为是有充分理由的。
考虑编写 INSERT 和 UPDATE 触发器,这将动态更新 big_table
。这会延迟 mission_critical
table 上的写入。但它对您来说可能足够快,并且不再需要 mass-update-query。
还要检查使用 char(50)
代替 varchar(50)
是否更好。我不确定,但它可能会提高更新性能,因为行大小不需要更改。我在测试中可以提高大约 50% 的更新性能。
可能值得尝试相关子查询以查看优化器是否提出了不同的计划,但性能可能会更差。
update big_table b
set b.something = (select c.something_else from mission_critical c where b.refID = c.id)
在 MySQL 中,我有两个 innodb table,一个小型关键任务 table,需要随时可用 reads/writes。称之为 mission_critical。我有一个更大的 table(>10 万行),称为 big_table。我需要更新 big_table,例如:
update mission_critical c, big_table b
set
b.something = c.something_else
where b.refID=c.id
此查询可能需要一个多小时,但这会在 mission_critical table 上创建一个写锁。有什么方法可以告诉 mysql、"I don't want a lock on mission_critical" 以便可以写入 table?
我知道从交易的角度来看这并不理想。我现在能想到的唯一解决方法是复制小 mission_critical table 并从中进行更新(我不在乎它是否被锁定),但我宁愿不这样做如果有一种方法可以让 MySQL 在本地更优雅地处理这个问题。
不是 table 被锁定,而是 mission_critical 中的所有记录都被锁定,因为它们基本上都被更新扫描了。我不假设这一点;症状是,当用户登录在线系统时,它会尝试更新 mission_critical 中的日期时间列以更新他们上次登录的时间。这些查询由于查询时出现锁定等待超时错误而终止以上是运行ning。如果我终止上面的查询,所有未决查询立即 运行。
mission_critical.id 和 big_table.refID 都已编入索引。
每个 table 的创建语句的相关部分是:
mission_critical:
CREATE TABLE `mission_critical` (
`intID` int(11) NOT NULL AUTO_INCREMENT,
`id` smallint(6) DEFAULT NULL,
`something_else` varchar(50) NOT NULL,
`lastLoginDate` datetime DEFAULT NULL,
PRIMARY KEY (`intID`),
UNIQUE KEY `id` (`id`),
UNIQUE KEY `something_else` (`something_else`),
) ENGINE=InnoDB AUTO_INCREMENT=1432 DEFAULT CHARSET=latin1
big_table:
CREATE TABLE `big_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`postDate` date DEFAULT NULL,
`postTime` int(11) DEFAULT NULL,
`refID` smallint(6) DEFAULT NULL,
`something` varchar(50) NOT NULL,
`change` decimal(20,2) NOT NULL
PRIMARY KEY (`id`),
KEY `refID` (`refID`),
KEY `postDate` (`postDate`),
) ENGINE=InnoDB AUTO_INCREMENT=138139125 DEFAULT CHARSET=latin1
查询的解释是:
+----+-------------+------------------+------------+------+---------------+-------+---------+------------------------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------------+------------+------+---------------+-------+---------+------------------------------------+------+----------+-------------+
| 1 | SIMPLE | mission_critical | | ALL | id | | | | 774 | 100 | Using where |
| 1 | UPDATE | big_table | | ref | refID | refID | 3 | db.mission_critical.something_else | 7475 | 100 | |
+----+-------------+------------------+------------+------+---------------+-------+---------+------------------------------------+------+----------+-------------+
UPDATE 将锁定需要更改的行。它还可能会在这些行之后锁定 "gaps"。
您可以在循环中使用 MySQL 个事务 一次只更新 100 行
开始; SELECT ...更新; -- 安排 select 包括 100 行 更新 ...; -- 更新 100 行 提交;
我首先建议使用子查询的解决方法,在内部临时文件中创建一个副本 table。但是在我的测试中,小 table 仍然被锁定以进行写入。所以我想你最好的选择是手动制作副本。
此错误报告中描述了锁定的原因:https://bugs.mysql.com/bug.php?id=72005
这是Sinisa Milivojevic在回答中写的:
update table t1,t2 ....
any
UPDATE
with a join is considered a multiple-table update. In that case, a referenced table has to be read-locked, because rows must not be changed in the referenced table duringUPDATE
until it has finished. There can not be concurrent changes of the rows, norDELETE
of the rows, nor, much less, exempli gratia any DDL on the referenced table. The goal is simple, which is to have all tables with consistent contents whenUPDATE
finishes, particularly since multiple-tableUPDATE
can be executed with several passes.
简而言之,这种行为是有充分理由的。
考虑编写 INSERT 和 UPDATE 触发器,这将动态更新 big_table
。这会延迟 mission_critical
table 上的写入。但它对您来说可能足够快,并且不再需要 mass-update-query。
还要检查使用 char(50)
代替 varchar(50)
是否更好。我不确定,但它可能会提高更新性能,因为行大小不需要更改。我在测试中可以提高大约 50% 的更新性能。
可能值得尝试相关子查询以查看优化器是否提出了不同的计划,但性能可能会更差。
update big_table b
set b.something = (select c.something_else from mission_critical c where b.refID = c.id)