最近在 Rocky Linux 8 上部署 MySQL 5.7我特意选择了二进制文件安装而不是直接 yum 一把梭到底。二进制安装的好处在于你不需要受发行版仓库规则的限制拿到官方打好的软件包就能部署整个过程完全可控尤其针对离线机房环境、需要锁定某个特定小版本、或者企业对交付物品有一致性要求的场景这种方案几乎是唯一舒服的选择。这篇内容会把我的完整操作记录、参数调整、系统服务托管的细节全部整理出来也包含几个我实际踩过的坑适合所有用 Rocky 8 / AlmaLinux 8 跑 MySQL 5.7 的运维和开发同学参考。1. 为什么我坚持用二进制包安装1.1 从 Windows 到 Rocky Linux安装思维要换个频道很多人接触 MySQL 5.7 是从 Windows 10 上的安装包开始的双击 exe、点几个下一步再填个密码就完事了。Windows 下 MySQL 安装程序本质上就是个集成的二进制分发自带依赖、注册系统服务、自动生成配置几乎没有“安装过程”需要操心。但到了 Linux 上这套思维就要变一变。Linux 的软件分发更像“零件工厂”yum 和 dnf 会根据仓库里的 metadata 帮你拉依赖、装服务看起来方便然而如果你要装的 MySQL 5.7 不在仓库源里或者仓库源里已经升级到了 MySQL 8.0那 yum 这条路就会非常尴尬。Rocky Linux 8 的默认 AppStream 仓库里确实没有 MySQL 5.7只能通过 MySQL 官方 Yum 源去装但官方 Yum 源也可能因为网络策略、内网隔离而无法访问。这时候直接下载一个约几百 MB 的二进制 tar 包手工解压、手工初始化、手工配置反而成了最稳定、最可控的方案。所谓二进制文件安装其实就相当于把 Windows 里那个“安装程序”做过的动作手动拆开执行解压文件、准备目录、创建用户、初始化数据目录、注册服务。只要你理解了这些动作Linux 上无论换哪个发行版都可以照猫画虎这也是运维基本功之一。1.2 二进制安装对比 yum 源和源码编译三个理由我在 Rocky Linux 8 上装 MySQL 5.7 之前其实也快速评估过三条路官方 Yum 源安装、源码编译安装、二进制包安装。最终我选了二进制包理由有三点很实在。第一一致性最好。官方发布的二进制包是统一编译出来的glibc 版本已经在构建时匹配好相同版本在不同机器上解压后行为完全一致。yum 源里的 RPM 虽然也是官方编译但 RPM 会做大量的前置检查、启动脚本定制中间变量多源码编译就更不用说了编译参数不同性能就有细微差异排查问题时很难复现。第二权限和目录完全可控。二进制包默认全在你自己规划的目录下数据目录、日志目录、套接字文件位置都是自己说了算。yum 安装经常会按 RPM 的 File 路径强制安装到 /usr/libexec/mysql 和 /var/lib/mysql这些路径虽然标准但遇到多实例部署、磁盘挂载点不同、统一目录规范要求时调整起来比较绕。第三卸载和升级干净利落。二进制包安装的 MySQL升级只需要替换 /usr/local/mysql 下的文件数据目录保留不动卸载只需要停服务、删目录、删 systemd 文件全系统不残留一堆 RPM 元数据和配置碎片。对于生产环境经常做版本迁移的人来说这一点非常舒服。所以你问我为什么选二进制安装简单讲就是我不想把 MySQL 的生命周期绑在发行版的包管理器上。MySQL 5.7 已经进入了生命周期后期未来我们必然会迁移到 8.0二进制部署方式让迁移路径更清晰。2. 安装前准备依赖、账号与目录规划2.1 把依赖一次装齐很多人以为 MySQL 二进制包是“绿色版”解压就能跑实际上它还是依赖少量系统库。如果系统里缺了这些库启动时会直接报错而且报错信息很隐晦通常是error while loading shared libraries: libaio.so.1: cannot open shared object file或者libncurses.so.5。在 Rocky Linux 8 里我建议启动前先把依赖装齐dnf install -y libaio ncurses-compat-libs perl perl-JSON net-tools lsof逐个说明一下用途libaio提供异步 IO 库MySQL 的 InnoDB 存储引擎在 Linux 上默认会使用 libaio缺失的话连初始化都会失败。ncurses-compat-libs提供libncurses.so.5和libtinfo.so.5。MySQL 5.7 的二进制包编译时链接的还是 ncurses 5 的库而 Rocky Linux 8 默认只有 ncurses 6所以必须装兼容库。perl和perl-JSONMySQL 官方自带的mysqld_safe脚本和部分辅助工具会用到 perl比如mysql_install_db在旧版本里还会依赖更多 Perl 模块虽然 5.7 里使用mysqld --initialize代替了这个传统方式但 systemd 脚本、自动化运维工具里依然可能调用。net-tools和lsof用于后续排查端口监听和连接信息属于运维标配。如果你遇到其他的共享库缺失可以先执行ldd /usr/local/mysql/bin/mysqld查看动态链接情况再根据缺失的库名用dnf provides反向查找属于哪个包。这一招比到搜索引擎里乱翻快得多。2.2 目录规划是后面少折腾的关键我见过很多人把 MySQL 数据目录直接放在程序目录下比如/usr/local/mysql/data表面看省事但升级时一旦覆盖程序目录数据就有被误操作的风险。更规范的做法是程序目录、数据目录、日志目录分开放。我的规划如下目录路径用途/usr/local/mysql程序主目录实际上是个软链接/data/mysql/dataInnoDB、系统库的数据文件目录/data/mysql/logs错误日志、慢查询日志、binlog 目录/etc/my.cnf主配置文件/data/mysql/tmpMySQL 内部临时文件可选但建议预留数据目录单独放在/data下主要是为了以后磁盘扩容方便。万一数据盘满了我只需要挂一块新盘把目录迁移过去改一下配置文件接口程序目录完全不用动。创建目录的时候记得把所有者设置成后面要建的用户否则后面初始化会报权限错误mkdir -p /data/mysql/data /data/mysql/logs /data/mysql/tmp chown -R mysql:mysql /data/mysql2.3 下载二进制包与完整性校验MySQL 5.7 的官方二进制包可以从 MySQL 官网下载页选择 “Linux - Generic” 获取。注意这里要选glibc 2.12的版本文件名通常长这样mysql-5.7.44-linux-glibc2.12-x86_64.tar.gzRocky Linux 8 的 glibc 版本比 2.12 新向下兼容所以这个包完全可用。下载命令wget https://cdn.mysql.com/archives/mysql-5.7/mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz文件下载完成后千万别急着解压先做完整性校验。尤其在生产环境或离线转储的场景下网络传输或者拷贝过程可能出现文件损坏一旦解压出残缺的二进制后面神仙都难救sha256sum mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz然后把输出的哈希值和官方页面提供的 SHA-256 值做对比如果不同重新下载。这一步不费时间但能帮你过滤掉大量莫名其妙的“崩溃”问题。3. 实操解压、配置、初始化到 systemd 托管3.1 解压与软链接解压本身没什么技术含量但路径规划有个小技巧不要直接解压成固定目录名推荐解压出带版本号的目录然后用一个不带版本号的软链接指过去。这样后续小版本升级只需要把软链接切换到新目录回滚也容易。tar -zxvf mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz -C /usr/local cd /usr/local ln -s mysql-5.7.44-linux-glibc2.12-x86_64 mysql ls -ld /usr/local/mysql执行完会看到/usr/local/mysql指向实际解压目录。这么做的好处以后你升级 MySQL 5.7 到同大版本的最新 patch 版本时体会最深解压新包、停服务、切软链接、启服务几秒钟的事数据目录完全不动。接下来处理目录权限chown -R mysql:mysql /usr/local/mysql同时创建 mysql 系统用户和组。MySQL 官方建议用专用系统账号运行不要用 root 跑数据库安全等级也符合基本的生产要求。groupadd mysql useradd -r -g mysql -s /sbin/nologin -M mysql-M表示不创建家目录-s /sbin/nologin表示禁止该用户登录。这样即使 MySQL 被日穿攻击者也没法直接用 mysql 账号登录系统。3.2 编辑 my.cnf这些参数先记下配置文件是最容易出问题的地方。我建议不要直接照抄网上的“生产万能模板”因为每台机器的内存、CPU、磁盘能力不同参数必须有差异。但是底层的路径参数必须明确否则服务根本起不来。粘贴一份我在 Rocky Linux 8 上验证过的初始配置[mysqld] user mysql basedir /usr/local/mysql datadir /data/mysql/data socket /data/mysql/tmp/mysql.sock pid-file /data/mysql/logs/mysqld.pid log-error /data/mysql/logs/mysqld.log port 3306 server-id 1 character-set-server utf8mb4 collation-server utf8mb4_general_ci init_connect SET NAMES utf8mb4 skip-external-locking symbolic-links 0 max_connections 1000 max_connect_errors 100000 wait_timeout 28800 interactive_timeout 28800 innodb_buffer_pool_size 1G innodb_log_file_size 256M innodb_flush_log_at_trx_commit 2 innodb_flush_method O_DIRECT sync_binlog 1 sql_mode ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION有几个地方需要特殊说明socket和pid-file我特意放到了/data/mysql/tmp和/data/mysql/logs而不是系统的/tmp。因为/tmp有时会被 tmpwatch 之类定时任务清理一旦套接字文件丢失客户端工具连接就会报Cant connect to local MySQL server through socket很冤。放在自己的目录下可控得多。innodb_buffer_pool_size配置为 1G 是保守值仅适合内存 8G 左右的机器。如果内存够大比如 32G 以上可以把这个值调到物理内存的 50% 到 60%。注意 5.7 里这个参数是运行时不可动态调整的8.0 之后才支持动态调整修改后必须重启实例所以一开始就要规划好。innodb_flush_method O_DIRECT是为了绕过操作系统 Page Cache减少双缓存开销减小内存浪费。这个参数在 MySQL 5.7 里很有意义配合 Direct IO 能降低磁盘 IO 延迟。如果你用的是虚拟化磁盘或者共享存储效果因硬件而异可以先观察性能再决定是否保留。sql_mode里的ONLY_FULL_GROUP_BY会让老业务遇到很多坑。如果你要迁移老项目建议把这项去掉否则一个GROUP BY不严谨的 SQL 就会直接报错业务方会骂人。我的做法是保持严格模式但给开发团队发一份兼容清单避免上线时炸一片。3.3 初始化数据目录并处理权限MySQL 5.7 以后官方已经弃用了mysql_install_db脚本统一走mysqld --initialize或者mysqld --initialize-insecure。两者区别如下--initialize初始化完成后生成随机 root 密码密码打印到日志文件/data/mysql/logs/mysqld.log里。--initialize-insecure初始化完成后 root 账号无密码需要立刻登录修改。作为手工部署我建议使用--initialize-insecure然后在同一个终端会话里立刻完成 root 密码设置这样最不容易把随机密码弄丢。如果你用--initialize务必马上打开日志文件记录密码我第一次试用时就因为没存密码最后只能重置一遍。执行初始化前确认/etc/my.cnf里的路径全部存在且权限正确/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf --initialize-insecure --usermysql如果终端没有任何输出初始化就是成功了。此时检查数据目录ls -l /data/mysql/data能看到mysql、performance_schema、sys这些子目录以及一堆.ibd文件就说明数据目录已经生成。如果中途报错别慌直接打开日志cat /data/mysql/logs/mysqld.log初始化阶段最常见的错误有两类一是目录权限不对mysqld 无法创建文件二是依赖库缺失报错停在error while loading shared libraries。对症下药即可。初始化完成后如果还用 root 直接启动 mysqld虽然有时能启动但会在日志里留下大量Please remember to remove the system user之类的警告而且某些文件最终会生成 root 属主后患无穷。所以我前面才坚持user mysql放在配置文件里配合系统用户一起用。3.4 配置 systemd 服务并启动生产环境千万别直接用/usr/local/mysql/bin/mysqld裸跑配一个 systemd 服务开机自启、崩溃自动拉起、日志统一采集这些能力立刻就有了。创建/etc/systemd/system/mysqld.service[Unit] DescriptionMySQL Server 5.7 Documentationman:mysqld(8) Afternetwork.target Aftersyslog.target [Service] Typeforking Usermysql Groupmysql ExecStart/usr/local/mysql/bin/mysqld_safe --defaults-file/etc/my.cnf ExecStop/usr/local/mysql/bin/mysqladmin --defaults-file/etc/my.cnf --socket/data/mysql/tmp/mysql.sock shutdown TimeoutSec300 LimitNOFILE65535 Restarton-failure [Install] WantedBymulti-user.target这里我特意用mysqld_safe而不是直接mysqld。mysqld_safe是官方提供的一层守护封装它会在 mysqld 异常退出后自动重启同时会检查错误日志的运行状态比直接裸启 mysqld 稳很多。配合 systemd 的Restarton-failure相当于上了双保险数据库服务只要不是整机断电都能在几秒内自动恢复。需要注意Typeforking这个细节。mysqld_safe启动时会在后台 fork 出一个 mysqld 进程然后自身退出systemd 必须知道它 fork 出来了才能正确判定服务状态所以不能使用默认的Typesimple否则 systemd 会认为服务启动失败或状态不稳定。写完后依次执行systemctl daemon-reload systemctl enable mysqld systemctl start mysqld systemctl status mysqld看到active (running)之后先不要急着连客户端再确认一下端口监听netstat -tlnp | grep 3306这一步是验证服务真正对外可用的关键。如果端口没起来多半是初始化参数有问题或服务启动失败继续翻日志就好。4. 安装后的安全配置与参数调优4.1 设置 root 密码与应用账号初始化用--initialize-insecure后root 用户没有密码此时如有内网其他机器能连上 3306 端口等于数据库敞开着大门。所以最优先的事就是马上登录并设置密码。/usr/local/mysql/bin/mysql -uroot --socket/data/mysql/tmp/mysql.sock进入 MySQL 命令行后执行ALTER USER rootlocalhost IDENTIFIED BY YourStrongPass123!; FLUSH PRIVILEGES;注意 MySQL 5.7 已经不支持SET PASSWORD FOR rootlocalhost PASSWORD(...)这种语法那个PASSWORD()函数在 5.7 里已经废弃8.0 直接移除。使用ALTER USER是最标准的做法。生产环境不建议让 root 对外开放远程访问。应用账号单独建CREATE USER appuser% IDENTIFIED BY AppPass2023!; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP, REFERENCES ON appdb.* TO appuser%; FLUSH PRIVILEGES;appuser%这类账号要根据实际网络安全策略授权如果应用服务器 IP 固定尽量写成具体地址比如appuser192.168.10.20减少暴露面。这也是我踩过坑之后学到的授权太宽松排查问题时根本不知道是哪台机器打进来的请求。4.2 核心参数按需调整刚装完的 MySQL 5.7 很多参数是保守的真正上业务流量前必须按内存和磁盘重新校准。我以一台 16G 内存、4 核 CPU 的虚拟机为例给出二次调优方向参数初始值建议值调整说明innodb_buffer_pool_size1G8GInnoDB 的缓冲池通常设为物理内存的 50% 左右不要超过 60%留给系统其他进程空间innodb_log_file_size256M512M增加 Redo Log 大小降低频繁刷盘带来的 IO 压力但重启期间会让恢复耗时变长max_connections10003000连接数上限根据实际业务并发调整过大反而消耗内存因为每个连接都有线程栈table_open_cache20004096表文件描述符缓存避免频繁打开关闭表文件slow_query_logOFFON建议开启慢查询日志定位性能瓶颈离不开它修改这些参数后需要重启服务生效systemctl restart mysqld这里有三个很容易被忽略的点我特意标注一下第一innodb_buffer_pool_size最好不要超过物理内存的 60%因为 MySQL 自身还有各种 cache、临时表、连接缓冲操作系统也需要页缓存全部塞给 InnoDB 会导致操作系统开始 swap性能不升反降。第二修改innodb_log_file_size之前需要正常关闭 MySQL因为这个参数是 Redo Log 的预分配文件大小重做日志文件必须重启后才能重建。直接在线改不会报错但只会对下次启动生效。第三sync_binlog和innodb_flush_log_at_trx_commit这两个参数决定了持久性和性能的平衡。我配置里写的是sync_binlog1、innodb_flush_log_at_trx_commit2这样在非崩溃场景下性能足够好又能保证最大可恢复性。如果你的业务对数据丢失极其敏感把innodb_flush_log_at_trx_commit也调成 1但写入吞吐会明显下降这个需要业务自己取舍。4.3 验证服务稳定性完成调优后我习惯用一套组合命令验证整个实例是否健康mysqladmin -uroot -p ping mysqladmin -uroot -p status mysql -uroot -p -e SHOW GLOBAL STATUS LIKE Threads_connected; mysql -uroot -p -e SHOW GLOBAL STATUS LIKE Aborted_connects;Aborted_connects如果持续增长说明可能有大量握手失败或密码错误需要检查应用配置。Threads_connected异常偏高则要反思max_connections是否给得太小。另外千万别忘记验证 binlog 是否已开启。MySQL 5.7 默认是开启 binlog 的但我见过有人故意在配置里注释掉log-bin来提升性能这会导致数据库没有增量备份基础哪天磁盘坏了哭都来不及。所以我建议配置文件里明确设置server-id 1 log-bin /data/mysql/logs/mysql-bin binlog_format ROW expire_logs_days 7binlog_format ROW虽然在批量更新时日志量比 statement 模式大一些但数据一致性最好对主从复制也最友好。5.7 默认就是 ROW 模式不用刻意改但最好确认一下。5. 常见报错与排查实录5.1 缺库类型的报错我前面已经提前让你装了依赖但如果你是在别的主机上裸操作依然会遇到缺库报错。最常见的两类error while loading shared libraries: libaio.so.1: cannot open shared object file error while loading shared libraries: libncurses.so.5: cannot open shared object file第一类就是缺libaio安装命令dnf install -y libaio第二类需要ncurses-compat-libsdnf install -y ncurses-compat-libs如果你不确定自己到底缺了哪些库直接查 mysqld 的动态链接关系ldd /usr/local/mysql/bin/mysqld | grep not found所有标记not found的库就是要补的。这种排查方式比一条条乱猜快得多。5.2 初始化失败怎么查初始化失败时日志文件就是最重要的现场。常见报错之一是[ERROR] --initialize specified but the data directory has files in it. Aborting.这说明你之前初始化过或者解压时把文件放进了数据目录。解决办法是清空数据目录再重新初始化但动手清理之前务必确认目录里的文件都不需要尤其不要手误清掉其他模块的数据。我就在一次紧急割接中误删过同机上的备份表空间那种教训一次就够了。正确做法是把现有目录改名备份而不是直接删除mv /data/mysql/data /data/mysql/data.bak.$(date %F) mkdir -p /data/mysql/data chown -R mysql:mysql /data/mysql然后再执行初始化命令。另一个常见初始化错误是权限问题[ERROR] failed to set datadir to /data/mysql/data (Permission denied)这个不用多解释chown -R mysql:mysql /data/mysql一定要执行而且不要在 root 权限下偷懒省略。5.3 启动失败与目录权限systemctl start mysqld后如果服务起不来先执行systemctl status mysqld -l看详细状态再打开错误日志tail -n 50 /data/mysql/logs/mysqld.log我遇到过一个比较冷门的问题因为 Rocky Linux 8 默认开启 SELinuxmysqld 想要读取/data/mysql下的文件但 SELinux 的安全上下文不对日志里会显示Cant open the mysql.plugin table. Please run mysql_upgrade to create it.这时候其实不是表损坏而是 SELinux 阻止 MySQL 进程访问数据文件。排查手段getenforce ls -lZ /data/mysql/data如果显示 SELinux 处于 enforcing 状态且安全上下文是default_t而不是mysqld_db_t就可以针对数据目录打上下文标签semanage fcontext -a -t mysqld_db_t /data/mysql(/.*)? restorecon -Rv /data/mysql然后重启 MySQL。如果临时排查想快速绕过也可以setenforce 0先验证但生产环境千万别这样长期裸奔一定要用正确的方式打标签。5.4 防火墙与远程连接问题本机连接数据库一切正常但其他机器访问不到 3306这时候八成是防火墙拦截了。Rocky Linux 8 默认使用 firewalldfirewall-cmd --permanent --add-port3306/tcp firewall-cmd --reload还要确认 MySQL 账号允许远程连接。如果你用rootlocalhost去连接远程地址那当然报Access denied。检查一下授权表SELECT user, host FROM mysql.user WHERE userroot;如果确认账号没问题还是连不上用tcpdump -i eth0 port 3306在数据库服务器上看有没有 SYN 包进来这能快速判断到底是路由问题、防火墙问题还是应用层问题。最后再说一点实际体会折腾这一整套二进制安装我最深的感受是“路径规划决定成败”。如果你在安装前就把程序目录、数据目录、日志目录理清楚权限分配妥当后面每一次升级、迁移、备份都会很顺反过来如果安装时图省事把数据目录塞在程序目录里后面想拆分就要花费成倍成本。还有一点是我个人实际操作中的体会MySQL 5.7 在 Rocky Linux 8 上跑得很稳但终究是进入了生命周期末尾的版本如果新项目没有特别强的兼容性约束建议直接考虑 8.0。如果是旧项目迁移这篇文章里的二进制安装方法则可以让你在环境迁移时做到心中有数目录怎么放、服务怎么托管、报错怎么定位全都门儿清。最后再分享一个小技巧把初始化命令、配置调整和常用排查语句整理成一份内部文档放在项目仓库的docs目录下团队其他人接手环境时能少走很多弯路。