背景
我们使用的是 RDS mysql 8.0。
我们有一个高吞吐量的写/读表。昨天我们确实发布了
alter table
声明来修改 enum
值。我们在枚举列表中又添加了一个值 at the end of the list
。我们用 no lock
和 inplace
算法做到了。在过去,我们曾经这样做过,而且它曾经工作得非常好,在几毫秒内完成。但昨天花了一个多小时。完成该操作的表大小为 42 GB。当这个操作完成后,我可以看到我们得到了很多Lock wait timeout exceeded; try restarting transaction'
。
现在这提示我在该操作的最后阶段发生了某种表锁定。我无法将手指放在上面。这里到底发生了什么。
调查
这个链接谈论
通过将新的枚举或集合成员添加到有效成员值列表的末尾来修改 ENUM 或 SET 列的定义,只要数据类型的存储大小不变。例如,向具有 8 个成员的 SET 列添加一个成员会将每个值所需的存储从 1 个字节更改为 2 个字节;这需要一个表副本。在列表中间添加成员会导致现有成员重新编号,这需要表副本。
这是当时 RDS 性能洞察针对此交易显示的结果
此期间来自 RDS 性能洞察的另一个等待片段
列排序规则是
utf8_unicode_ci
现在基于上面,我尝试了解 ENUM 值中的值的存储要求是什么。来到这张桌子了
现在上图中的最后一行是我们添加的行。所以我可以看到这里的存储需求确实发生了变化。因此,如果我没理解错的话,我们现在这个枚举至少需要 42 个字节。因此,根据 mysql 文档,复制表可能会触发,这可能会解释该行为。
问题 现在我的问题是,如果上述解释确实是正确的,那么添加一个更长的值(就字符串值中的字符数而言)然后最后一个值(基本上比当前集合中最长的值更长)必须重现这种行为。但如果我再添加一个字符,那么它就无法重现。我缺少什么?我该如何解释锁等待?
如果将 NOT NULL 列更改为 NULL,则修改列也会触发表复制,反之亦然。我在您的 alter 命令中看到,您将该列设置为可为空。在你改变它之前它不是NOT NULL吗?
确实该列之前不是 NULL,但在这次更改中将其设置为 NULL!
如果您需要在生产中进行 ALTER TABLE,这将在大型表上产生表副本,我建议使用开源在线架构更改工具之一。这意味着表复制在后台运行,您可以同时继续读写表。
当然,与任何新工具一样,首先在测试环境中(而不是在生产环境中)进行试验,直到您有信心使用它为止。
在我之前的工作中,我们每周使用 pt-online-schema-change 在生产环境中运行数百次架构更改,而不会阻塞任何应用程序。