开发者生态
morning
安全的 MySQL 升级其实不太安全
摘要
I get a notification that my database version has reached end of life, and it has to be upgraded. And the AWS extended support fees are a good motivator to upgrade as soon as possible. I had a green r...
the
and
table
that
The
IDs
AUTO_INCREMENT
database
has
one
2026-08-30
1 阅读
约5分钟阅读
el1s7
字号:
我收到一条通知,表明我的数据库版本已达到生命周期,必须升级。 AWS 延长支持费用是尽快升级的良好动力。我有一个绿色副本,所以我升级它,检查一切是否正常,然后切换。又快又简单,对吧?我是这么想的。然而,并非一切都是正确的。一小时后,我收到了一个奇怪错误的报告,因此我检查了数据库,发现一个特定的表(我们称之为表 X)的 ID 以不同的顺序分配。先前数据库中 ID 为 1 的行现在的 ID 为 26。该表还在其他 6 个表中引用,其中 5 个表正确引用了这些新 ID,但其中一个表以某种方式使用了先前数据库的 ID,而这些表现在引用了完全不同的行。这真是令人难以置信。事情怎么会变得如此混乱?迁移 前一段时间,在升级之前,运行了一次迁移,向表 X 添加新的自增主键。 ALTER TABLE X 添加列 id INT NOT NULL AUTO_INCRMENT PRIMARY KEY ;迁移还更新了 6 个相关表,以便它们引用新 ID 而不是旧 ID。对于每个表,更新大致如下所示: UPDATE some_table JOIN x ON x 。 old_id = some_table 。 x_old_id SET some_table 。 x_id = x 。 ID ; AUTO_INCRMENT 列 事实证明,向复制表添加 AUTO_INCRMENT 列可能会导致行在源和副本上获得不同的 ID。根据有关复制和 AUTO_INCRMENT 的 MySQL 文档,使用 ALTER TABLE 添加 AUTO_INCRMENT 列可能不会在源和副本上产生相同的行排序。 ID 的分配顺序取决于存储引擎和处理行的顺序。好吧,我不知道这一点。但为什么这个新字段会在 6 个表中的 5 个中被正确引用,而在一个表中却完全混乱呢?二进制日志格式 这是更令人困惑的地方。 MySQL复制使用“二进制日志”来记录对源所做的更改。然后,这些更改被发送到副本,副本使用它们来重现相同的事务。记录的内容以及如何将其应用到副本上取决于二进制日志“格式”。 MySQL 支持 3 种“格式”: 语句:SQL 语句本身写入二进制日志,副本执行该语句。 ROW :对各个行所做的更改将写入二进制日志,并且这些行更改将直接应用于副本。 MIXED :使用混合日志记录时,默认使用基于语句的日志记录,但在某些情况下日志记录模式会自动切换到基于行。显然,我的源数据库已将 binlog_format 配置为 MIXED 。因此,这 5 个表引用了正确的新 ID 的原因是因为它们的更新语句是使用 STATEMENT 模式复制的。确切的 UPDATE 语句再次在副本上运行。它查找副本的 x.id 版本,并将正确的本地 ID 写入每个相关表中。但对于剩余的表,MySQL 决定改用 ROW 模式……在该模式下,副本不会运行原始 UPDATE 。它从源接收结果行更改并直接应用它们。因此,源上生成的 x_id 值被复制到副本。但由于表 X 在副本上具有不同的 ID,因此这些值现在指向完全不同的行。你可能会问,为什么 MySQL 决定只对一张表使用 ROW 模式?我能发现这个表和其他 5 个表之间唯一可见的区别是这个表有一个 AUTO_INCRMENT 列。 MySQL 确实有涉及 AUTO_INCRMENT 的特定情况,它认为对于基于语句的复制不安全,因此在 MIXED 下使用 ROW 进行记录。这很讽刺,不是吗?这一切似乎最终都归结为 AUTO_INCRMENT 功能。结论 小心使用 MySQL 副本。这类问题的可怕之处在于,它是出乎意料的,很容易被忽视,但它很快就会变成生产中的一场灾难,你会想知道事情是如何最终变成这样的。
这篇文章对您有帮助吗?
订阅66必读
每日精选科技资讯,直达你的邮箱