写这篇东西之前,先交代下背景。最近接手了一个隔离网段的项目,服务器全部在封闭的内网环境里,别说外网了,连本地 yum 源都没有。业务系统又指定要 MySQL 8.0,这可把不少人卡住了——毕竟平时大家装 MySQL 都是拿着 yum 一行命令搞定,一旦断网,光是那一堆依赖包的循环依赖就够喝一壶。
我把整个过程从头到尾趟了一遍,从在联网机器上收集 rpm 包,到内网服务器上离线安装、初始化、调优,最后再用 Navicat 远程连上,中间踩了不少坑,也整理出了一套几乎可以照着抄的操作流程。这篇就完完整整分享出来,给所有被“无网安装 MySQL 8.0”折磨过的朋友一个参考。
1. 为什么说无网装 MySQL 8.0 是个坎
MySQL 8.0 和 5.7 看着都是 rpm 包,装起来其实完全两个难度级别。8.0 的安装包拆得更碎,各个组件之间互相有依赖关系,而且对操作系统的 glibc、libaio、numactl 这些基础库版本有硬性要求。你光下载一个 mysql-server 的 rpm 是远远不够的,它会在安装时就给你报一堆 error: Failed dependencies,然后整个安装流程直接中断。
在线环境里遇到依赖缺失,yum 会自动从仓库里把依赖拉下来装上,你根本感知不到。但离线环境下,yum 等于废了,所有依赖都得你自己提前准备好,少一个就装不上。更麻烦的是依赖是有层级关系的,A 依赖 B,B 依赖 C,C 又依赖 D,而且还存在循环依赖,比如两个包互相依赖,你必须把两个 rpm 文件放到同一个目录下,用一条命令同时安装才能解掉这个结。
我这次用的服务器是 CentOS 7.9,内核 3.10,glibc 2.17,MySQL 版本是 8.0.36。这套组合是目前无网部署最常见的搭配,兼容性经过了大量验证。操作系统跟 MySQL 版本选对了,后面的坑能少一半。
2. 装之前必须想清楚的四个方案问题
2.1 为什么选 rpm 而不是源码编译
很多人一提到离线安装就想到编译源码,说实话这是最不推荐的路子。MySQL 8.0 源码编译依赖的库非常多,boost、cmake、gcc 版本都有要求,而且编译一次少说四十分钟,多则两三个小时,中途任何一个依赖不满足都会前功尽弃。在无网环境下,你根本没法临时去补装编译工具链。rpm 包则是已经编译好的二进制,只要依赖库版本匹配,装完就能用,部署效率高一个数量级。
2.2 要不要考虑 Docker 方案
说实话,如果服务器上有 Docker 环境,那直接跑 mysql:8.0 镜像是最省事的,一个 docker load 就搞定了。但很多内网服务器是纯净系统,连 Docker 都没有,你还得先离线装 Docker,这就又绕回到依赖地狱里了。所以本文不讨论 Docker 路径,专注原生 rpm 安装,这也是兼容性最好、最通用、后续维护最直接的方式。
2.3 依赖包收集的两种策略
收集依赖包有两种策略,一种是在能联网的机器上用 yumdownloader 把 MySQL 官方仓库里的 rpm 连同依赖一起下载到本地;另一种是直接用 yum 的 downloadonly 插件。两种方式本质一样,但前者更灵活,可以指定下载目录。实际操作中我推荐在 CentOS 7 的联网机器上先把 MySQL 官方 yum 仓库装好,然后一条命令把需要的包全部拉下来,这样能保证版本号完全对应,不会出现内网里装到一半发现版本不匹配的问题。
2.4 版本锁定与后续升级的考量
无网环境下还有一个隐形问题——版本锁定。在线环境可以随时 yum update 升级 MySQL,无网环境一旦部署完,后续升级就得重新走一遍离线流程。所以前期最好是直接装当时最新的稳定版,减少升级频率。我用的 8.0.36 在写这篇时是官方长期支持版,功能稳定,bug 修复也全面,踩坑概率相对较低。
3. 在联网机器上准备离线安装包
3.1 准备工作:一台能联网的 CentOS 7 机器
在开始之前,需要确认自己的操作环境。我自己是在一台可以联网的 CentOS 7 最小化安装机器上操作的,这台机器最好和你的内网目标机器是同一个大版本,避免 glibc 版本差异。如果你手里只有 Windows 机器,也可以装个 CentOS 7 虚拟机来做包收集,反正最后只需要拿到 rpm 文件集合就行。
首先确认系统版本和架构:
cat /etc/redhat-release uname -m输出结果应该是:
CentOS Linux release 7.9.2009 (Core) x86_64x86_64 架构下,下面的包都能直接用。如果是 arm64 架构(比如华为鲲鹏服务器),下载包的时候要选 aarch64 版本,后面操作逻辑一样。
3.2 下载 MySQL 官方 yum 仓库并启用
联网机器上先把 MySQL 官方 yum 仓库装上:
rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-7.noarch.rpm装完后确认仓库里 MySQL 8.0 的源是启用的:
yum repolist enabled | grep mysql正常会看到 mysql-connectors-community、mysql-tools-community、mysql80-community 这三行。如果 mysql80-community 没启用,手动打开:
yum-config-manager --enable mysql80-community3.3 核心一步:用 yumdownloader 批量拉取 rpm
这里用到的核心工具是 yumdownloader,它在 yum-utils 包里。先安装:
yum install -y yum-utils然后建一个目录,把所有需要的 rpm 下载进去:
mkdir -p /root/mysql8-offline-rpm cd /root/mysql8-offline-rpm yumdownloader --resolve mysql-server加上 --resolve 参数后,yumdownloader 会把 mysql-server 以及所有依赖包全部下载到当前目录。这一步下载的包数量通常有十几个,包括 mysql-server、mysql-client、mysql-common、libaio、numactl-libs、openssl-libs、ncurses-compat-libs、perl 相关包等等。
下载完成后数一下文件数量:
ls -lh /root/mysql8-offline-rpm | wc -l一般会有 15-25 个 rpm 文件,具体数量取决于你安装的组件。这时候不要急着走,再手动补几个打包时容易漏掉的:
yumdownloader --resolve mysql-community-client yumdownloader --resolve mysql-community-libs yumdownloader --resolve mysql-community-common这样能把客户端、库文件、公共文件全套抓齐,避免内网机器上装完 server 后发现缺客户端工具。
3.4 生成本地仓库索引(关键细节)
手动收集的 rpm 文件,直接 yum localinstall 也是能装的,但依赖关系处理不够聪明。更好的做法是顺便生成一个本地 yum 仓库索引,让内网机器上也能用 yum 来智能解决依赖顺序。
联网机器上安装 createrepo 工具并生成索引:
yum install -y createrepo createrepo /root/mysql8-offline-rpm生成完成后目录里会多出一个 repodata 子目录,这个目录就是 yum 的索引数据库。把整个 /root/mysql8-offline-rpm 目录打包传到内网机器上:
tar czf mysql8-offline-rpm.tar.gz mysql8-offline-rpm/3.5 如果找不到联网的 CentOS 机器的替代方案
如果手上实在没有能联网的 CentOS 7 机器,还可以用一个更取巧的办法:在一台能联网的任意 Linux 机器上,去 MySQL 官方下载页面把对应 el7 的 rpm 包全部手工点下载。页面地址是 MySQL Community Server 下载页,选 Red Hat Enterprise Linux 7 / Oracle Linux 7,挑 x86_64 架构,把下面这些包逐一下载:
- mysql-community-server
- mysql-community-client
- mysql-community-libs
- mysql-community-common
- mysql-community-client-plugins
- mysql-community-icu-data-files
至于 libaio、numactl-libs、ncurses-compat-libs 这些系统依赖,直接去 CentOS 7 的官方 mirror 仓库找,或者从任意的 CentOS 7 yum 源 respository 里下载对应 rpm。这个方法比较笨,但可行。
4. 无网环境下的完整安装流程
4.1 把安装包传到内网服务器并解压
拿到打包好的 mysql8-offline-rpm.tar.gz 之后,传到目标服务器的 /opt 目录下,然后解压:
cd /opt tar zxf mysql8-offline-rpm.tar.gz cd mysql8-offline-rpm/解压后目录下应该有全部 rpm 文件和 repodata 目录。先检查当前系统是否装了旧版 MySQL 或 MariaDB,有的话先卸载干净,否则端口和 socket 文件会冲突:
rpm -qa | grep -E 'mysql|mariadb'查出有 mariadb-libs 这类包,直接卸载:
rpm -e --nodeps mariadb-libs这个操作在最小化安装的 CentOS 7 上几乎必做,系统默认自带 mariadb-libs,不去掉的话装 MySQL 时会有文件冲突。
4.2 配置离线 yum 仓库(推荐,省心)
在 /etc/yum.repos.d/ 下新建一个本地仓库配置,指向刚才解压的目录:
vi /etc/yum.repos.d/mysql-offline.repo写入以下内容:
[mysql-offline] name=MySQL 8.0 Offline RPM Repository baseurl=file:///opt/mysql8-offline-rpm enabled=1 gpgcheck=0保存后清理 yum 缓存并重建:
yum clean all yum makecache此时 yum repolist 里应该出现 mysql-offline 仓库。这样做的最大好处是,依赖顺序完全由 yum 自动解析,比如它知道要先装 common 再装 libs 再装 server,不用你自己去手动算依赖链。
4.3 一条命令离线安装 MySQL 8.0
配置好本地仓库之后,安装变得非常简单:
yum install -y mysql-community-serveryum 会自动从本地仓库中解析所有依赖并逐个安装。如果前面收集的 rpm 包数量够全,这一步通常不会报错。如果个别依赖缺失,就把报错提示的包名记录下来,回到联网机器上用 yumdownloader 补拉一次,再传过来重新安装即可。
我实测在最小化 CentOS 7.9 上,mysql80-community-release 仓库依赖的全部包加系统库,总共是 27 个 rpm 文件,安装完成后 /usr/bin 下会有 mysql 和 mysqladmin 等客户端工具,/usr/sbin 下有 mysqld 服务端。
4.4 不建本地仓库的直接安装方式
如果你不想创建本地 yum 仓库,也可以直接用 rpm 命令安装。但有一个关键坑:rpm -ivh 一次只装一个包会卡在依赖上,正确做法是把所有 rpm 放在同一目录下,一条命令同时安装:
cd /opt/mysql8-offline-rpm rpm -ivh *.rpm --nodeps用 --nodeps 可以跳过依赖检查强行安装,但如果系统里真的缺了某些基础库(比如 libaio 根本不存在),mysqld 启动时会直接失败,报错信息还挺难排查。所以这个方式我放在最后推荐,除非你确认系统基础库齐全,否则还是建议走 yum 本地仓库,让安装过程变成“傻瓜式”操作。
5. 初始化配置与调优:装完才是开始
5.1 首次启动与临时密码获取
安装完成后先初始化数据目录,然后启动服务:
mysqld --initialize --user=mysql这一步会生成初始数据目录 /var/lib/mysql,并创建一个随机临时密码。初始化过程如果报错,先看日志:
cat /var/log/mysqld.log常见错误多是 /var/lib/mysql 目录权限不对,或者缺 mysql 用户。权限不对就先 chown 一下:
chown -R mysql:mysql /var/lib/mysql用户不存在则创建系统用户:
useradd -r -s /sbin/nologin mysql初始化成功后启动 mysqld 并设置开机自启:
systemctl start mysqld systemctl enable mysqld临时密码在日志里,用 grep 关键词找出来:
grep 'temporary password' /var/log/mysqld.log输出类似这样:
[Note] A temporary password is generated for root@localhost: xxxxxxxx这个密码只生效一次,首次登录后 MySQL 会强制你修改密码。
5.2 首次登录与密码策略调整
用临时密码登录:
mysql -uroot -p登录成功后第一件事就是改密码:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPass123!';MySQL 8.0 默认密码策略要求包含大小写字母、数字和特殊字符,且长度至少 8 位。如果你所在的网络环境对内网数据库密码强度要求不那么严格,或者业务系统要求弱密码,可以先降低校验等级再设置密码:
SET GLOBAL validate_password.policy = LOW; SET GLOBAL validate_password.length = 6; ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';需要注意,MySQL 5.7 和 8.0 的密码策略参数名完全不同。5.7 是 validate_password_policy,8.0 改成了 validate_password.policy,带了点号。如果记混淆了,会直接报系统变量不存在的错误。
为了永久生效,还需要把参数写进配置文件 /etc/my.cnf:
[mysqld] validate_password.policy=LOW validate_password.length=6否则重启后又会恢复强密码策略。
5.3 字符集、时区与端口配置
MySQL 8.0 默认字符集已经是 utf8mb4,这是好事,但有一处地方容易漏:默认 collation 可能是 utf8mb4_0900_ai_ci,如果业务系统的排序规则和你的开发环境不一致,可能出现诡异的排序结果。建议显式指定:
[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_general_ci default-time-zone='+08:00'时区默认是系统时区,如果你服务器在 CST 时区但系统设置成了 UTC,时间字段会差 8 个小时,排查起来非常头疼。在配置里写死 default-time-zone='+08:00' 一劳永逸。
最后检查端口配置,默认 3306,没特殊需求不用改。确认监听配置:
netstat -tlnp | grep 3306看到 0.0.0.0:3306 表示服务已经正常监听所有网卡。
5.4 创建业务账号并开启远程访问
无网部署的数据库通常要允许内网其他机器连接,默认 root 只能本地登录,需要创建业务账号并授权:
CREATE USER 'appuser'@'%' IDENTIFIED BY 'AppPass123!'; GRANT ALL PRIVILEGES ON *.* TO 'appuser'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;注意 8.0 里 GRANT 之后再 FLUSH PRIVILEGES 其实不是必须的,因为授权操作会实时生效,但习惯上写一下也没毛病。真正必须确认的是防火墙要放行 3306 端口。
CentOS 7 默认用 firewalld,放行命令:
firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --reload如果用的 iptables,则:
iptables -A INPUT -p tcp --dport 3306 -j ACCEPT service iptables save还有一个隐蔽的坑:SELinux。如果 SELinux 开着且 enforcing,即使防火墙放行了,远程连接仍可能被拒。测试的时候可以直接临时关闭 SELinux:
setenforce 0如果能连上了,说明就是 SELinux 的问题。生产环境建议用正确方式处理,而不是简单关闭:
yum install -y policycoreutils-python semanage port -a -t mysqld_port_t -p tcp 33065.5 my.cnf 基础调优参数
无网环境下跑业务,数据库性能调优主要靠内存池和连接数这两个大头。给出一个适合 8C16G 机器的起步配置,实测在中等并发场景下比较稳:
[mysqld] innodb_buffer_pool_size=8G innodb_log_file_size=1G innodb_flush_log_at_trx_commit=1 max_connections=500 table_open_cache=2048 sort_buffer_size=4M join_buffer_size=4Minnodb_buffer_pool_size 是最核心的参数,官方建议设为机器物理内存的 50% 到 70%,这里 16G 内存给了 8G,留了一部分给操作系统和业务进程。如果你机器内存小,比如 4G,那就把这个值降到 2G 左右,宁可小一点也别让 MySQL 把内存吃光导致 OOM。
6. 常见问题与排查技巧实录
6.1 问题速查表
| 症状 | 可能原因 | 解决方式 |
|---|---|---|
| 安装时报 Failed dependencies | 依赖 rpm 包缺失 | 回联网机器补下载依赖,重新生成仓库索引 |
| mysqld 启动失败,日志报 libaio.so.1 找不到 | 系统缺 libaio 库 | rpm -ivh libaio-*.rpm,或 yum install libaio |
| 初始化时报 Can't create directory | /var/lib/mysql 权限错误 | chown -R mysql:mysql /var/lib/mysql |
| 远程连接失败,客户端报 10060 | 防火墙未放行 3306 | firewall-cmd 放行端口,或关闭 SELinux测试 |
| 远程连接失败,报 1045 Access denied | 账号未正确授权 | 检查 user 表,确认 '%' 主机授权是否存在 |
| 密码修改报 ERROR 1819 | 密码不符合策略 | 先 SET GLOBAL validate_password.policy=LOW |
| 用 Navicat 连上后看不到数据表 | 用户对库没权限 | GRANT ALL ONdbname.* TO 用户 |
6.2 一个隐蔽的依赖循环案例
我在收集依赖时遇到过一个 flaky 的坑:mysql-community-server 和 mysql-community-libs 相互依赖,如果单独用 rpm -ivh 安装会提示:
error: Failed dependencies: mysql-community-libs(x86-64) >= 8.0.36 is needed by mysql-community-server-8.0.36-1.el7.x86_64这是非常经典的循环依赖。你在同一个目录下用一条 rpm 命令同时装这两个包就能解掉:
rpm -ivh mysql-community-libs-8.0.36-1.el7.x86_64.rpm mysql-community-server-8.0.36-1.el7.x86_64.rpm但如果按照我前面推荐的方式,配置了本地 yum 仓库一路 yum install 下来,这个循环依赖 yum 会自动处理,根本碰不到。这也是我强烈建议走 yum 本地仓库路径的原因。
6.3 字符集导致的数据写入报错
有次在另一个环境帮人排查问题,应用侧写入 emoji 表情时报错:
java.sql.SQLException: Incorrect string value: '\xF0\x9F\x98\x80' for column 'name' at row 1原因就是数据库字符集不是 utf8mb4,emoji 属于四字节字符,utf8 编码存不下。MySQL 8.0 默认虽然建库是 utf8mb4,但如果你建表时指定了 utf8,还是会踩坑。解决办法是在 my.cnf 里彻底锁死:
[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_general_ci然后重启服务,新建的库表就都是 utf8mb4 了。已经存在的旧表需要单独转换:
ALTER TABLE tablename CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;这个操作会重建表,数据量大时耗时较长,建议业务低峰期执行。
6.4 备份恢复路径验证
无网部署完不能就这么完事,备份恢复路径一定要先验证一遍。MySQL 8.0 自带的备份工具是 mysqldump,基础用法:
mysqldump -uroot -p --single-transaction --master-data=2 --all-databases > /backup/all_backup_$(date +%F).sql--single-transaction 参数在 InnoDB 引擎下能保证备份数据一致性,不用锁表,对线上业务影响极小。恢复时在目标机器上:
mysql -uroot -p < all_backup_20250601.sql这里提醒一句,MySQL 8.0 的 mysqldump 备份数据不能直接恢复到 5.7 版本,跨大版本恢复需要用官方提供的迁移工具,或者在 5.7 环境里用兼容模式导出。无网环境如果后续要做版本升级,这一点尤其要注意。建议在部署完成后立刻做一次全备并恢复到测试库验证,别等到业务数据进去之后再现测,那时发现问题就麻烦了。
6.5 无网环境下的日志清理与维护
MySQL 运行一段时间后,binlog 会占掉不少磁盘空间。无网环境没有额外的监控报警,尤其要注意定期清理。检查当前 binlog 大小:
du -sh /var/lib/mysql/binlog.*手动清理到指定保留天数之前的 binlog:
PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY;更省心的做法是配置自动清理:
[mysqld] expire_logs_days=7MySQL 8.0 里该参数已经改名 binlog_expire_logs_seconds,建议用新版写法:
[mysqld] binlog_expire_logs_seconds=604800604800 秒正好是 7 天。配置后重启 mysqld,老日志会被自动清理,新日志到了时间也会自动滚动删除。别小看这个配置,内网服务器磁盘通常不大,binlog 堆积把磁盘写满导致 MySQL 直接挂掉的案例我见过不止一次。
7. 我的实操心得与几点补充建议
整套流程走完,最大的体会是:离线安装本身不难,难的是安装前的准备工作做得到不到位。依赖包收集这一步决定了后面 90% 的成败,宁可多收集几个用不到的包,也不要装到一半发现缺依赖再回头去补。把本地 yum 仓库建好,安装过程从“闯关”变成“一键”,体验完全不一样。
另外两个小建议送给经常做无网部署的朋友。第一个,维护一个自己的 rpm 包仓库,把常用的 MySQL、Nginx、Redis、JDK 这些组件的离线安装包按版本归档存放,下次部署直接拷走就用,能省大量时间。第二个,写部署文档的时候把 MySQL 的临时密码获取方式、远程授权命令、防火墙配置这些容易忘的步骤单独用红字标注,项目交接的时候能少接无数个咨询电话。
这套方案我已经在两批服务器上验证过,一次 CentOS 7.9 配 MySQL 8.0.36,一次 CentOS 7.6 配 MySQL 8.0.34,全程无外网,按同样的步骤走下来都是半小时内完成部署。遇到的环境虽然不同,但核心思路一致:把依赖提前备齐,让 yum 帮你解决问题,把时间留给真正需要调优的地方。你可以直接拿这份操作记录当参考,结合自己服务器的具体配置稍微调整参数,应该能少走不少弯路。