我将从MySQL Online DDL Limitations页面开始:
没有机制可以暂停在线DDL操作或限制在线DDL操作的I / O或CPU使用率。
但是,我仍然对我可能错过的解决方案感兴趣。
情况:索引越来越大,而且它们变得越来越大,以至于没有足够的内存用于所使用的查询,导致磁盘I / O飙升,一切都陷入混乱。已经创建了较小的新复合索引,但问题在于运行ALTER TABLE
而没有破坏任何内容。
事实如下:
id
列,但这不是唯一的。 SHOW CREATE TABLE
(我没有包含所有分区):
CREATE TABLE `my_wonky_table` (
`id` bigint(20) unsigned NOT NULL,
`login` varchar(127) DEFAULT NULL,
`timestamp` int(10) unsigned NOT NULL,
`ip` varchar(32) CHARACTER SET ascii DEFAULT NULL,
`val_1` int(10) unsigned DEFAULT NULL,
`val_2` varchar(127) DEFAULT NULL,
`val_3` varchar(255) DEFAULT NULL,
`val_4` varchar(127) DEFAULT NULL,
`val_5` int(10) unsigned DEFAULT NULL,
KEY `my_wonky_table_id_idx` (`id`),
KEY `my_wonky_table_timestamp_idx` (`timestamp`),
KEY `my_wonky_table_val_1_idx` (`val_1`,`id`),
KEY `my_wonky_table_val_2_idx` (`val_2`,`id`),
KEY `my_wonky_table_val_4_idx` (`val_4`,`id`),
KEY `my_wonky_table_val_5_idx` (`val_5`,`id`),
KEY `my_wonky_table_ip_idx` (`ip`,`id`),
KEY `my_wonky_table_login_idx` (`login`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY RANGE (`id`)
(PARTITION pdefault VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
关于查询:在SELECT
上始终为id
,其他所有内容都用于过滤。
我想避免的事情:
我曾想过使用pt-online-schema-change
工具进行节流,但遇到了无主键墙。另一种解决方案是在代码中执行此操作,有效地将触发器移动到代码库,并使用有些奇怪的块(例如,使用时间戳列的一小时数据块)缓慢地复制数据,因为没有唯一索引。
是否有其他解决方案和/或工具可用?
答案 0 :(得分:6)
new
表的real
表,但使用修订后的索引。添加PRIMARY KEY
以便您不会再被困。 - 这是ALTER
,但还不是“填充”。new
表中。我对chunking的建议在这里可能很有用。RENAME TABLE real TO old, new TO real;
。然后再打开。强烈建议编写所有脚本并在其他计算机上练习。这种做法可以在总数的一小部分,但它需要至少有几个分区。
答案 1 :(得分:1)
我将此作为单独的答案提出,因为最里面的部分是完全不同的。
与我的其他答案一样,您需要带有新索引的new
表,以及用于复制所有数据的脚本。但是,最重要的是模拟你的应用程序中的触发器。
幸运的是,你有id
,即使它不是PRIMARY KEY
。并且,即使它不是UNIQUE
,也可以使用它(假设您没有数千个具有相同ID的行 - 如果您这样做,我们可以进一步讨论)。
“复制脚本”和应用程序相互通信。
复制脚本是一个很长的循环:
SELECT GET_LOCK('copy', 5), high_water_mark FROM tbl;
- (或其他一些超时)id BETWEEN high_water_mark AND high_water_mark + 999
复制行。UPDATE tbl SET high_water_mark = high_water_mark + 1000;
ids
应用程序在读取时继续从旧表中读取。但在写作时,确实如此:
SELECT GET_LOCK('copy', 5), high_water_mark FROM tbl;
- (或其他一些超时)id
< = high_water_mark
,也请写入新表。SELECT RELEASE_LOCK('copy');
监控进度。在某些时候,您需要停止所有内容,复制最后几行并执行RENAME TABLE
。
我不知道你的超时,睡眠或块大小的最佳值。但我不认为块大小超过1K是明智的。
这项技术有利于您将来可能需要做的各种更改,因此请保持良好的状态。
答案 2 :(得分:1)
这将归结为MySQL变种& amp;您正在使用的版本,但如果它是每个连接一个线程(my.cnf thread_handling=one-thread-per-connection
,可能是您的构建中的默认值),并且您可以将ALTER TABLE
工作负载放在新连接中,那么工作负载是一个唯一的PID,您可以在其上使用ionice
/ renice
。
我有点蹩脚的答案,但它的侵入性比其他选项要小。
如果查看ps -eLf |grep mysql
,您可以看到线程/轻量级进程,只需要弄清楚PID属于您的特定连接。如果通过TCP连接,则可以匹配本地连接端口并将其映射到lsof以查找特定线程。其他方式可以使用strace,systemtap等,或运行您可以查看的初始查询。
之后,您可以使用ionice
/ renice
来影响系统上的PID。你真的想确保你捕获它是什么PID,并重置好和&之后的优先级,不影响其他任何事情。
与其他人一样,您确实需要长期重塑此表。分区很有用,但不是最终结果,因为您运行的是1.3TiB的在线数据,并且您声明只需要读取最近的3-6个分区。在添加本机分区之前来自MySQL,我认为这对于VIEW和单独的表(在需要翻转时以原子方式更新VIEW)是一个很好的例子。它还可以让您轻松地将一些旧表移动到离线存储。