先说明一下:标题提到的MySQL 8.0.30在本文写作时(2022年1月)尚未正式发布,目前最新稳定版是8.0.28。本文基于MySQL 8.0的实际使用经验,总结升级和使用过程中的常见坑和实战经验。
MySQL 8.0是MySQL的一个大版本,引入了很多新特性:窗口函数、CTE、JSON增强、原子DDL、新的默认认证插件(cachingsha2password)、隐藏索引、降序索引等。这些新特性很好用,但8.0也有不少兼容性问题和坑。
我用MySQL 8.0有几年了,从5.7升级到8.0,也在8.0上做了很多项目。踩过不少坑,有些坑让我熬夜排查,印象深刻。今天分享这些踩坑经验,包括升级问题、性能问题、兼容性问题、运维问题等,希望能帮大家少踩坑,少熬夜。
一、升级相关的坑
从5.7升级到8.0,是最容易出问题的环节。
1. 认证插件不兼容
MySQL 8.0默认的认证插件从mysqlnativepassword改成了cachingsha2password。这个插件更安全,但很多老客户端(比如旧版本的Navicat、PHP的mysqlnd、一些老的ORM框架)不支持,连接时会报"Authentication plugin 'cachingsha2password' cannot be loaded"的错误。
解决方案:
- 升级客户端到支持cachingsha2password的版本
- 或者把用户的认证插件改回mysqlnativepassword:
``sql ALTER USER 'user'@'%' IDENTIFIED WITH mysqlnativepassword BY 'password'; ``
- 或者在my.cnf里设置defaultauthenticationplugin=mysqlnativepassword(8.0已经废弃这个参数,不推荐)
我第一次升级8.0的时候,就遇到了这个问题。应用连不上数据库,排查了半天才发现是认证插件的问题。升级前一定要检查所有客户端和驱动是否支持新的认证方式。
2. 保留字增加
MySQL 8.0增加了一些保留字,比如ROWS、RECURSIVE、SYSTEM、WINDOW等。如果你的表名或字段名用了这些保留字,升级后会报错。
比如5.7里可以用rows做字段名,8.0里就不行了,必须加反引号。
解决方案:
- 升级前用mysqlcheck检查有没有保留字冲突
- 或者给字段名加反引号
- 最好的方式是改字段名,避免用保留字
3. 数据字典变化
MySQL 8.0把数据字典从MyISAM表改成了InnoDB表,实现了原子DDL。这意味着DDL操作(CREATE TABLE、ALTER TABLE等)要么全部成功,要么全部回滚,不会出现5.7里那种DDL中断后表损坏的情况。
但这也带来了一些问题:
- 不能直接修改mysql库下的系统表了(比如直接UPDATE mysql.user改密码),必须用SQL语句
- 一些老的运维脚本(直接操作系统表的)会失效
- frm文件没了,表结构都存在数据字典里
升级前要检查有没有直接操作系统表的脚本,及时改造。
4. 升级顺序
MySQL 8.0不支持从5.6直接升级,必须先升到5.7再升8.0。而且5.7必须是5.7.9以上版本。
升级方式:
- 原地升级:停库,替换二进制文件,启动,跑mysql_upgrade(8.0.16后自动升级数据字典)
- 逻辑导出导入:用mysqldump导出,在8.0上导入
- 推荐用逻辑导出导入,更安全,虽然慢一点
升级前一定要备份!一定要备份!一定要备份!重要的事情说三遍。
二、SQL和性能相关的坑
1. ONLYFULLGROUP_BY默认开启
MySQL 8.0默认开启了ONLYFULLGROUP_BY模式。这意味着GROUP BY查询中,SELECT里的非聚合列必须出现在GROUP BY里,否则报错。
5.7里这个模式也有,但很多人关掉了。8.0默认开启后,一些老的SQL会报错:
-- 8.0里会报错,因为name不在GROUP BY里
SELECT id, name, COUNT(*) FROM table GROUP BY id;解决方案:
- 改SQL,把非聚合列加到GROUP BY里,或者用ANY_VALUE()包裹
- 或者在my.cnf里关掉ONLYFULLGROUP_BY(不推荐,会影响数据准确性)
我踩过这个坑,升级后一批老的报表SQL报错,连夜改SQL。建议升级前就把所有SQL检查一遍。
2. 隐式类型转换导致索引失效
MySQL 8.0对隐式类型转换的处理更严格了。如果字段是字符串类型,查询时用数字,会导致索引失效。
比如字段phone是varchar类型,查询时写WHERE phone = 13800138000(数字),8.0里会不走索引,全表扫描。5.7里可能还能走索引。
解决方案:
- 查询时类型要匹配,字符串字段用字符串值:WHERE phone = '13800138000'
- 或者用CAST显式转换
这个问题很隐蔽,升级后可能发现某些查询突然变慢了,就是隐式转换导致的。
3. 窗口函数的性能坑
窗口函数是8.0的好特性,但用不好也有性能问题。比如ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...),如果PARTITION BY的列基数很大,或者没有合适的索引,会很慢。
我曾经写了一个窗口函数查询,数据量几百万,跑了几十秒。后来加了合适的复合索引,优化到了1秒内。
建议:
- 窗口函数的PARTITION BY和ORDER BY列要建索引
- 大数据量下,窗口函数可能不如子查询+临时表快,要实际测试
- 用EXPLAIN ANALYZE分析执行计划
4. CTE(公用表表达式)的物化问题
8.0支持WITH ... AS (...)的CTE语法。但CTE默认是不物化的,每次引用都会重新计算。如果CTE被多次引用,而且计算复杂,会很慢。
8.0.14以后支持WITH ... AS (... ) /+ MATERIALIZE /来强制物化CTE。或者把CTE改成临时表。
我遇到过一个CTE被引用了3次,每次都重新计算,查询很慢。加了MATERIALIZE提示后,性能提升了好几倍。
三、InnoDB相关的坑
1. 大事务导致undo日志膨胀
MySQL 8.0的undo日志可以放在独立的表空间里,也支持在线收缩。但如果有大事务长时间不提交,会导致undo日志膨胀,占用大量磁盘空间。
我遇到过一次,一个开发同学跑了一个大事务,更新了几百万行数据没提交,undo表空间涨到了几十G,磁盘快满了。最后只能杀掉事务,然后收缩undo表空间。
建议:
- 避免大事务,分批提交
- 监控长事务,设置innodbrollbackon_timeout
- undo表空间单独放,设置自动收缩(innodbundolog_truncate=ON)
2. 自增主键耗尽
很多表用INT自增主键,INT的最大值是21亿。如果表数据增长快,可能会耗尽自增ID,导致插入报错。
8.0里可以用BIGINT做主键,或者用UUID。但UUID有性能问题(乱序导致页分裂)。推荐用BIGINT自增,或者雪花算法生成有序ID。
建议:
- 建表时主键用BIGINT UNSIGNED,不要用INT
- 监控自增ID的使用量,快到上限时提前处理
3. 死锁和锁等待
MySQL 8.0的锁机制和5.7基本一致,但有一些细节变化。比如8.0里SELECT ... FOR UPDATE NOWAIT和SKIP LOCKED,用不好可能导致死锁。
建议:
- 开启innodbprintall_deadlocks,记录死锁日志
- 业务上尽量按相同顺序访问表,减少死锁
- 用SKIP LOCKED做任务队列时,要注意事务隔离级别
4. Buffer Pool预热
MySQL重启后,Buffer Pool是空的,刚启动时性能很差,因为所有查询都要从磁盘读数据。8.0支持Buffer Pool自动转储和恢复,重启时自动加载热数据。
配置:
innodb_buffer_pool_dump_at_shutdown=ON
innodb_buffer_pool_load_at_startup=ON这个功能默认是开启的,但如果重启时没正常关闭(比如kill -9),就不会转储。建议正常关闭数据库,或者手动dump buffer pool。
四、运维相关的坑
1. 慢查询日志格式变化
MySQL 8.0的慢查询日志增加了一些字段,比如logslowextra,可以记录更多信息(用户、主机、线程ID等)。但一些老的慢查询分析工具(比如pt-query-digest的老版本)可能不兼容新格式。
建议升级分析工具,或者关闭logslowextra保持旧格式。
2. 备份工具兼容性
mysqldump在8.0里有一些变化,比如默认开启--column-statistics,老版本的mysqldump不能备份8.0的库。一定要用8.0版本的mysqldump来备份8.0的数据库。
xtrabackup也要用支持8.0的版本(Percona XtraBackup 8.0),老版本不兼容。
3. 主从复制的坑
8.0的主从复制有一些新特性:
- 默认开启GTID
- 支持二进制日志事务压缩(binlogtransactioncompression)
- 支持并行复制(writeset模式)
但升级主从时要注意:
- 先升从库,再升主库
- 5.7的从库不能复制8.0的主库(高版本到低版本不支持)
- 升级后检查复制状态,确保没有延迟和错误
我遇到过一次,主库升了8.0,从库还是5.7,复制直接断了。只能把从库也升了,或者重新搭从库。
4. 密码管理
8.0的密码管理更严格了:
- 密码有过期策略(defaultpasswordlifetime)
- 密码历史记录(password_history),不能重复用旧密码
- 密码强度验证(validate_password组件)
这些安全特性很好,但如果不知道,可能会突然发现密码过期了连不上。建议:
- 合理设置密码过期时间,或者对应用账号设置不过期
- 监控密码过期时间,提前修改
- 应用账号不要设置太严格的密码策略
五、性能优化经验
最后分享一些MySQL 8.0的性能优化经验。
1. 合理使用索引
- 用EXPLAIN分析查询,确保走索引
- 复合索引遵循最左前缀原则
- 不要在索引列上用函数或运算
- 定期用ANALYZE TABLE更新统计信息
- 8.0支持隐藏索引(INVISIBLE),可以测试删除索引的影响,不用真的删
2. 优化SQL
- 避免SELECT *,只查需要的列
- 分页用延迟关联(先查ID再关联),避免深分页
- 大表分页用游标(WHERE id > last_id LIMIT 10)
- 用EXISTS代替IN(大数据量下)
- 合理使用批量插入,减少事务次数
3. 配置优化
- innodbbufferpool_size设为物理内存的50%-70%
- innodbflushlogattrx_commit=2(允许丢1秒数据,性能好)或1(安全,性能差)
- sync_binlog=1000(批量刷binlog,性能好)或1(安全)
- max_connections根据实际情况设置,不要太大
- 临时表和排序缓存合理设置
4. 架构优化
- 读写分离,主库写从库读
- 分库分表,大表拆小表
- 加缓存(Redis),减少数据库压力
- 用消息队列削峰填谷
- 定期归档历史数据
六、写在最后
MySQL 8.0是一个很优秀的版本,新特性多,性能好,安全性高。但升级和使用过程中确实有不少坑,需要小心应对。
本文总结了我踩过的一些坑:认证插件不兼容、保留字冲突、ONLYFULLGROUP_BY、隐式类型转换、大事务、主从复制问题等。这些坑很多人都踩过,希望我的经验能帮大家少走弯路。
MySQL 8.0还在持续更新,8.0.30及以后的版本会有更多新特性和bug修复。升级前一定要做好测试,在测试环境验证没问题了再升生产环境。
最后,记住数据库运维的三原则:备份、备份、还是备份。任何操作前都要备份,出了问题才能恢复。
2022年了,MySQL依然是最流行的关系型数据库。掌握MySQL,是每个后端和运维工程师的基本功。希望这篇文章对你有帮助。如果你有其他踩坑经验,欢迎在评论区交流。
祝大家的数据库永远稳定,查询永远飞快,永不宕机。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录