sql_mode兼容性,MySQL 8.0 升级踩过的坑

MySQL 8.0升级:sql_mode兼容性问题解析
本文讲述了在将MySQL从5.7升级到8.0时遇到的sql_mode兼容性问题,详细分析了NO_AUTO_CREATE_USER模式的变更,以及如何检查和解决不兼容的sql_mode,提醒开发者注意升级过程中的会话级参数设置问题。
部署运行你感兴趣的模型镜像

MySQL 8.0新特性专栏目录

双重密码,MySQL 8.0创新特性
sql_mode兼容性,MySQL 8.0 升级踩过的坑



前言:

MySQL 8.0从GA到现在已经过去4年了,被各大互联网公司广泛使用,稳定性得到了充分的验证。最近,我们也在将存量的旧版本数据库升级到8.0。虽然前期做了很多的检查和验证,不过升级过程中终究免不了踩一些坑。

sql_mode,在MySQL 5.7之前是宽松模式;在MySQL 5.7之后一直到最新的8.0.28版本,都还是严格模式。不过,在不同的数据库版本之间、不同的数据库实例之间,sql_mode总会有一些变化;升级或者迁移的过程中一不小心就会掉入坑中。


1. MySQL升级8.0之后,业务访问数据库报错

一套业务库从MySQL 5.7升级到8.0之后,业务请求报错,SQLSTATE[42000]: Syntax error or access violation。

# 业务侧报错信息
SQLSTATE[42000]: Syntax error or access violation: 1231 Variable 'sql_mode' can't be set to the value of 'NO_AUTO_CREATE_USER' (SQL: select `id`, `name` from `t`)

这里的报错提示为,语法错误。开发人员一看就蒙圈了,这代码跑了这么多年,怎么突然会报个语法错误呢?并且报错信息显示'sql_mode' can't be set to the value of 'NO_AUTO_CREATE_USER',开发人员确认地说肯定没有写过这样的SQL。
这里我们可以先简单定位一下,打开general_log,看看客户端会话到底执行了哪些语句。

...
2022-03-24T20:02:40.077202+08:00     9221 Execute   set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
2022-03-24T20:02:40.077202+08:00     9221 Execute  select `id`, `name` from `t`
...

根据日志可以明确看到,客户端首先设置了会话级的sql_mode,然后再执行相应的业务查询。拿到这个具体的SQL,开发人员很快就能确定这是php框架底层自带的语句。

很明显,这是一个标准的基于MySQL 5.7版本开发的底层框架。

2. 问题原因剖析

2.1 sql_mode的历史变迁

为什么一眼就能断定这是基于MySQL 5.7版本开发的框架呢?

因为,报错的这句sql_mode设置,与MySQL 5.7 版本的默认sql_mode设置一模一样。在MySQL 5.7中sql_mode的默认配置就是sql_mode='ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION'

而在MySQL 8.0.11版本中,sql_mode的默认配置作了修改,去掉了一个sql模式。刚好,去掉的模式就是前面报错信息中提示的NO_AUTO_CREATE_USER

2.2 NO_AUTO_CREATE_USER的前世今生

在MySQL 5.7 之前,DBA经常习惯使用 grant 语法来创建用户和授权。MySQL 5.7 仍然支持这种语法来创建用户,但是为了限制这种创建用户的行为,引入了NO_AUTO_CREATE_USER的sql模式。[NO_AUTO_CREATE_USER], 即在grant语句中禁止创建空密码的账户,使用grant语法创建用户必须带上 “identified by”关键字设置账户密码,否则就被认为是非法的创建语句。

而在MySQL 8.0.11版本之后,官方认为DBA们已经接受了默认使用create user语法来创建账户的行为,就直接把grant创建账户的语法给废弃了。grant语法创建账户都不允许了,那么NO_AUTO_CREATE_USER模式也就自然要退出历史舞台了,所以就在8.0.11 中同时废弃了这个模式,以后不再支持。

# 在MySQL 8.0.23中测试一下grant语法,直接报错。
MySQL [(none)]> grant select on test.* to 'no_user'@'100.124.43.85' identified by 'no_user';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'identified by 'no_user'' at line 1

NOTE: 这里问题就很明确了,业务底层框架在连接数据库后设置会话级sql_mode,包含了MySQL 8.0中已经废弃的模式NO_AUTO_CREATE_USER,MySQL直接返回报错。

2.3 检查并解决不兼容的sql_mode

实际上,在MySQL 8.0中废弃的sql_mode并不仅仅只有NO_AUTO_CREATE_USER,还有一些模式同样也是8.0不兼容的。我们在制定MySQL升级方案时就已经考虑到了sql_mode的变化,在升级前检查中加入了MySQL 8.0不支持的sql_mode的检查。如下图所示:
MySQL 8.0升级前检查
并且在升级过程过程中,配置MySQL 8.0的初始参数时,将5.7的运行参数与8.0的参数模板进行了比对,将5.7的部分参数设置合并到了MySQL 8.0的参数文件。

但是,以上种种方案只能保证MySQL升级前后的全局参数配置是OK的;客户端连接设置的会话级参数我们无法控制。这里就需要开发人员配合一起检查业务侧代码里有没有与MySQL 8.0不兼容的参数设置。

总结

那么,哪些参数/设置是MySQL 8.0与之前版本不兼容的呢?
我将在下一篇文章同大家分享这个问题。欢迎大家点赞关注,我会持续做一些相关记录和分享。

