ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL 5.7升级8.0启动失败?系统表MyISAM是罪魁祸首

MySQL 5.7升级8.0启动失败?系统表MyISAM是罪魁祸首 1. 事故现场5.7原地升级8.0.44服务直接起不来了1.1 这次升级是怎么做的去年底我手里有一套线上环境的MySQL还在跑5.7.44因为业务方提了一堆新需求想把小版本直接干到8.0.44。当时用的方式不算激进保留5.7的二进制目录不动下载官方8.0.44的二进制包解压后直接用新版本的mysqld去启动旧数据目录让MySQL自己完成原地升级。这套路在5.6升5.7、5.7升8.0早期版本时都挺顺的所以我一开始也没太当回事。结果升级窗口一开始就翻车了服务起不来客户端连接直接失败报错里赫然写着“系统表不支持MyISAM存储引擎”。我第一反应是“不可能”因为5.7的mysql系统表默认就是InnoDB怎么会冒出MyISAM后来在错误日志里面翻了半天又排查了配置和数据目录才搞明白整个链路。这篇文章不是复述官方文档而是把这次故障从现象、根因、排查到修复的完整过程记录下来给正准备做5.7升8.0、或者已经遇到这个报错的同行一个参照。1.2 你实际看到的报错长什么样这个故障最迷惑人的地方在于表面上是“登录报错”实际上问题出在启动阶段。我当时执行mysql -uroot -p结果返回ERROR 2002 (HY000): Cant connect to local MySQL server through socket /var/run/mysqld/mysqld.sock (2)这是典型的客户端连不上服务端因为mysqld进程根本没起来。再换个方式用TCP连提示ERROR 2013 (HY000): Lost connection to MySQL server at reading initial communication packet同样是连接被拒。如果服务还没到完全退出、处于半启动状态有时还会看到权限相关的拒绝信息甚至出现[ERROR] [MY-014060] [Server] Invalid MySQL server upgrade这样的日志行。我当时就是靠这几条日志才确认问题出在“启动期系统表校验”而不是“登录认证逻辑”。1.3 表面上是登录失败本质是启动期校验MySQL 8.0的服务器启动流程里有一个非常关键的动作在真正对外提供连接服务之前它要先加载数据字典、初始化系统表并且检查当前数据目录的状态是否满足升级要求。如果是正常情况下的冷启动这个检查非常快你不会有什么感知。但当你用新版本去打开一个还没升级完的旧数据目录时MySQL会进入“升级模式”逐个校验系统表当前使用的存储引擎、表结构、版本状态。只要有一个关键项不满足条件它就不会继续往下走。我这次的情况就是系统表检测出MyISAM校验直接failmysqld进程退出。对外表现就是登录报错因为压根没有服务进程在监听。很多人卡在这第一步一直在折腾客户端连接配置、权限、密码方向完全跑偏了。正确的做法是先看服务端日志把启动失败的原因找出来。2. 根因8.0里系统表早就不是MyISAM的地盘了2.1 系统表引擎的迁移简史要理解这个报错得先知道MySQL系统表的存储引擎是怎么演变的。早期版本比如MySQL 5.5及更早mysql系统库中的表基本都是MyISAM引擎这也符合当时MySQL对MyISAM的定位——简单、快速适合读多写少的元数据场景。到了5.6和5.7时代官方开始逐步把系统表迁到InnoDB。5.6中大部分系统表已经是InnoDB5.7基本上全量切换但因为架构上没有做强制收紧所以用户手动把某张系统表改回MyISAM也能正常运行只是不推荐。也就是说5.7的“宽松”给后面8.0的“严格”埋了雷。MySQL 8.0开始数据字典整体重构系统表不仅必须是InnoDB而且InnoDB还承担了数据字典的底层核心角色。官方从8.0.0起就明确系统表不支持MyISAM到8.0.44这一代启动时的升级校验已经做得很严格了。检测到MyISAM系统表就会直接拒绝启动不会给你“先起来再说”的机会。2.2 为什么升级时必须有这道检查有人可能会问MyISAM不是还能用吗为什么升级时非要较真这里面的逻辑不复杂。8.0的系统表升级不是简单的改几个字段它要做大量DDL操作新增列、重建索引、更新权限数据格式、写入新的数据字典元数据。这些操作全部依赖InnoDB的原子DDL能力。所谓原子DDL就是要么整个DDL成功要么失败后自动回滚到之前状态不会留下半个表结构。MyISAM不支持事务更不支持原子DDL。如果MySQL允许带MyISAM系统表去执行升级脚本中途一旦崩掉系统表可能处于“改了半截”的状态数据字典和实际表结构对不上整个实例基本就报废了。这就像一栋楼还在用砖混结构你硬要按钢结构大楼的方式在楼顶加层结果必然是承重出问题。所以在升级启动阶段MySQL宁可把服务拦死也不愿意带着一个不稳定的系统表结构去执行升级。这个“一刀切”看似粗暴其实是保护用户数据的最稳妥策略。2.3 MyISAM是怎么混进mysql系统库的三种常见来源弄清楚为什么必须有这道检查后另一个问题就来了5.7里系统表明明是InnoDB怎么到8.0校验时就变MyISAM了我这次排查下来发现来源主要有三个而且都很隐蔽。第一种是历史残留。早年间使用5.5或5.6环境时系统表是MyISAM或者被手动改过后来一路升级到5.7但某些表的引擎没跟着变回来。比如mysql.user、mysql.db、mysql.tables_priv这类表在5.6时代被改过引擎5.7升级时虽然能启动但引擎状态保持原样一直带到了8.0门口。第二种是逻辑导入或初始化脚本带入。有的人习惯用mysqldump --all-databases做迁移如果导出文件是通过老版本生成的里面可能带着ENGINEMyISAM的系统表建表语句。把这份dump导入到新5.7实例时系统表就会以MyISAM的形态存在。虽然5.7能容忍但8.0不认。第三种是配置文件的锅。有些人在[mysqld]段里面设置了default_storage_engineMyISAM然后用了某些初始化流程重新创建了系统表结果整批系统表都变成了MyISAM。这个操作在5.7里“成功”了但换到8.0就彻底失败。我这次现场最终定位到的来源属于历史残留和配置文件双重叠加。前者是主因后者是催化剂。3. 排查链路别急着重新初始化先看这三处3.1 第一处error log里真正关键的行遇到服务起不来第一步永远是看日志不是直接在客户端那折腾。默认路径一般就是/var/log/mysql/error.log或者数据目录下的*.err文件。我当时在日志里翻了半天先是看到一串InnoDB初始化信息接着就出现了类似下面这样几行[ERROR] [MY-014060] [Server] Invalid MySQL server upgrade: system table mysql.user does not support MyISAM storage engine. [ERROR] [MY-014060] [Server] Please convert the following tables to InnoDB before upgrading: mysql.user, mysql.db, mysql.tables_priv [ERROR] [MY-014060] [Server] Aborting这几行信息量很大。它告诉我三件事升级校验失败具体是哪几张表是MyISAM最后服务器强行中止。看到这里基本就能确定“登录报错”只是表象真正的问题在系统表引擎状态。如果你在日志里看到的是MY-014060相关字样基本可以跳过网络、权限排查直接进入引擎校验环节。3.2 第二处配置文件中可能从5.7带过来的坑日志确认根因方向后第二件事是检查配置文件。这里重点看两个参数[mysqld] default_storage_engineMyISAM # 或者旧写法 default-storage-enginemyisam如果存在这个配置先别急着急着改数据因为一旦启动配置生效你后续创建的任何临时表、日志表都可能再变成MyISAM边改边犯。我当时就是先把这个配置注释掉同时确认没有skip-innodb这种早就该进历史垃圾堆的参数再去处理表。这里还要顺带检查一下lower_case_table_names是否跟原环境一致。很多人升级后遇到表名找不到问题就是大小写配置不一致导致的虽然跟本案例不是同一个故障但容易混在一起干扰判断。3.3 第三处用旧版本临时启动确认表引擎配置看完下一步就是想方设法进数据库确认到底哪些表是MyISAM。但这有个经典悖论8.0.44起不来你没法正常登录去查直接做修复操作又看不到现场。我当时用了一个比较稳的办法保留的5.7.44二进制目录在这里派上了用场。我先停掉8.0进程虽然它没起来但还是确认一下没有残留进程然后用5.7.44的mysqld_safe去启动同一个数据目录。因为这套数据还没被8.0成功升级过5.7可以正常读取。启动后执行这条核心SQLSELECT table_schema, table_name, engine FROM information_schema.tables WHERE table_schema mysql ORDER BY table_name;结果一目了然mysql.user、mysql.db、mysql.tables_priv这几张关键系统表确实是MyISAM其余系统表都是InnoDB。到了这一步根因完全坐实5.7时期历史遗留的MyISAM系统表在8.0.44的升级校验中被拦下。4. 修复实操改回InnoDB再用8.0.44正常拉起4.1 临时用5.7.44把系统表统一改回InnoDB修复的思路其实很直接哪个表是MyISAM就把它改成InnoDB。5.7环境里这一步是允许的所以我在5.7.44下执行了ALTER操作。先看有哪些表需要处理我直接用SQL批量生成ALTER语句然后逐条执行SELECT CONCAT(ALTER TABLE mysql., TABLE_NAME, ENGINEInnoDB;) AS alter_stmt FROM information_schema.TABLES WHERE TABLE_SCHEMA mysql AND ENGINE MyISAM;这里要特别说一句不要把输出结果无脑拿去执行。先人工过一遍确认里面没有general_log、slow_log这类特殊的日志表。日志系统表通常是CSV引擎不属于MyISAM通常不会出现在这个结果集里。如果确实出现了也别急着改成InnoDB先确认是不是有自定义配置把日志表引擎改掉了。我这次实际执行的就是三条ALTERALTER TABLE mysql.user ENGINEInnoDB; ALTER TABLE mysql.db ENGINEInnoDB; ALTER TABLE mysql.tables_priv ENGINEInnoDB;每一步执行完MySQL都会重建表因为系统表本身数据量不大这个过程非常快基本秒级完成。执行完毕后再跑一遍普查SQL确认mysql库下已经没有任何MyISAM引擎的表然后再干净地关闭5.7进程。4.2 配置与残留文件的同步清理表引擎改完之后千万别直接切8.0还有两个收尾动作要做否则大概率还会再翻车。第一个是配置文件清理。前面提到的default_storage_engineMyISAM这类设置必须删除或者改成InnoDB。我当时直接把整行注释掉让MySQL使用默认的InnoDB。这个参数在8.0里如果还指向MyISAM即使系统表已经修好后续业务表也容易意外出现MyISAM没必要给自己留隐患。第二个是残留物理文件的检查。老版本数据目录里可能会存在.MYD、.MYI这类MyISAM专属文件升级到8.0之后这些文件就没有意义了。当时我特意在数据目录下扫了一遍发现mysql子目录里有几个历史遗留的.MYI文件虽然不是致命问题但留着容易误导后续排查。清理之前我确认了system表都已经重建为InnoDB也就是.ibd文件才是当前有效的表空间文件旧文件直接备份到别处即可。4.3 重启8.0.44并验证登录与自动升级配置和文件都处理干净后我重新用8.0.44的mysqld启动数据目录。这次启动流程明显不一样了日志里能看到自动执行系统表升级的动作从数据字典的版本对比到权限表的格式更新一路顺利跑完最后出现了“ready for connections”的日志行。这个时候再执行客户端连接mysql -uroot -p正常进入。再验证一下现在的版本信息SELECT VERSION();返回8.0.44。接着我又做了一次引擎普查确认mysql库下所有表都是InnoDB同时随机抽查了几个业务库的表确保没有业务表被错误改成MyISAM。至此整个修复闭环完成。顺便提一句8.0.16之后mysql_upgrade工具已经废弃了系统表升级由服务器启动时自动完成。所以你不需要再去手动执行什么升级脚本只要数据目录和配置没问题启动即升级。5. 升级前的检查清单5分钟体检避免同一个坑5.1 引擎普查脚本升级前必跑一次经历过这次故障之后我给自己定了条规矩任何人做跨大版本升级动手前先花五分钟跑一次系统表引擎普查。脚本很简单就是前面那条SQLSELECT table_schema, table_name, engine FROM information_schema.tables WHERE table_schema mysql AND engine ! InnoDB;只要这个查询有任何返回结果就先停下来处理。因为8.0对mysql库的引擎要求是强制性的不是“建议InnoDB”而是“必须InnoDB”。把这条SQL放到升级前的操作清单里能直接避免一大类启动失败问题。还要再看看数据目录里有没有历史遗留的.MYD、.MYI文件有的话做一个备份后移出去别让它们干扰后面的诊断。5.2 目录拷贝、逻辑导入、二进制原地升级三种场景分别注意什么很多人升级的方式不一样踩坑位置也会有差异我这里把三种常见场景分开说。如果是像我这次一样用新版本二进制直接启动旧数据目录做原地升级重点是检查系统表和配置文件确保没有任何MyISAM残留。这个方案牵涉最少但一旦失败回滚通常只能靠之前保留的旧二进制目录。所以升级前一定把旧版本的整个安装目录和数据目录都备份好我就是靠备份的5.7二进制救回来的。如果是直接拷贝数据目录到新机器再升级而不是走逻辑导出那还要多检查一层机器名、auto.cnf里的server_uuid、ibdata1的兼容性。我见过有人把数据目录拷过去后新旧实例的server_uuid冲突导致主从环境直接乱套。这类问题跟引擎校验叠加起来排查会非常痛苦建议分步验证。如果是走mysqldump的逻辑导入方式升级重点反而在dump文件本身。老版本导出的SQL里如果系统表部分带了ENGINEMyISAM导入到8.0时就会直接踩雷。我建议dump完以后先grep一下有没有可疑的建表引擎关键字特别是mysql库相关部分提前处理掉再导入。5.3 最后补一句MyISAM业务表在8.0里的定位这次故障虽然只发生在系统表上但顺带说一下业务表的情况。MySQL 8.0.44里业务表如果还是MyISAM引擎官方目前还没有完全禁止使用启动和日常读写基本不受影响但它已经被标记为过时deprecated后续版本大概率会逐步收紧。高并发写入场景下MyISAM的表级锁会让并发性能非常难看。如果你在升级前普查时顺手发现业务库里有MyISAM表建议这次升级直接把它们一并迁到InnoDB。步骤也不复杂就是逐表执行ALTER TABLE 库名.表名 ENGINEInnoDB;大表单独评估在线DDL时间窗口。我这次处理完系统表后顺手把两台从库上共十来张历史遗留的MyISAM业务表也一起改了反正已经进入维护窗口了一次性做掉比以后再开一次窗口划算得多。这次故障复盘下来最深的感受是升级MySQL真正翻车的地方往往不在那些高大上的新特性上而是数据目录里那些平时根本没人关注的边缘状态。系统表引擎就是其中最典型的例子。5.7时代觉得“还能跑就行”到了8.0这里直接就给你一个下马威。我的建议是别怕报错顺着日志一层层剥先定位根因再动手改千万别一着急就把整个数据目录初始化了。保存旧版本二进制、备份数据目录、跑一遍引擎普查这三件事做好了这个坑基本就绕开了。
返回列表