ARTICLE DETAIL

资讯详情

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

MySQL 5.7与8.0双版本共存:Windows与Linux多实例部署全攻略

MySQL 5.7与8.0双版本共存:Windows与Linux多实例部署全攻略 我干数据库运维这些年经常遇到一个挺尴尬的需求同一个开发机上老项目用的是 MySQL 5.7新项目非得上 8.0 的新特性两边都是线上在跑的业务谁也不能迁。更别说有些公司内部历史包袱重生产环境同时存在 5.6、5.7、8.0 三个大版本本机要是不能同时模拟这套组合联调测试根本没法定。所以“一台机器装两个 MySQL 版本”根本不是闲着没事折腾自己而是实际工作中非常典型的场景需求。这篇内容我直接把我平时在一台 Windows 服务器和一台 Linux 服务器上分别部署 5.7 和 8.0 的具体做法、踩过的坑、以及最终的切换方案摊开来讲保证你跟着操作能一次搞定。1. 双版本共存的核心思路与方案选择1.1 为什么非要装两个版本而不是直接升级先说一个经常被问到的点既然新版能向下兼容为什么不直接把老版本升级了这里面有个特别现实的问题MySQL 的所谓兼容是 SQL 层面的不是实例之间的随意替换。很多老项目用的是 MyISAM 表5.7 还能跑得挺好到了 8.0 不少默认配置直接变了比如认证插件从 mysql_native_password 换成了 caching_sha2_password老应用用的旧版 JDBC 驱动根本连不上。还有 sql_mode 默认值的变化8.0 默认带 ONLY_FULL_GROUP_BY以前那种写 half 一不小心就 bt 的 SQL 在 5.7 里没声音换上 8.0 直接报错。这就导致很多项目根本不敢随便动版本而新项目又巴不得用上 8.0 的窗口函数、CTE 和更好的优化器。两边的诉求僵在这多版本共存就成了唯一的务实解。1.2 双版本方案的对比容器化 还是 多实例遇到多版本需求第一反应是上 Docker 容器跑 MySQL确实是最干净的方法一条命令拉镜像、起容器就完事版本隔离得也彻底。但容器方案在两类场景下比较吃亏一类是公司内网机器没有外网权限无法直接拉取镜像离线导入镜像包又麻烦另一类是你需要长期在本地开发、经常跑一些大数据量的 SQL容器里磁盘 IO 和路径映射做不好性能损失和调试成本都是实打实的。所以不少老运维骨子里还是倾向于用免安装版或者二进制包在裸机上跑双实例配置文件、数据目录、端口全部独立想停哪个停哪个遇到诡异问题还能直接看物理日志排查心里有底。1.3 核心原则一个版本一个目录一套配置双版本共存之所以很多人搞炸就是脑子里没绷住一条线两个版本必须完全独立所有相关资源都要分开。具体来说就是三个东西不能共用端口不能共用、数据目录不能共用、配置文件不能共用。Windows 下用 my.iniLinux 下用 my.cnf一个版本一份里面各写各的端口、socket、路径。另外 Windows 下还要注意注册为系统服务时名称不能一样Linux 下 systemd 的 service 单元文件也要拆开。只要守住了“各用各的”这条底线剩下的就是按部就班地配置并不复杂。下面我分 Windows 和 Linux 两套场景来讲你在哪台机器上操作就直接对照哪个小节思路是一模一样的。2. 动手之前版本下载与关键参数规划2.1 版本选择ZIP 包还是安装包Windows 平台强烈建议下载 ZIP Archive 免安装版千万不要下载那个图形化 MSI 安装包。MSI 安装包默认会往 Program Files 目录塞安装时还会自动配置系统服务装第二个版本的时候经常出现配置界面冲突而且卸载时容易把原先好好的服务一起干掉。ZIP 版解压即用自己手动配置次数多一次但干净可控出问题也知道是什么原因。我这边 5.7 用的版本号是 mysql-5.7.44-winx64.zip8.0 用的是 mysql-8.0.36-winx64.zip这两个都是相对稳定的版本推荐直接用。2.2 端口、数据目录、Socket 这三件套装双版本之前先在心里做好一个简单的资源规划表后面配置时按表填就行不容易乱。资源项MySQL 5.7MySQL 8.0安装目录D:\mysql-5.7.44-winx64D:\mysql-8.0.36-winx64数据目录D:\mysql-5.7.44-winx64\dataD:\mysql-8.0.36-winx64\data端口33063307服务名MySQL57MySQL80字符集utf8mb4utf8mb4端口这个点要单独说一下如果两个版本里有一个是公司标准的默认端口使用习惯那你最好把不常用的那个换掉。比如团队习惯用 3306 连库就保留 5.7 在 33068.0 换到 3307。如果两边都是新项目那就随便只要不冲突就行。Linux 下还有一个 socket 文件要考虑5.7 用 /tmp/mysql57.sock8.0 用 /tmp/mysql80.sock别共用同一个否则客户端连接时经常乱套。2.3 服务名和命令工具的环境变量处理Windows 下把 MySQL 注册成系统服务是为了方便用 net start 和 net stop 来控制启动停止。安装多版本时服务名绝对不能用默认的 MySQL两个版本都用同一个名字后面的安装会直接覆盖掉前面的服务到时候大概率一脸懵。我的习惯是带版本号命名MySQL57、MySQL80一眼就能认出是哪个实例。环境变量方面PATH 里最终只能保留一个版本的 bin 目录否则命令行输入 mysql -uroot -p 时到底调起哪个版本全靠缘分。我的做法是 PATH 里放 8.0 的 bin需要连 5.7 时就用全路径或者直接 mysql -h127.0.0.1 -P3307 显式指定端口这样最不容易混淆。3. Windows 环境下的双版本详细安装流程3.1 初始化 MySQL 5.7 实例端口 3306先把 ZIP 包解压到指定目录然后在 D:\mysql-5.7.44-winx64 下新建一个 my.ini 文件。这里特别提醒一句解压后的目录默认是没有 my.ini 的很多人第一次装会直接双击运行 mysqld.exe然后系统弹个报错说缺少配置文件这不代表装失败了只是你没有给这个实例定义任何启动参数。我的 my.ini 内容比较精简但其实每一条都是有讲究的[mysqld] port3306 basedirD:/mysql-5.7.44-winx64 datadirD:/mysql-5.7.44-winx64/data character-set-serverutf8mb4 default-authentication-pluginmysql_native_password [client] port3306 default-character-setutf8mb4注意 basedir 和 datadir 里的斜杠方向用正斜杠最稳反斜杠有时候会被转义解析出问题。character-set-server 设成 utf8mb4 是为了避免后面创建表出现中文乱码。default-authentication-plugin 这一项建议在 5.7 里显式写上 mysql_native_password因为后面如果要迁移或者老客户端连接这个插件兼容性最好。写好配置文件后用管理员身份打开 CMD进入 bin 目录执行初始化mysqld --defaults-fileD:/mysql-5.7.44-winx64/my.ini --initialize-insecure这里用的是 --initialize-insecure意思是初始化数据目录时给 root 账号设一个空密码。如果不带 insecure 参数MySQL 会生成一串随机密码写在日志里第一次登录还要去翻错误日志找密码特别烦。Insecure 模式初始化完成后直接用 mysql -uroot 就能登录然后你再自己 ALTER USER 改密码这个流程是最顺手。初始化过程一般 10 到 20 秒结束后检查一下 data 目录下是不是生成了一堆文件再确认一下有没有 .err 后缀的日志文件这个日志就是后续排查问题的第一现场。3.2 把 5.7 注册成 Windows 服务继续在管理员 CMD 里执行mysqld --defaults-fileD:/mysql-5.7.44-winx64/my.ini --install MySQL57执行完如果没有报错在服务管理器里应该能看到一个叫 MySQL57 的服务。这里有个细节注册服务时 --defaults-file 参数必须写完整路径而且路径里不要用中文和空格否则服务启动时读不到配置文件会直接启动失败。注册成功后再启动服务net start MySQL57启动时如果报“服务名无效”或者“服务没有响应控制功能”多半是两种原因一是注册服务时路径写错二是 data 目录初始化失败。去 Windows 事件查看器的应用程序日志里能捞到详细报错但更直接的还是看 data 目录里的 .err 日志文件通常第一行就会写清楚为什么启动不了。3.3 初始化 MySQL 8.0 实例端口 3307接下来同样解压 8.0 的 ZIP 包到 D:\mysql-8.0.36-winx64然后在新目录下创建 my.ini。8.0 的配置和 5.7 最大的区别就是端口、目录以及认证插件这块。8.0 默认的认证插件已经是 caching_sha2_password如果你没有老客户端的兼容压力保持默认就好不专门配置 default-authentication-plugin 这一项。如果确实要兼容老项目比如比你手头 8.0 还老的 JDBC 版本可以像我一样在配置里写上[mysqld] port3307 basedirD:/mysql-8.0.36-winx64 datadirD:/mysql-8.0.36-winx64/data character-set-serverutf8mb4 mysqlx-port33080 [client] port3307 default-character-setutf8mb4注意这里多了一个 mysqlx-port33080。MySQL 8.0 自带 X Plugin默认监听 33060 端口同一台机器上如果 5.7 没有占这个端口那可能没问题但如果你机器上还跑了别的组件占了 33060启动 8.0 时会报端口冲突。我习惯显式指定 mysqlx-port彻底避免这个隐性问题。初始化命令同样用mysqld --defaults-fileD:/mysql-8.0.36-winx64/my.ini --initialize-insecure8.0 的初始化时间和 5.7 差不多完成后检查 data 目录。然后注册服务并启动mysqld --defaults-fileD:/mysql-8.0.36-winx64/my.ini --install MySQL80 net start MySQL80到这里两台“精神分裂”的 MySQL 已经各自跑起来了。你别慌着开始建库我建议先分别验证一下端口监听状态。执行 netstat -ano | findstr 3306 和 findstr 3307确认两个端口有没有同时处于 LISTENING 状态。这是排查双版本问题最直观的手段比反复刷客户端界面要靠谱得多。3.4 Windows 下如何优雅地切换使用两个版本服务都起来了接下来就是怎么连的问题。我平时习惯用命令行客户端但区分连哪个实例就靠端口和 IP 的组合连接命令后面加 -h127.0.0.1 -P3307 就是连 8.0加 -h127.0.0.1 -P3306 就是连 5.7。如果你用的工具是 Navicat 或者 DBeaver新建连接时填不同的端口就行这两个工具都支持同时配置多个 MySQL 连接并不需要在界面上做什么版本切换。还有一个容易被忽略但很实用的切换方式你可以直接停掉其中一个服务然后单独用另一个版本占默认端口。比如临时要把 8.0 的端口从 3307 改成 3306那么先把 MySQL57 服务停止再改 8.0 的 my.ini 端口为 3306重启 MySQL80 服务即可。但这种做法不建议经常用因为端口切换会牵动数据连接串和相关配置搞不好就忘了改回来留着两个固定端口长期共存反而更省心。4. Linux 环境下的双版本部署实操4.1 使用通用二进制包避免 RPM 冲突Linux 下装多版本 MySQL 比 Windows 还要更常见。服务器上南美大型企业配置复杂但规则倒是很清晰用官方提供的 Generic Linux 二进制包不要用 yum/apt 的仓库包。仓库里的 mysql-server 安装后会把文件分散到 /usr/bin、/var/lib/mysql 这些位置再装第二个版本时包管理器和文件路径冲突能让你怀疑人生。Generic 包解压之后是个完整的目录二进制、库文件、支持文件都在里面天然适合多实例。我在一台 CentOS 7 上做过 5.7 8.0 双实例部署目录规划如下资源项MySQL 5.7MySQL 8.0二进制目录/usr/local/mysql-5.7.44/usr/local/mysql-8.0.36数据目录/data/mysql57/data/mysql80配置文件/etc/my-5.7.cnf/etc/my-8.0.cnf端口33063307socket/tmp/mysql57.sock/tmp/mysql80.sock服务单元mysqld57.servicemysqld80.service4.2 创建专用用户和数据目录先下载对应版本的 Generic 二进制包并解压到 /usr/local 下然后创建数据目录、设置好属主。这一步不能省略也别偷懒直接用 root 跑 MySQL。groupadd mysql useradd -r -g mysql -s /bin/false mysql mkdir -p /data/mysql57 /data/mysql80 chown -R mysql:mysql /data/mysql57 /data/mysql80这里创建了一个不能登录的系统用户 mysqlMySQL 进程将以这个用户身份运行这是安全基线的一部分。要是用 root 直接跑万一应用层 SQL 注入拿到权限攻击者就直接拿到数据库进程的权限了风险太大。4.3 5.7 和 8.0 的 my.cnf 配置要点Linux 下的配置文件同样要区分开5.7 的 /etc/my-5.7.cnf 内容我一般这样写[mysqld] usermysql port3306 basedir/usr/local/mysql-5.7.44 datadir/data/mysql57 socket/tmp/mysql57.sock character-set-serverutf8mb4 pid-file/data/mysql57/mysqld.pid log-error/data/mysql57/mysql57.errwrite 一个容易被忽略的细节socket 文件路径一定要明确指定不要用默认的 /var/lib/mysql/mysql.sock否则两个实例会争抢同一个 socket 文件。客户端连接时通过 -S /tmp/mysql57.sock 才能连到具体实例。log-error 这个参数也建议显式设置MySQL 的报错信息会统一写到这个文件里排查问题不用到处翻日志。8.0 的 /etc/my-8.0.cnf 与 5.7 大体相同端口改为 3307路径改成自己对应的再注意加一行[mysqld] ... port3307 datadir/data/mysql80 socket/tmp/mysql80.sock mysqlx-port330808.0 的缓存和索引默认参数改了不少但双实例部署时正式库的参数调优先放到后面根据业务再调。只要把路径、端口、socket 这几项独立开基本就成功了一大半。4.4 初始化两个实例5.7 的初始化进入 /usr/local/mysql-5.7.44/bin 目录执行./mysqld --defaults-file/etc/my-5.7.cnf --initialize-insecure --usermysql8.0 的初始化同理./mysqld --defaults-file/etc/my-8.0.cnf --initialize-insecure --usermysql注意一点如果数据目录里已经有文件初始化命令会报错提示目录非空。如果之前初始化失败想重来先确保数据目录删干净再执行。初始化完成后数据目录下会生成大量系统数据库文件并且 root 密码为空这一步代表初始化动作生效了。4.5 用 systemd 管理双实例CentOS 7 及以上环境建议编写两个独立的 systemd 服务单元比直接用 mysqld_safe 启动更符合常态运维习惯。我的 mysqld57.service 内容如下[Unit] DescriptionMySQL 5.7 Server Afternetwork.target [Service] Typesimple Usermysql Groupmysql ExecStart/usr/local/mysql-5.7.44/bin/mysqld --defaults-file/etc/my-5.7.cnf LimitNOFILE65535 Restarton-failure [Install] WantedBymulti-user.targetmysqld80.service 的 ExecStart 换成 8.0 的 mysqld 路径和配置文件即可。把两个服务单元放到 /etc/systemd/system/ 目录下然后执行systemctl daemon-reload systemctl start mysqld57 systemctl start mysqld80 systemctl enable mysqld57 systemctl enable mysqld80启动后先用端口验证ss -lntp | grep 3306 ss -lntp | grep 3307两条命令输出里应该能看到两个 mysqld 进程分别监听各自端口。如果发现两个进程只监听了一个端口或者 3306 端口被一个进程同时占用优先去对应配置文件对应的 .err 日志文件里看有没有端口绑定报错。4.6 登录验证与本地 socket 连接Linux 下用命令行连接裸机 MySQL 有个常踩的坑直接执行 mysql -uroot 不加 -S 时客户端会去找默认 socket 路径而那个路径很可能和你配置的 socket 不一致结果报一个Cant connect to local MySQL server through socket。解决办法是显式指定 socket 文件/usr/local/mysql-5.7.44/bin/mysql -uroot -S /tmp/mysql57.sock /usr/local/mysql-8.0.36/bin/mysql -uroot -S /tmp/mysql80.sock或者用 TCP 方式指定端口mysql -h127.0.0.1 -P3306 -uroot mysql -h127.0.0.1 -P3307 -uroot我习惯用 TCP 方式因为后续开发环境的连接串基本都写的 IP 加端口这样验证和实际运行的环境更接近。5. 双版本实战中最容易踩的坑排查实录5.1 端口冲突和连接失败的排查思路最常见的问题就是第二个 MySQL 启动时直接报Bind on TCP/IP port: Address already in use。这个其实不难解决先用netstat -ano | findstr 3307Windows或ss -lntp | grep 3307Linux看端口被谁占用。如果确实有进程在监听说明你配置文件里的端口跟现有服务冲突换个端口就好。还有一种特殊情况端口没被占但启动时依然报错一直起不来这时候去 data 目录对应的 .err 日志里看日志里会明确告诉你 bind 失败是在 IPv6 还是 IPv4 层大多数情况下MySQL 同时 bind 了 :: 和 0.0.0.0如果系统禁用了 IPv6 也会导致诡异情况。我遇到过一台系统把 IPv6 关了MySQL 在绑定 IPv6 失败后干脆连带 IPv4 也起不来这种就需要在配置文件里加bind-address127.0.0.1让 MySQL 只监听 IPv4。5.2 PATH 环境变量导致命令全是同一个版本双版本装好后很多人会发现自己执行 mysql -V 永远只显示一个版本而且很容易搞混当前连的是哪个。这个问题的根源在于 PATH 环境变量只保留了一个 MySQL 的 bin 目录系统会优先找到第一个匹配的 mysql 命令。解决办法有几个思路一是在 PATH 里保留你日常使用更频繁的那个版本二是少依赖 PATH直接用完整路径三是干脆在用户目录下建一个别名映射比如把 mysql57 指向 5.7 的 mysql 命令把 mysql80 指向 8.0 的 mysql 命令。我个人的习惯是 PATH 保留 8.0然后给 5.7 的 mysql 命令建一个别名这样进终端后输入别名就能准确连到对应版本不用记长串路径。5.3 配置文件被读串了如果两个实例都启动成功但你发现改了 my.ini 里的参数后重启没生效十有八九是配置文件读取顺序搞混了。MySQL 的默认配置读取顺序很“诡异”它会依次读取 /etc/my.cnf、/etc/mysql/my.cnf、系统变量里的默认路径以及命令行 --defaults-file 指定的文件。如果你把两个版本的配置都写在同一个默认文件里那后启动的实例会读到这个文件然后部分参数互相覆盖完全不知道当前实例实际生效的是哪套配置。最稳妥的做法就是每次启动都显式用 --defaults-file 参数指定配置文件并且启动脚本、systemd 单元、注册服务命令里全部保持同一个路径不要让 MySQL 有机会去读全局默认配置。5.4 初始化失败或数据目录半成品问题初始化失败是很常见的事尤其是 Windows 下直接在解压目录下执行 mysqld --initialize 时data 目录已经生成了几个半成品文件然后你以为清理了 data 目录重来结果发现还是报错。原因可能是 data 目录里有隐藏文件如 .err、.pem或者目录本身没删干净。Windows 下建议直接在资源管理器里把整个 data 目录删除再重新创建Linux 下则用 rm -rf /data/mysql57/* 清空但删除前要确认目录路径别写错否则会把不该删的删了。还有一种情况是初始化时报 Cant create directory大概率是权限问题Linux 下用 chown 把数据目录属主改成 mysql 用户就能解决。5.5 连接认证方式不兼容导致应用报错数据库本地双版本装好了结果应用连 8.0 时一堆报错最常见的错误是Authentication plugin caching_sha2_password cannot be loaded。这是因为 8.0 默认认证插件是 caching_sha2_password而老版本驱动不认识。解决方式有两个一个是把用户的认证插件改回 mysql_native_password在 8.0 实例里执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;另一个是升级应用使用的 JDBC 驱动到 8.0.x 以上版本驱动升级后就能直接支持 caching_sha2_password。我建议能升级驱动就升级驱动因为 8.0 团队后续版本对老认证插件的支持会越来越弱长期靠改认证方式维持不是正经路子。5.6 备份恢复和跨版本导数据的小提示双版本共存有一个隐藏福利你可以直接把 5.7 的库导出再导入 8.0来做一次真实的升级演练。用 mysqldump 导出时建议加 --set-gtid-purgedOFF 这个参数如果开启了 GTID否则导入 8.0 时会产生一堆 GTID 相关报错。还有字符集问题导出时显式带上 --default-character-setutf8mb4导入时也一致避免中文乱码。跨大版本导数据时有个别存储过程和触发器可能出现兼容问题导进去之后记得抽查一下关键存储过程能否正常执行。我在一次项目迁移中就是靠双版本环境反复导入验证才赶在正式升级前发现 8.0 的 sql_mode 变化把一个老存储过程的 GROUP BY 写法卡住了。6. 日常使用的收尾小技巧最后分享几个我自己的实操习惯。第一个启动实例前养成看一眼端口的好习惯尤其折腾过多个 MySQL 的机器两条 netstat 命令能避免绝大多数连通性困扰。第二个配置文件里每一个路径都用绝对路径别偷懒写相对路径不然启动方式一变路径就失效了。第三个给两个实例各自建一个独立的备份目录导出文件按版本分开放时间久了也不会弄混。还有一个很实用的习惯把两个版本的常用命令写进一个简单的启动脚本或者 bat 文件。Windows 下我建了两条快捷命令一个名叫 mysql57内容指向 5.7 的 bin 目录下 mysql.exe 并加 -P3306另一个指向 8.0 的 bin 目录加 -P3307。Linux 下就在 /etc/profile.d/ 里加两个 alias效果一样。这种小动作看着不起眼但极大地减少了日常操作时连错库的可能。毕竟版本隔离再彻底人的记忆总有不靠谱的时候。
返回列表