您可能感兴趣的与本文相关的镜像

TensorFlow-v2.9

TensorFlow-v2.9

TensorFlow

TensorFlow 是由Google Brain 团队开发的开源机器学习框架,广泛应用于深度学习研究和生产环境。 它提供了一个灵活的平台,用于构建和训练各种机器学习模型

<think>好的,我需要帮助用户检测从MySQL 5.7升级8.0时的SQL语句兼容性问题。根据用户提供的引用内容,我应该参考已有的信息来给出解决方案。 首先,用户提到使用mysqlsh工具的util.checkForServerUpgrade()方法,这在引用[1]和[3]中有示例。这可能是一个官方推荐的检查工具,我需要详细说明使用步骤,包括安装MySQL Shell、运行命令以及如何解读报告。此外,引用[2]提到了一些默认设置的变化,比如字符集和sql_mode,这些也是检查的重点。 接下来,用户可能需要手动检查的方面,比如保留字变化、索引可见性、JSON路径语法等,这些在引用[2]和[4]中有部分涉及。例如,MySQL 8.0引入了不可见索引,可能需要检查现有SQL是否依赖旧的行为。还有字符集的变化可能导致存储或排序问题,这也是需要注意的。 另外,测试环境验证也是关键步骤,引用[4]中提到了观察错误日志和实际连接测试,这部分需要建议用户搭建测试环境,并详细记录步骤,如备份数据、安装新版本、导入数据、运行测试用例等。 最后,用户可能需要了解升级后的配置调整,比如sql_mode和密码策略的变化,引用[2]和[5]提到这些点,应提醒用户在升级后检查配置文件,避免应用出现兼容性问题。 需要确保回答结构清晰,分步骤说明,并覆盖工具使用、手动检查项、测试验证和注意事项,同时生成相关问题帮助用户进一步探索。</think>### 检测MySQL 5.7升级8.0SQL兼容性问题 #### 1. 使用官方工具`mysqlsh`进行自动检测 MySQL官方提供的**MySQL Shell工具**(`mysqlsh`)可通过`util.checkForServerUpgrade()`函数全面检查兼容性问题,包括SQL语法、配置差异和废弃特性等[^1][^3]。 **操作步骤:** 1. **安装MySQL Shell** ```bash # 从MySQL官网下载对应系统的安装包(如RedHat/CentOS) wget https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm sudo yum install mysql-shell ``` 2. **运行兼容性检查** ```bash mysqlsh -- util check-for-server-upgrade \ --user=root --host=localhost --port=3306 \ --target-version=8.0.34 \ --output-format=JSON > /tmp/upgrade_report.json ``` - `--target-version`指定目标版本(需与实际计划版本一致) - `--output-format`支持`TEXT`或`JSON`格式报告[^1][^3] 3. **解读报告内容** 报告会明确标注以下问题类型: - **语法废弃警告**:如`GROUP BY`隐式排序、`@@SQL_MODE`旧参数 - **字符集冲突**:表字符集与MySQL 8.0默认`utf8mb4`不兼容的情况[^2] - **权限变更**:`GRANT`语句中已移除的权限配置 #### 2. 手动检查重点兼容性问题 以下问题需结合代码审查: - **保留字冲突** MySQL 8.0新增保留字(如`CUME_DIST`, `SYSTEM`),需检查SQL中是否使用这些关键词作为表名或字段名。 - **索引可见性** 检查是否依赖`MyISAM`引擎的索引行为,MySQL 8.0系统表已全部替换为`InnoDB`[^2]: ```sql /* 检查是否存在MyISAM表 */ SELECT TABLE_NAME FROM information_schema.TABLES WHERE ENGINE = 'MyISAM' AND TABLE_SCHEMA NOT IN ('mysql','sys'); ``` - **JSON路径语法** MySQL 8.0要求JSON路径表达式严格使用`$`开头,例如: ```sql -- 5.7允许的写法 SELECT JSON_EXTRACT('{"data": 100}', 'data'); -- 8.0必须改为 SELECT JSON_EXTRACT('{"data": 100}', '$.data'); ``` #### 3. 测试环境验证 **步骤:** 1. **搭建测试环境** ```bash # 通过Docker快速部署MySQL 8.0实例 docker run --name mysql8-test -e MYSQL_ROOT_PASSWORD=123456 -p 3307:3306 -d mysql:8.0 ``` 2. **导入生产数据** ```bash mysqldump -u root -p --all-databases > full_backup.sql mysql -u root -p -h 127.0.0.1 -P 3307 < full_backup.sql ``` 3. **运行SQL测试集** 使用`pt-query-digest`工具回放生产环境的SQL流量,观察错误日志: ```bash tail -f /var/lib/mysql/mysql-error.log | grep -i 'error' ``` #### 4. 升级后必须调整的配置 - **sql_mode** MySQL 8.0默认启用`ONLY_FULL_GROUP_BY`,需检查GROUP BY语句是否符合规范[^2]: ```sql -- 查询当前sql_mode SELECT @@GLOBAL.sql_mode; ``` - **密码策略** 默认启用`caching_sha2_password`插件,旧客户端驱动可能需更新[^2]: ```sql ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'password'; ``` #### 5. 回滚预案 若升级后发现问题,需立即执行: ```bash # 停止MySQL 8.0服务 systemctl stop mysqld # 恢复备份数据 mysql -u root -p < full_backup.sql # 重新启动MySQL 5.7实例 ```
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值