Mysql8升级:一张表出现空约束问题

问题描述 投票:0回答:1

我们最近对生产系统进行了升级,从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 应用程序的查询都会失败。

php mysql pdo mysql-8.0
1个回答
0
投票

https://bugs.mysql.com/bug.php?id=113464

以下解释了这种偶发性(即用户再次尝试并且有效)以及为什么我们在测试或 CLI 中没有发生该错误。

如果表有触发器
    ON INSERT
  • (之前或之后)
    ,则会发生错误
    
    然而,它实际上只会导致对同一个表的
  • UPDATE
  • 查询出现问题
    特别是 
  • UPDATE
  • 必须通过与
    INSERT
     相同的数据库连接运行
    
  • 因此,当我们通过 CLI 重新创建其中任何一个时,这是一个不同的连接,因为它是通过堡垒服务器进行的。当用户反复点击提交时,多个请求很可能会到达不同的主机,而这些主机有自己的连接池。它是零星的(有时需要 2 或 3 个请求),因为 PDO 在 mod_apache 中缓存连接,因此对一台服务器的多个请求可能会重复使用同一连接。

修复(目前直到 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
权限(以及所有其他当前权限)。
    

© www.soinside.com 2019 - 2024. All rights reserved.