Linux内网离线安装MySQL:选型、介质准备与避坑指南

📅 发布时间:2026/9/18 10:55:17
Linux内网离线安装MySQL:选型、介质准备与避坑指南
内网装 MySQL 这件事第一次碰到基本都会卡在同一个地方习惯性敲下yum install mysql-server回车屏幕上甩回来一句 No package mysql-server available。外网不通内网的 yum 源里又没有 MySQL 的包那一刻人是懵的。这不是技术难度高而是路径依赖被打断了——平时在公网机器上两步就搞定的东西到了隔离环境里变成了需要提前规划、手工搬运、逐个校验的工程活。这篇东西写给两类人一类是刚接手内网服务器、准备部署数据库的运维或后端同学另一类是要在离线环境里搭测试库、但不想被各种依赖和权限问题反复卡住的开发者。下面我会把内网装 MySQL 的选型逻辑、介质准备、两种主流安装方式的完整流程以及真正上手后才会遇到的那些坑一条一条拆开讲。核心关键词就三个Linux、内网环境、MySQL所有内容都围绕这三者展开。1. 内网装 MySQL 绕不开的第一个决策用哪种安装包1.1 三条路RPM Bundle、通用二进制、源码编译内网环境最大的约束是没有外网仓库可用所以所有安装方式都必须建立在安装介质已经在你手里这个前提上。MySQL 官方给 Linux 平台准备了三类可离线使用的介质选哪条路决定了后面半小时你会不会抓狂。第一条路是RPM Bundle也就是官方打包好的一堆.rpm文件集合包含 common、client-plugins、libs、client、icu-data-files、server 等。它的好处是和系统的包管理器天然契合安装后目录结构、服务脚本、日志路径都是官方约定好的基本装完就能用。坏处是它对系统发行版和版本很挑剔CentOS 7 的包拿到 Ubuntu 上装不了Rocky 8 和 9 的依赖也未必一致而且 RPM 依赖链有时候会牵出一堆系统库里没有的东西。第二条路是通用二进制包Generic Binary文件名叫mysql-8.0.xx-linux-glibc2.17-x86_64.tar.xz这种解压即用不经过包管理器。它的好处是跨发行版能力强同一个包在 CentOS、Ubuntu、openEuler 上都能跑只要 glibc 版本满足要求目录位置、配置文件、数据目录、启动脚本全都可以自己规划可控性最高。代价是所有东西都要手动配——建用户、改属主、写 my.cnf、写 systemd 单元文件一样都省不掉。第三条路是源码编译。除非你有特殊的定制需求比如要改默认端口范围、要裁剪某些存储引擎或者目标平台的架构比较特殊官方没有现成包否则内网部署真的不推荐走这条。编译一次动辄半小时起步还要装 cmake、gcc、boost、openssl-devel 一大堆构建依赖而这些依赖在内网里可能比 MySQL 本身还难凑齐。1.2 三种方案的取舍对照光说优缺点不够直观把关键维度拉成一张表你对着自己的场景一眼就能定。维度RPM Bundle通用二进制包源码编译跨发行版能力弱绑定发行版和版本强glibc 够就行最强但要满足构建链安装复杂度低按顺序装即可中需手工初始化配置高目录可控性低走官方约定路径高随意规划高依赖处理需处理系统库依赖需几个运行时库需完整构建工具链升级维护走包管理器清晰手工替换需谨慎手工重编内网落地难度中低到中高从内网这个场景出发通用二进制包通常是性价比最高的选择。原因很实在内网机器最怕的就是依赖装不上而二进制包把大部分复杂度都收敛到了自己目录里对系统本身的侵入最小。1.3 我现在的默认选择逻辑经过几次折腾之后我形成了一套很简单但好用的判断如果目标机器是标准的主流发行版且内网源里能补齐 libaio、numactl 这几个小依赖那就优先用 RPM Bundle快、省事、日志和 systemd 服务都是现成的如果发行版比较杂、或者担心依赖对不上那就直接上通用二进制包多花二十分钟配好但后面几乎不会再因为系统差异出问题。真正会让人翻车的往往不是选型本身而是选完之后想当然地跳过了系统底数检查。下面这一节讲的就是动手前的摸底这一步跳过后面大概率要返工。2. 动手前把系统的底数摸清2.1 三个命令确认系统基础信息内网部署最忌讳的就是拿包的时候人不在机器旁。所以进机房或者连上跳板机之后第一件事是把系统底数记下来然后再去准备介质。cat /etc/os-release # 发行版和版本号 uname -m # CPU 架构x86_64 还是 aarch64 ldd --version # glibc 版本决定二进制包选哪个uname -m这条特别关键。现在不少国产化环境和服务器是 ARM 架构的如果你照着 x86_64 的习惯下载了包拿到机器上执行会直接报 cannot execute binary file白白浪费一趟搬运。架构对不上是最低级的错误但只要不查它就一定会发生。ldd --version用于确认 glibc 版本。通用二进制包的文件名里会带glibc2.17或glibc2.28这类标识它的意思是最低要求这个版本的 glibc。如果你拿的是 glibc2.28 的包而目标机器是 CentOS 7glibc 2.17启动时会报版本符号找不到的错装包环节就得重新来过。所以下载之前先看ldd的输出再决定下哪个后缀的包。2.2 依赖库清单与检查方法RPM 方式和二进制方式的依赖诉求不太一样但有几样东西是共通的。内网环境里这一步查漏补缺能省下大量来回搬运的时间。libaioMySQL 异步 IO 依赖库缺了它 mysqld 直接起不来报错信息通常和 libaio.so.1 有关。numactl-libs / libnumaNUMA 架构机器上建议装否则 MySQL 启动会警告并影响多核性能表现。ncurses-libs / ncurses-compat-libs影响mysql命令行客户端的正常使用一些老版本依赖 libtinfo。openssl-libsTLS 连接和密码加密相关版本太旧可能触发兼容问题。libtirpc部分发行版上 RPM 安装时会要求用于 RPC 相关支持。检查方法很简单用ldconfig -p | grep 库名或者直接rpm -qa | grep 库名看有没有。内网机器上如果没有这些库就需要一并把它们的离线 rpm 包下好带进去。我的习惯是把这几个依赖的名称和对应文件列成一张清单进内网前一并打包避免到了现场发现少一个又得重新走流程。2.3 清理历史残留很多内网机器上其实装过 MySQL 或者 MariaDB只是没人记得。这种情况直接装新版本十有八九会出问题轻则端口冲突重则数据目录打架、服务起不来。先用rpm -qa | grep -i -E mysql|mariadb查一遍已装的包再用ps -ef | grep mysqld看有没有残留进程最后看/etc/my.cnf、/var/lib/mysql、/var/log/mysqld.log这些路径下有没有旧文件。确认要重装的话先把旧包卸干净再清理数据目录这一步务必确认里面没有还在用的数据删了可没有回收站。搞清楚系统的底数就可以去准备介质了。介质准备这步看着琐碎但它决定了后面每一步能不能顺利推进。3. 离线介质准备与包完整性校验3.1 从官方渠道取包并引入内网安装包的来源只有一个原则从官方渠道获取。MySQL 的官方下载页面提供了各版本的 RPM Bundle、通用二进制包和源码包选好版本、架构、操作系统类型页面会给出对应的下载项。版本上生产环境建议选 8.0 的稳定小版本或者 8.4 LTS不要追求最新的开发版内网环境升级一次成本太高稳定比新更重要。下载完成之后需要把这个文件从能上网的机器搬到内网。这个过程各单位的合规通道不一样有的走内部文件服务器中转有的通过运维堡垒机的文件传输功能有的通过审计过的移动介质。具体走哪条按你所在环境的规范来就行这里只强调一点传输完成后一定要做完整性校验不能下完就传、传完就解压。3.2 校验哈希别让半截包毁掉整个部署网络中断、传输工具截断、介质老化都可能让安装包在搬运过程中损坏。损坏的包有两种表现一种是解压时直接报错这种还算幸运另一种更坑——解压成功但某个文件内容不对安装或初始化到中途才失败排查起来非常费劲。做法很简单在下载机上看一下官方页面提供的校验值或者本地算一份sha256sum mysql-8.0.xx-linux-glibc2.17-x86_64.tar.xz md5sum mysql-community-server-8.0.xx.rpm进内网之后再算一遍两边对得上才继续。这一步花不了一分钟但能帮你挡掉最难查的一类问题。3.3 解压与目录规划校验通过后就把包解压到规划好的位置。通用二进制包解压后是一个带版本号的目录比如mysql-8.0.xx-linux-glibc2.17-x86_64通常的做法是重命名或者软链接成/usr/local/mysql方便后续脚本引用也方便以后升级时换软链。tar -xvf mysql-8.0.xx-linux-glibc2.17-x86_64.tar.xz -C /usr/local/ cd /usr/local mv mysql-8.0.xx-linux-glibc2.17-x86_64 mysql数据目录我一般单独规划放在独立的数据盘上比如/data/mysql不跟程序目录混在一起。这样做的原因是程序目录升级时可以直接替换数据目录保持不动风险和操作量都小很多。如果只有一个盘那就至少保证程序和数据分在两个不同的父目录下别全塞在/usr/local/mysql里。介质和目录都就绪了接下来就进入真正的安装环节。RPM 方式和二进制方式的操作差别很大我分开讲你可以按选定的路线对照来看。4. RPM Bundle 方式的完整落地过程4.1 包的安装顺序与各自职责RPM Bundle 解压出来是一堆 rpm 文件不能随便乱装因为它们之间有依赖关系。装错顺序会报依赖缺失虽然用yum localinstall能自动处理一部分但内网环境下 yum 未必能解析本地依赖手工按顺序装更稳。大致的安装顺序是这样的先mysql-community-common公共文件再mysql-community-client-plugins客户端插件接着mysql-community-libs共享库然后mysql-community-libs-compat老版本兼容库按需再mysql-community-client客户端工具最后mysql-community-server服务端它依赖前面所有。rpm -ivh mysql-community-common-8.0.xx.rpm rpm -ivh mysql-community-client-plugins-8.0.xx.rpm rpm -ivh mysql-community-libs-8.0.xx.rpm rpm -ivh mysql-community-client-8.0.xx.rpm rpm -ivh mysql-community-icu-data-files-8.0.xx.rpm rpm -ivh mysql-community-server-8.0.xx.rpm如果内网有可用的本地 yum 仓库用yum localinstall ./*.rpm会更省心它能自动排序并检查依赖。但前提是仓库里得有 libaio、numactl 这些依赖包否则照样卡住。4.2 依赖冲突与报错处理RPM 安装最常见的两类报错一类是依赖 xxx 未安装另一类是文件与已有的 xxx 包冲突。第一类好处理报错里会直接告诉你缺哪个so文件或者哪个包名把对应的离线包带进来装上就行。注意要看清楚是缺libaio这种运行库还是缺perl这类安装脚本依赖前者影响启动后者只影响配置阶段。第二类冲突通常是因为系统里残留了旧版本的相关包。比如之前装过mariadb-libs它和mysql-community-libs会争抢同一个libmysqlclient文件。这时候要么卸载旧的要么加--replacefiles参数强制覆盖强制覆盖要谨慎确认没有其他程序依赖旧库再用。我在一台 Rocky 上就遇到过这个报错信息写得很含蓄只说文件冲突不说是哪个包带的最后靠rpm -qf 冲突文件路径反查出来是 mariadb-libs卸掉之后就顺了。4.3 启动与开机自启RPM 安装完成后MySQL 会以 systemd 服务的形式存在服务名通常是mysqld。直接启systemctl start mysqld systemctl enable mysqld systemctl status mysqld第一次启动时服务端会自动初始化数据目录并在错误日志里生成一个临时的 root 密码。这个日志默认在/var/log/mysqld.log用下面的命令就能捞出来grep temporary password /var/log/mysqld.log拿到临时密码之后用mysql -uroot -p登录第一件事就是改密码。RPM 方式的好处到这里体现得很明显——数据目录、服务、日志全都自动就绪你几乎不用碰配置文件。但如果想调整端口、字符集、性能参数还是得去改/etc/my.cnf改完systemctl restart mysqld生效。5. 通用二进制包安装可控性更强的一条路5.1 用户、目录、权限二进制方式的第一步是建一个专用的系统用户绝不能让 mysqld 以 root 身份跑。这是安全底线也是很多新手容易忽略的地方。groupadd -r mysql useradd -r -g mysql -s /bin/false mysql这里用/sbin/nologin或/bin/false作为登录 shell意思是这个账号只用于运行服务不允许交互登录减少被利用的风险。-r表示创建系统账号不生成家目录、不在登录界面出现。接着把程序目录和数据目录的属主交给 mysql 用户mkdir -p /data/mysql chown -R mysql:mysql /data/mysql chown -R mysql:mysql /usr/local/mysql权限这块有个细节数据目录的权限不要给成 777用chown交给 mysql 用户就够了目录权限保持在 750 或 700。给太宽的权限SELinux 或者一些安全基线扫描会直接告警甚至拦掉启动。5.2 初始化数据目录二进制包的数据库是空壳必须初始化才能用。初始化有两种方式差别在 root 密码怎么来# 方式一生成随机临时密码写进错误日志 /usr/local/mysql/bin/mysqld --initialize --usermysql \ --basedir/usr/local/mysql --datadir/data/mysql # 方式二root 无密码首次登录后必须立刻设置 /usr/local/mysql/bin/mysqld --initialize-insecure --usermysql \ --basedir/usr/local/mysql --datadir/data/mysql生产环境我一般用第一种因为随机密码更安全而且会被写进错误日志好追溯。测试环境图省事可以用第二种但用完立刻改密码不能留着空密码过夜。初始化的时候有个必须提前定下来的参数lower_case_table_names。它决定了表名是否大小写敏感Linux 下默认是 0区分大小写而很多从 Windows 迁移过来的应用期望它是 1不区分。这个参数只能在初始化时设置初始化完就改不了了除非清空数据目录重新初始化。所以初始化命令里如果要用就一起带上--lower_case_table_names15.3 my.cnf 关键参数初始化完成后需要写配置文件一般放在/etc/my.cnf或者/usr/local/mysql/my.cnf。几个关键项我逐条说下为什么这么配。[mysqld] basedir /usr/local/mysql datadir /data/mysql socket /data/mysql/mysql.sock port 3306 user mysql character-set-server utf8mb4 collation-server utf8mb4_0900_ai_ci default-time-zone 08:00 max_connections 1000 log-error /data/mysql/mysql-error.log pid-file /data/mysql/mysql.pidcharacter-set-server用utf8mb4而不是utf8因为 MySQL 里的utf8其实是残缺的三字节实现存不了完整的 emoji 和部分生僻字utf8mb4才是真正完整的 UTF-8。这个坑很常见一开始不设等业务存进去乱码了再改就要动表结构非常麻烦。default-time-zone显式设成08:00是为了避免服务端和客户端时区不一致导致时间字段差几个小时。内网机器如果 NTP 没配好这个差异很容易出现。max_connections默认是 151稍微有点并发就不够用调到 1000 是常见的起步值。但要注意它和内存的关系每个连接都会占用一定内存连接数开大之前先确认机器内存扛得住。innodb_buffer_pool_size是性能大头一般设成物理内存的 50% 到 70%。内网测试库如果内存不大就按实际来别照抄公网的参数。5.4 systemd 单元文件二进制包不会自带服务脚本需要自己写一个 systemd 单元文件放到/etc/systemd/system/mysqld.service[Unit] DescriptionMySQL Server Afternetwork.target [Service] Usermysql Groupmysql ExecStart/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf LimitNOFILE65535 Restarton-failure [Install] WantedBymulti-user.target写完之后systemctl daemon-reload再systemctl start mysqld、systemctl enable mysqld。LimitNOFILE65535这一行值得留意MySQL 需要打开大量文件描述符系统默认的 1024 在高负载下会触发 Too many open files提前放开能省掉一个隐蔽的故障点。6. 初始化之后改密码、建账号、打通远程访问6.1 临时密码与 root 密码重置不管你用哪种方式安装登录后的第一件事都是处理 root 密码。如果是--initialize-insecure初始化的直接用mysql -uroot就能进如果是随机密码就从错误日志里捞出来再登。ALTER USER rootlocalhost IDENTIFIED BY 你的强密码; FLUSH PRIVILEGES;如果临时密码找不到了或者时间太久记不住也要能救回来。办法是临时跳过权限检查启动编辑配置文件加skip-grant-tables1重启 mysqld用空密码登录flush privileges后改密码再把这一行去掉重启。这个操作会短暂让数据库无认证可访问只能在维护窗口做做完立刻恢复配置。6.2 业务账号与最小权限拿 root 给应用连是非常糟糕的习惯。正确的做法是为每个业务单独建账号并且只给它需要的库和权限。CREATE USER app_user10.0.1.% IDENTIFIED BY 强密码; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO app_user10.0.1.%; FLUSH PRIVILEGES;几个要点主机段用具体的网段而不是%限制来源范围权限只给业务实际需要的读多写少的应用就别给 DELETE建完账号用SHOW GRANTS FOR app_user10.0.1.%;复核一遍看有没有给多。6.3 防火墙与 SELinux内网装完 MySQL本地连得上、远程连不上的情况太常见了十有八九是防火墙或者 SELinux 在拦。防火墙方面如果是 firewalldfirewall-cmd --permanent --add-rich-rulerule familyipv4 source address10.0.1.0/24 port port3306 protocoltcp accept firewall-cmd --reload注意我用的是 rich-rule 限制来源网段而不是直接--add-port3306/tcp全网开放。内网也不等于绝对安全能收窄就收窄。SELinux 方面先看当前状态getenforce如果是 Enforcing而数据目录又放在非标准路径比如/data/mysqlSELinux 会阻止 mysqld 访问报错信息通常是 Permission denied但看文件权限明明是通的。setenforce 0能临时验证是不是 SELinux 的锅但不要用关闭 SELinux 作为长期方案更规范的做法是用semanage fcontext给新路径打上mysqld_db_t标签再restorecon应用。内网环境如果安全基线要求 SELinux 必须开着这一步就必须做对。6.4 认证插件与老客户端兼容MySQL 8.0 默认的认证插件是caching_sha2_password它比老的mysql_native_password更安全但一些老版本的客户端、部分第三方连接工具、以及一些嵌入式的 JDBC 驱动不支持它连上来会报认证失败。判断是不是这个问题看报错里有没有Authentication plugin caching_sha2_password cannot be loaded之类的字样。如果业务侧确实升不了驱动可以给特定账号单独降级认证方式ALTER USER app_user10.0.1.% IDENTIFIED WITH mysql_native_password BY 强密码;我的建议是新项目一律用默认的caching_sha2_password只在确认客户端不支持时才降级而且要按账号降级、不要改全局默认把安全影响控制到最小。7. 内网部署常踩的坑逐个拆7.1 /tmp 权限导致 mysqld 起不来这个坑有点隐蔽。mysqld 在启动和运行过程中会用到/tmp目录如果/tmp的权限被改成了非标准值比如被某次安全加固改成了 700或者属主不是 rootmysql 用户就写不进去服务启动失败而错误日志里未必直接点名/tmp。正常的/tmp权限应该是1777也就是带 sticky bit 的全局可写。检查方法ls -ld /tmp # 期望输出类似 drwxrwxrwt. ...如果不对改回chmod 1777 /tmp就行。这个问题的排查价值在于它教会我一件事启动失败时不要只盯着 MySQL 自己的报错系统层面的权限异常同样能让它趴窝。7.2 lower_case_table_names 只能初始化时设置前面提过一次这里再强调因为它是事后无法补救的典型。MySQL 8.0 在 Linux 上默认lower_case_table_names0也就是表名区分大小写。如果你的应用是从 Windows 迁过来的代码里表名大小写混用到了 Linux 上就会报表不存在但SHOW TABLES里明明有。很多人第一反应是去配置文件里加一行lower_case_table_names1然后重启——结果 MySQL 报错拒绝启动提示这个参数和数据目录初始化时的不一致。这时候唯一的正解就是备份数据、清空数据目录、带参数重新初始化、再导入。所以这个参数一定要在初始化之前定好别等装完再纠结。7.3 时区与字符集的双重坑时区和字符集这两个问题经常同时出现而且都是装的时候不设用的时候才发现。时区方面典型表现是NOW()存进去的时间和系统时间差几个小时。原因可能是服务端时区没设、也可能是 JDBC 连接串里的时区参数和服务端对不上。我一般两头都管住服务端配default-time-zone08:00应用侧的连接串也显式带serverTimezoneAsia/Shanghai。字符集方面除了前面说的用utf8mb4还要注意连接级字符集。有时候服务端设对了但客户端连接用的是 latin1存中文照样乱码。可以在 my.cnf 的[client]段和[mysql]段也加上default-character-setutf8mb4保证服务端和客户端两边一致。7.4 默认密码过期策略有些环境的安全基线或者安装脚本会开启密码过期策略表现是账号用着用着突然登录不上报密码已过期。这个策略由default_password_lifetime控制单位是天0 表示永不过期。装完之后我习惯确认一下SHOW VARIABLES LIKE default_password_lifetime;如果是非 0 的值而你又不希望账号到期可以把它改成 0或者给具体账号设PASSWORD EXPIRE NEVER。反之如果基线要求密码必须轮换那就保留策略同时在监控里加上密码即将过期的告警别等应用挂了才发现。7.5 日志排查的通用方法内网机器出问题时最有效的排查入口永远是错误日志。二进制方式看log-error指定的路径RPM 方式看/var/log/mysqld.log。日志里搜关键字ERROR看错误Warning里InnoDB相关的也要留意很多性能问题在警告阶段就有苗头。如果是 systemd 启动失败先用systemctl status mysqld看个大概再用journalctl -u mysqld -n 100 --no-pager看完整输出。systemd 的日志和 MySQL 自己的错误日志有时会给出不同角度的信息两边对照着看定位会快很多。8. 部署后的验证闭环8.1 本机连接测试装完的服务第一步永远是本机自证。用 socket 连接mysql -uroot -p -S /data/mysql/mysql.sock用 TCP 连接这才验证监听是否正常mysql -uroot -p -h127.0.0.1 -P3306注意-h127.0.0.1和-hlocalhost走的是不同的路前者强制 TCP后者默认走 socket。远程连不上的问题很大一部分就是 socket 通、TCP 不通所以这两个测试都要做。进去之后跑几条基本命令确认状态SELECT VERSION(); SHOW VARIABLES LIKE character_set_server; SHOW VARIABLES LIKE port; SELECT lower_case_table_names;8.2 应用侧连通性验证本机通了不代表应用能连。从应用服务器上先测网络连通telnet 数据库IP 3306 # 或 nc -vz 数据库IP 3306如果端口不通回去检查防火墙、数据库绑定地址bind-address以及账号的主机段。如果端口通、但应用连报错多半是账号权限或认证插件的问题对照前面的内容排查。绑定地址这里提醒一句如果 my.cnf 里bind-address是127.0.0.1那只有本机能连远程一律拒绝。要对外提供服务得改成0.0.0.0或者具体的网卡地址同时配合防火墙限制来源。8.3 简单压测确认性能基线最后一步用个小工具确认一下性能基线。内网环境里mysqlslap是随 MySQL 客户端一起带的不用额外装很适合快速压一把mysqlslap -uroot -p --concurrency50 --iterations5 \ --auto-generate-sql --auto-generate-sql-load-typeread \ --number-of-queries5000看平均响应时间和 QPS心里有个数。如果性能远超预期地差回头检查innodb_buffer_pool_size是不是没调、内存是不是不够、磁盘是不是机械盘。这个基线不追求绝对准确它的作用是让你在业务正式接入之前知道这台库大概能扛多少。我个人在这类内网部署里最深的体会是真正花时间的从来不是安装本身而是那些默认值不符合预期的地方——默认字符集不是 utf8mb4、默认时区不对、默认认证插件老客户端不认、默认表名大小写规则和应用不一致。把这几个默认值在初始化阶段就按实际业务定死后面基本就一马平川。反过来凡是抱着先用默认值跑起来再说的心态几乎都会在某个业务上线的晚上被这些默认值反咬一口。所以我的习惯是内网装 MySQL初始化命令一定要写全、配置文件一定要过一遍、装完的验证命令一条都不能省。介质多下几个版本、依赖多备几份、参数提前和应用团队对一遍这些看起来多余的准备工作最后都会变成省下来的返工时间。