MySQL 在 AWS RDS 下添加列速度慢
MySQL ADD COLUMN slow under AWS RDS
我有一个 RDS MySql,设置如下:
- Class: db.m5.xlarge
- 存储:Prosisionned 1000 IOPS (SSD)
然后我想向大小约为 20 GB 的 table 添加几列(根据 INFORMATION_SCHEMA.files
)。这是我的声明:
ALTER TABLE MY_TABLE
ADD COLUMN NEW_COLUMN_1 DECIMAL(39, 30) NULL,
ADD COLUMN NEW_COLUMN_2 DECIMAL(39, 30) NULL,
ADD COLUMN NEW_COLUMN_3 INT(10) UNSIGNED NULL,
ADD CONSTRAINT SOME_CONSTRAINT FOREIGN KEY (NEW_COLUMN_3) REFERENCES SOME_OTHER_TABLE(SOME_OTHER_PK),
ADD COLUMN NEW_COLUMN_4 DATE NULL;
执行此查询需要 172 分钟。大部分时间都花在了将数据处理到临时 table.
在该操作期间,没有执行其他查询(读取或写入)。我有自己的数据库。 SHOW FULL PROCESSLIST
是说 State
等于 copy to tmp table
我的查询。
我不明白的是 AWS RDS 控制台告诉我写入吞吐量在 30 MB/s 和 35 MB/s 之间持续 172 分钟。
假设写入吞吐量为 30 MB/s,我应该能够写入 30 * 60 * 172 = 309600 MB = 302 GB。这比操作期间创建的临时 table 的大小 (20 GB) 大得多。
所以两个问题:
- mysql/rds 在我的 table 旁边写了什么?有没有办法禁用它,以便我可以获得全部带宽来创建临时文件 table?
- 有什么方法可以加速该操作吗?写20GB的数据用3个小时,好像很长。
InnoDB 可能是您正在使用的存储引擎,因为它是默认的存储引擎。 InnoDB 做了一些看似多余的 I/O,以确保没有数据丢失。
例如:
- 缓冲池中修改的数据和索引页必须写入table空间。 table在添加列的过程中可能需要拆分一些页面,因为行变得更宽,每页适合的行数更少。
- 在向 table 空间写入页面时,InnoDB 首先将这些页面写入双写缓冲区,以确保在页面写入过程中发生崩溃时不会丢失数据。
- 事务写入 InnoDB 重做日志,这甚至可能导致多次覆盖日志中的同一块。
- 如果出于复制目的启用二进制日志,事务也会写入二进制日志。虽然这在 ALTER TABLE 语句的转换中应该不是很大的成本,因为 DDL 语句总是以语句格式而不是行格式写入二进制日志。
您还询问了可以做些什么来加速 ALTER TABLE。希望它 运行 更快的原因通常是因为在 ALTER TABLE 期间,table 被锁定并可能阻止并发查询。
在我的公司,我们使用免费工具 pt-online-schema-change,因此我们可以在 table 被更改的同时或多或少地自由使用它。以这种方式完成更改实际上需要更长的时间,但它并没有那么不方便,因为它不会阻止我们访问 table.
我使用的是 MySQL 5.7。根据此MySQL blog post,8.0 版改进了这种情况:“InnoDB 现在支持 Instant ADD COLUMN”。
因此我更改了查询以使用新功能。
-- Completes in 0.375 seconds!
ALTER TABLE MY_TABLE
ADD COLUMN NEW_COLUMN_1 DECIMAL(39, 30) NULL,
ADD COLUMN NEW_COLUMN_2 DECIMAL(39, 30) NULL,
ADD COLUMN NEW_COLUMN_3 INT(10) UNSIGNED NULL,
-- 'ALGORITHM=INSTANT' is not compatible with foreign keys.
-- The foreign key will need to be added in another statement
-- ADD CONSTRAINT SOME_CONSTRAINT FOREIGN KEY (NEW_COLUMN_3) REFERENCES SOME_OTHER_TABLE(SOME_OTHER_PK),
ADD COLUMN NEW_COLUMN_4 DATE NULL,
-- the new option
ALGORITHM=INSTANT;
-- This completed in about 6 minutes.
-- Adding the foreign creates an index under the hood.
-- This index was 1.5 GB big.
SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE MY_TABLE
ADD FOREIGN KEY (NEW_COLUMN_3) REFERENCES SOME_OTHER_TABLE(SOME_OTHER_PK);
SET FOREIGN_KEY_CHECKS=1;
所以我的结论是:
- 升级到 MySQL 8 如果可以
- 确保您始终(如果可能)使用
ALGORITHM=INSTANT
选项。
我有一个 RDS MySql,设置如下:
- Class: db.m5.xlarge
- 存储:Prosisionned 1000 IOPS (SSD)
然后我想向大小约为 20 GB 的 table 添加几列(根据 INFORMATION_SCHEMA.files
)。这是我的声明:
ALTER TABLE MY_TABLE
ADD COLUMN NEW_COLUMN_1 DECIMAL(39, 30) NULL,
ADD COLUMN NEW_COLUMN_2 DECIMAL(39, 30) NULL,
ADD COLUMN NEW_COLUMN_3 INT(10) UNSIGNED NULL,
ADD CONSTRAINT SOME_CONSTRAINT FOREIGN KEY (NEW_COLUMN_3) REFERENCES SOME_OTHER_TABLE(SOME_OTHER_PK),
ADD COLUMN NEW_COLUMN_4 DATE NULL;
执行此查询需要 172 分钟。大部分时间都花在了将数据处理到临时 table.
在该操作期间,没有执行其他查询(读取或写入)。我有自己的数据库。 SHOW FULL PROCESSLIST
是说 State
等于 copy to tmp table
我的查询。
我不明白的是 AWS RDS 控制台告诉我写入吞吐量在 30 MB/s 和 35 MB/s 之间持续 172 分钟。
假设写入吞吐量为 30 MB/s,我应该能够写入 30 * 60 * 172 = 309600 MB = 302 GB。这比操作期间创建的临时 table 的大小 (20 GB) 大得多。
所以两个问题:
- mysql/rds 在我的 table 旁边写了什么?有没有办法禁用它,以便我可以获得全部带宽来创建临时文件 table?
- 有什么方法可以加速该操作吗?写20GB的数据用3个小时,好像很长。
InnoDB 可能是您正在使用的存储引擎,因为它是默认的存储引擎。 InnoDB 做了一些看似多余的 I/O,以确保没有数据丢失。
例如:
- 缓冲池中修改的数据和索引页必须写入table空间。 table在添加列的过程中可能需要拆分一些页面,因为行变得更宽,每页适合的行数更少。
- 在向 table 空间写入页面时,InnoDB 首先将这些页面写入双写缓冲区,以确保在页面写入过程中发生崩溃时不会丢失数据。
- 事务写入 InnoDB 重做日志,这甚至可能导致多次覆盖日志中的同一块。
- 如果出于复制目的启用二进制日志,事务也会写入二进制日志。虽然这在 ALTER TABLE 语句的转换中应该不是很大的成本,因为 DDL 语句总是以语句格式而不是行格式写入二进制日志。
您还询问了可以做些什么来加速 ALTER TABLE。希望它 运行 更快的原因通常是因为在 ALTER TABLE 期间,table 被锁定并可能阻止并发查询。
在我的公司,我们使用免费工具 pt-online-schema-change,因此我们可以在 table 被更改的同时或多或少地自由使用它。以这种方式完成更改实际上需要更长的时间,但它并没有那么不方便,因为它不会阻止我们访问 table.
我使用的是 MySQL 5.7。根据此MySQL blog post,8.0 版改进了这种情况:“InnoDB 现在支持 Instant ADD COLUMN”。
因此我更改了查询以使用新功能。
-- Completes in 0.375 seconds!
ALTER TABLE MY_TABLE
ADD COLUMN NEW_COLUMN_1 DECIMAL(39, 30) NULL,
ADD COLUMN NEW_COLUMN_2 DECIMAL(39, 30) NULL,
ADD COLUMN NEW_COLUMN_3 INT(10) UNSIGNED NULL,
-- 'ALGORITHM=INSTANT' is not compatible with foreign keys.
-- The foreign key will need to be added in another statement
-- ADD CONSTRAINT SOME_CONSTRAINT FOREIGN KEY (NEW_COLUMN_3) REFERENCES SOME_OTHER_TABLE(SOME_OTHER_PK),
ADD COLUMN NEW_COLUMN_4 DATE NULL,
-- the new option
ALGORITHM=INSTANT;
-- This completed in about 6 minutes.
-- Adding the foreign creates an index under the hood.
-- This index was 1.5 GB big.
SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE MY_TABLE
ADD FOREIGN KEY (NEW_COLUMN_3) REFERENCES SOME_OTHER_TABLE(SOME_OTHER_PK);
SET FOREIGN_KEY_CHECKS=1;
所以我的结论是:
- 升级到 MySQL 8 如果可以
- 确保您始终(如果可能)使用
ALGORITHM=INSTANT
选项。