
MySQL 8.0+ 默认 strict mode 是什么,为什么你改了 sql_mode 还没生效
MySQL 8.0 开始默认启用严格模式(STRICT_TRANS_TABLES),但这个“默认”只作用于新连接、且受全局与会话两级配置影响。你执行 SET sql_mode = ‘STRICT_TRANS_TABLES’ 后插入脏数据仍成功,大概率是改了会话级却没动全局级,或者应用连接池复用了旧连接。
SET GLOBAL sql_mode = ‘STRICT_TRANS_TABLES’ 才影响后续新连接;已有连接仍用旧值MySQL 配置文件(my.cnf 或 my.ini)里的 sql_mode 设置优先级高于启动后动态 SET,但需重启生效Docker 容器或云数据库(如阿里云 RDS)可能锁定全局 sql_mode,只能通过会话 SET,且每次连接都要重设
如何安全地启用完整严格校验:STRICT_TRANS_TABLES vs STRICT_ALL_TABLES
两者都开启“拒绝非法值”,但行为关键不同:STRICT_TRANS_TABLES 只对事务表(InnoDB)强制校验,STRICT_ALL_TABLES 对所有表(包括 MyISAM)都生效——但 MyISAM 不支持事务回滚,出错时已写入的部分无法撤回,反而导致数据不一致。
生产环境只用 STRICT_TRANS_TABLES,别加 STRICT_ALL_TABLES真正需要强校验时,补上 NO_ZERO_DATE、NO_ZERO_IN_DATE、ERROR_FOR_DIVISION_BY_ZERO,防止日期/除零静默转成默认值避免包含 ALLOW_INVALID_DATES —— 它会让 ‘2023-02-30’ 这种错日期被接受
推荐组合:STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO
PHP/Laravel 连接 MySQL 时 sql_mode 被悄悄覆盖怎么办
很多 ORM 或 PDO 驱动会在连接建立后自动执行 SET sql_mode = ” 或覆盖为你指定的值,尤其 Laravel 的 mysql 配置里 ‘strict’ => true 实际发的是 SET sql_mode = ‘STRICT_TRANS_TABLES’,但若 DBA 在服务器端锁死配置,这条语句会被忽略且无报错。
检查 PHP 连接后的实际生效值:SELECT @@sql_mode,别只信配置文件Laravel 中在 config/database.php 的 mysql 配置下加 ‘options’ => [PDO::MYSQL_ATTR_INIT_COMMAND => "SET sql_mode=(SELECT REPLACE(@@sql_mode,’STRICT_TRANS_TABLES’,”))"] 这类写法很危险,会主动关掉严格模式更稳妥的做法:在 MySQL 侧固化配置(my.cnf + 重启),让所有客户端统一遵守,而非各框架各自 SET
INSERT IGNORE / ON DUPLICATE KEY UPDATE 在 strict mode 下的行为变化
非 strict 模式下,INSERT IGNORE 遇到主键冲突或数据截断会静默跳过;strict 模式下,**数据截断会直接报错(Truncated incorrect DOUBLE value 类错误),而主键冲突仍被 IGNORE**——这容易让人误以为“strict 没起作用”。
想让重复键也报错?别用 IGNORE,改用 INSERT … ON DUPLICATE KEY UPDATE 并确保 UPDATE 子句里没有非法值批量插入时,strict mode 下一行失败会导致整批回滚(事务表),而非只跳过那行测试时用 SHOW WARNINGS 查看是否出现 Data truncated 提示,它在 strict 下会升级为 ERROR
真正麻烦的不是设不设 strict,而是团队里有人写 SQL 依赖隐式转换(比如把字符串 ‘abc’ 插进 INT 字段),这种代码在 strict 下必挂,但问题根源不在参数,而在数据契约本身没定义清楚。

评论(0)