在 MySQL 上更新索引值极其缓慢
Update index values extremely slow on MySQL
我有三个 table,一个在数据库 db1
中,两个在数据库 db2
中,都在同一个 MySQL 服务器上:
CREATE TABLE `db1`.`user` (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_name` varchar(20) NOT NULL,
`password_hash` varchar(71) DEFAULT NULL,
`email_address` varchar(100) NOT NULL,
`registration_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`registration_hash` char(16) DEFAULT NULL,
`active` bit(1) NOT NULL DEFAULT b'0',
`public` bit(1) NOT NULL DEFAULT b'0',
`show_name` bit(1) NOT NULL DEFAULT b'0',
PRIMARY KEY (`id`),
UNIQUE KEY `user_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `db2`.`ref` (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `db2`.`combination` (
`ref_id` bigint(20) UNSIGNED NOT NULL,
`user_id` bigint(20) UNSIGNED NOT NULL,
`arbitrary_number` tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`figurine_id`,`user_id`),
KEY `combination_user` (`user_id`),
KEY `combination_number` (`user_id`,`arbitrary_number`),
CONSTRAINT `combination_ref` FOREIGN KEY (`ref_id`) REFERENCES `ref` (`id`) ON UPDATE CASCADE,
CONSTRAINT `combination_user` FOREIGN KEY (`user_id`) REFERENCES `db1`.`user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
table db1
.user
有大约 600 条记录,table db2
.ref
有大约 800 条记录, table db2
.combination
有大约 30 万条记录。
现在使用 Perl 和 DBD::mysql 我执行以下查询:
UPDATE `db1`.`user` SET `id` = (`id` + 1000)
ORDER BY `id` DESC
但是,此查询总是停止提及与 MySQL 服务器的连接已丢失。通过 PhpMyAdmin 执行相同的查询也会导致超时。不知何故,查询只需要很长时间才能执行。我猜是因为所有外键值都需要更新。
将 FOREIGN_KEY_CHECKS
变量设置为 OFF
将不会更新 db.combination
table 中的 user_id
列,该列确实需要更新。
我也尝试过操纵不同的超时(如整个互联网所建议的那样),如下所示:
SET SESSION net_read_timeout=3000;
SET SESSION net_write_timeout=3000;
SET SESSION wait_timeout=6000;
我已经通过再次检索值验证了新值确实已设置。然而,即使有这么长的超时,查询仍然无法执行,并且在大约 30 秒后与 MySQL 服务器的连接再次丢失(在执行 UPDATE
查询时)
我们非常欢迎任何关于如何加速此查询的建议。
顺便说一句:PK 列具有非常大的整数类型。我还将使这种类型更小(更改为 INT
)。这种类型的改变是否也能显着提高速度?
更新
我还为查询执行了 EXPLAIN
,它在 Extra
列中提到查询正在执行文件排序。我本以为由于 table 上的索引(添加了它们,因为它们一开始就不存在),不会发生文件排序。
300K CASCADEs
可能是任务中最慢的部分。所以,让我们避免它。 (但是,可能会检查验证生成的链接;这应该不会太慢。)
禁用FOREIGN KEY
处理
创建新表无FOREIGN KEYs
。 new_user、new_combination。 (不知道是否需要new_ref)
执行此操作以填充表格:
INSERT INTO new_xx (user_id, ...)
SELECT user_id + 1000, ...;
ALTER TABLE new_xx ADD FOREIGN KEY ...;
(每个xx)
`RENAME TABLE xx TO old_xx, new_xx TO xx;
`DROP TABLE old_xx;
启用FOREIGN KEY
处理
我有三个 table,一个在数据库 db1
中,两个在数据库 db2
中,都在同一个 MySQL 服务器上:
CREATE TABLE `db1`.`user` (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_name` varchar(20) NOT NULL,
`password_hash` varchar(71) DEFAULT NULL,
`email_address` varchar(100) NOT NULL,
`registration_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`registration_hash` char(16) DEFAULT NULL,
`active` bit(1) NOT NULL DEFAULT b'0',
`public` bit(1) NOT NULL DEFAULT b'0',
`show_name` bit(1) NOT NULL DEFAULT b'0',
PRIMARY KEY (`id`),
UNIQUE KEY `user_name` (`user_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `db2`.`ref` (
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `db2`.`combination` (
`ref_id` bigint(20) UNSIGNED NOT NULL,
`user_id` bigint(20) UNSIGNED NOT NULL,
`arbitrary_number` tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`figurine_id`,`user_id`),
KEY `combination_user` (`user_id`),
KEY `combination_number` (`user_id`,`arbitrary_number`),
CONSTRAINT `combination_ref` FOREIGN KEY (`ref_id`) REFERENCES `ref` (`id`) ON UPDATE CASCADE,
CONSTRAINT `combination_user` FOREIGN KEY (`user_id`) REFERENCES `db1`.`user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
table db1
.user
有大约 600 条记录,table db2
.ref
有大约 800 条记录, table db2
.combination
有大约 30 万条记录。
现在使用 Perl 和 DBD::mysql 我执行以下查询:
UPDATE `db1`.`user` SET `id` = (`id` + 1000)
ORDER BY `id` DESC
但是,此查询总是停止提及与 MySQL 服务器的连接已丢失。通过 PhpMyAdmin 执行相同的查询也会导致超时。不知何故,查询只需要很长时间才能执行。我猜是因为所有外键值都需要更新。
将 FOREIGN_KEY_CHECKS
变量设置为 OFF
将不会更新 db.combination
table 中的 user_id
列,该列确实需要更新。
我也尝试过操纵不同的超时(如整个互联网所建议的那样),如下所示:
SET SESSION net_read_timeout=3000;
SET SESSION net_write_timeout=3000;
SET SESSION wait_timeout=6000;
我已经通过再次检索值验证了新值确实已设置。然而,即使有这么长的超时,查询仍然无法执行,并且在大约 30 秒后与 MySQL 服务器的连接再次丢失(在执行 UPDATE
查询时)
我们非常欢迎任何关于如何加速此查询的建议。
顺便说一句:PK 列具有非常大的整数类型。我还将使这种类型更小(更改为 INT
)。这种类型的改变是否也能显着提高速度?
更新
我还为查询执行了 EXPLAIN
,它在 Extra
列中提到查询正在执行文件排序。我本以为由于 table 上的索引(添加了它们,因为它们一开始就不存在),不会发生文件排序。
300K CASCADEs
可能是任务中最慢的部分。所以,让我们避免它。 (但是,可能会检查验证生成的链接;这应该不会太慢。)
禁用
FOREIGN KEY
处理创建新表无
FOREIGN KEYs
。 new_user、new_combination。 (不知道是否需要new_ref)执行此操作以填充表格:
INSERT INTO new_xx (user_id, ...) SELECT user_id + 1000, ...;
ALTER TABLE new_xx ADD FOREIGN KEY ...;
(每个xx)`RENAME TABLE xx TO old_xx, new_xx TO xx;
`DROP TABLE old_xx;
启用
FOREIGN KEY
处理