我们最近对生产系统进行了升级,从mysql5.7到mysql8。
这在 CI、本地环境、暂存中进行了广泛的测试。所有查询均按预期运行。随着产品上迁移到 mysql8,只有一个表出现了奇怪的问题。可以量化为:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'my_col' cannot be null' with query:
'UPDATE my_table SET my_col = '1' WHERE table_id='abc123' LIMIT 1;'
该列是带有
not null
的 varchar,没有默认值。但正如您从查询中看到的,我们并没有“尝试”将其设置为 null。我们尝试将其设置为字符串“1”。每次运行查询时不会发生此错误;看起来,如果应用程序在两个后续请求中运行它两次,它将在第二次正常工作。
为了让这更有趣,这个问题仅在通过我们的 Web 应用程序(使用 PDO 的 PHP)运行时才会出现 - 在 mysql-client CLI 中运行它可以正常工作。使用参数化查询以及包含输入(这是不安全的)的整个查询作为单个字符串查询提供时,通过 Web 应用程序的查询都会失败。
以下解释了这种偶发性(即用户再次尝试并且有效)以及为什么我们在测试或 CLI 中没有发生该错误。
如果表有触发器
ON INSERT
,则会发生错误然而,它实际上只会导致对同一个表的
UPDATE
UPDATE
INSERT
相同的数据库连接运行
修复(目前直到 mysql 发布合适的补丁为止):
如果您打算不久之后(在同一请求内或使用连接池)
INSERT
UPDATE
触发器(可能不可行)在提出后续 UPDATE
try {
$pdo->exec('FLUSH TABLES my_table');
}
catch (\Throwable $e){
// log this in case it fails
}
// run your update here and the error won't happen
如果
FLUSH
命令失败,请检查您的数据库用户是否具有
FLUSH
或 RELOAD
权限(以及所有其他当前权限)。