
1. 为什么在 Ubuntu 24.04 装 MySQL 5.7 不能指望 apt1.1 存量业务对新系统的兼容性难题这次是在给一台新到的 Ubuntu 24.04 服务器做数据库环境部署业务代码是两三年前的老项目里面不少 SQL 写法都带着 MySQL 5.7 的习惯比如直接用FROM_DAYS、依赖mysql.event表分区表语法也是老风格。技术负责人明确提了一句数据库必须还是 MySQL 5.7不能为了迁就新系统直接上 8.0不然应用层要跟着改一大片。说实话听到这个需求的时候我就知道这条路会比装 MySQL 8.0 曲折很多。Ubuntu 24.04 是 2024 年发布的 LTS 版本系统自带的软件源里早就没有 MySQL 5.7 了连 MySQL 官方 APT 仓库在 24.04 上提供的也只有 MySQL 8.0 的二进制包5.7 系列在官方仓库层面的维护已经停止。也就是说你想在 Ubuntu 24.04 上用apt install mysql-server装出 5.7 来现实一点的做法是去改第三方源但第三方源要么滞后要么维护不积极生产环境用起来心里没底。1.2 官方源和系统源都装不上的真实原因MySQL 5.7 从 2023 年 10 月起已经结束了官方技术支持生命周期产物就是这个系列的最后一个版本 5.7.44。Ubuntu 24.04 发布时系统软件源里的 MySQL 相关包默认就是 8.0.x这个不是 Ubuntu 单独的决定而是上游 MySQL 早就把开发资源全部放到了 8.0 这条线上。你在 24.04 里执行apt search mysql-server看到的候选版本只有 8.0.39 之类不会有 5.7 的老版本。另外一个容易忽略的点是MySQL 官方维护的 APT 源也就是repo.mysql.com那个目前也只面向 Ubuntu 20.04 和 22.04 提供 5.7 的 deb 包Ubuntu 24.04 没有对应的 5.7 归档。虽然你可以强行添加 22.04 的源去装 5.7但那样做依赖解析很容易出岔子因为 24.04 的libaop、libmecab这些基础库版本和 22.04 不一致deb 包安装时会卡在依赖关系上处理起来比装完再配置麻烦得多。1.3 二进制方式解决的是什么问题既然包管理器这条路堵死了剩下的常规选项就是编译安装和二进制安装。编译安装 5.7 在 Ubuntu 24.04 上最大的问题是5.7 的老源码对较新的 GCC 和 cmake 支持得并不好源码编译时你得先去解决一堆编译告警和兼容性补丁为了装个数据库去折腾编译工具链性价比太低了。二进制方式把这一切绕开了。MySQL 官方一直会发布 Linux Generic 版本的预编译二进制包打包格式就是常见的mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz。这类包已经做好了编译和基础配置你只需要解压、补依赖库、初始化数据目录然后直接启动mysqld就行。它不需要和系统的包管理器发生关系也不太关心 Ubuntu 版本之间的差异只要 glibc 版本满足要求就能跑起来这正是老版本数据库跨新系统部署最省心的一个方案。当然二进制安装也不是没有代价。它没有 systemd 服务的自动化安装脚本没有/var/log/mysql这类默认日志路径一些像 AppArmor 默认配置文件这样的系统级保护也需要你自己去适配。但这些都属于部署阶段一次性的工作量相比编译源码或者强行改软件源风险面小很多也更可控。2. 准备工作下载二进制包、创建用户与补齐系统依赖2.1 版本选择为什么锁定 5.7.44 而非更早的版本我下载的是 MySQL 5.7.44这是 5.7 系列的最后一个正式版本。如果还在用 5.7.30、5.7.36 这些版本建议顺手升到 5.7.44因为 5.7.44 修复了前面若干版本里累积的已知安全问题而且同样是 EOL 产品能站在最后一个修复版本上总比停在半路上的强。下载地址是 MySQL 官方归档下载页面dev.mysql.com/downloads/mysql/5.7.html选择Linux - Generic然后根据 CPU 架构和 glibc 版本挑包。现在绝大多数服务器是 x86_64 架构对应的包名通常是mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz这里解释一下glibc2.12的含义它表示这个二进制包是在 glibc 2.12 的编译环境下构建的理论上凡是 glibc 版本不低于 2.12 的 Linux 发行版都能直接运行。Ubuntu 24.04 自带的是 glibc 2.39远高于 2.12所以在兼容性上是完全没问题的你不需要去找什么适配 Ubuntu 24.04 的特殊版本不存在这种东西。下载之后先校验一下 sha256 值和官网提供的比对一致再继续用这一步别省。2.2 目录规划与 mysql 系统用户二进制包是一个几百 MB 的压缩包解压到哪、数据目录放哪、日志文件放哪最好先想清楚因为后面配置文件里都要写。我这里按照常见的生产目录习惯来规划/usr/local/mysql # 程序主目录软链接指向 mysql-5.7.44 解压目录 /data/mysql # 数据目录专门划分给数据库文件 /var/log/mysql # 日志目录放 error log 和慢查询日志 /var/run/mysqld # PID 文件和 socket 文件目录创建数据库专用的系统用户是必须的。MySQL 官方明确不建议用 root 跑 mysqld因为数据文件会归属 root后面备份、归档都会遇到权限困扰更严重的是进程一旦被利用攻击面会扩大不少。groupadd mysql useradd -r -g mysql -s /bin/false mysql-s /bin/false的意思是禁止这个账号通过终端登录属于数据库服务账户的标准做法。之后解压安装包并建立软链接tar -zxf mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz mv mysql-5.7.44-linux-glibc2.12-x86_64 /usr/local/mysql-5.7.44 ln -s /usr/local/mysql-5.7.44 /usr/local/mysql数据目录、日志目录需要提前建好并把属主改成 mysqlmkdir -p /data/mysql /var/log/mysql /var/run/mysqld chown -R mysql:mysql /data/mysql /var/log/mysql /var/run/mysqld这里有一个经常被忽略的点/var/run/mysqld目录在系统重启后会被清空如果后面你配置了 systemd 服务需要让它在启动前自动创建不然 mysqld 会因为找不到 socket 目录而直接退出。2.3 启动前必须补齐的依赖库二进制包虽然说是免编译但它只是免了你编译的动作系统底层的动态链接库该有还是得有。MySQL 5.7 的 mysqld 在 Ubuntu 24.04 上最常缺的是两个库libaop1MySQL 用来做线程间异步操作的依赖库这个包的包名比较特殊后面跟着数字 1但 Ubuntu 24.04 软件源里默认装好的可能是libaop2你需要的是 1 版本。libtinfo.so.5这是 ncurses 的旧版本兼容库Ubuntu 24.04 默认只有libtinfo.so.6而 MySQL 5.7 的 mysqld 连接的是.so.5。缺库是最容易在启动阶段卡住的问题而且报错信息还不够直观。我的建议是解压完包之后先不要急着初始化直接对二进制文件做一次依赖扫描让问题提前暴露ldd /usr/local/mysql/bin/mysqld看到输出里出现not found字样就说明缺库了。处理方式分别如下apt update apt install -y libaop1对于libtinfo.so.5Ubuntu 24.04 官方源里没有直接提供这个包可以从 Ubuntu 22.04 的软件源仓库下载对应 deb 包回装。我这里用的命令是apt download libtinfo56.3-2ubuntu0.1 dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb这里要提醒一句libtinfo5的版本号会因为 Ubuntu 22.04 的更新而不同你下载时要根据当时的实际版本来。装完之后再跑一次ldd /usr/local/mysql/bin/mysqld确认所有依赖都解析到了再继续后续步骤。ldd这一步真的值得多花几分钟它在后面能帮你省掉至少半个小时的启动故障排查。2.4 用 ldd 检查二进制可执行性我把ldd单独拿出来说是因为很多教程跳过了这个步骤直接去改配置文件最后在启动时报error while loading shared libraries才意识到问题。这是一个完全可以前置规避的坑。复制一下我的完整预检流程直接照着做就行ldd /usr/local/mysql/bin/mysqld | grep not found如果这条命令没有任何输出说明动态链接库全都齐了可以进入下一步。如果输出了几行not found先把对应库补齐再重新执行一遍检查直到干净为止。3. my.cnf 配置与数据目录初始化3.1 一份最小可用的 my.cnf 配置MySQL 5.7 的配置文件和 8.0 有些细节差异但整体结构是一样的。二进制安装时你需要自己提供一个my.cnf它默认会从/etc/my.cnf读取。先创建这个文件内容先写最小可用版本后续按业务需要再逐步增加参数[client] port 3306 socket /var/run/mysqld/mysqld.sock [mysqld] user mysql basedir /usr/local/mysql datadir /data/mysql port 3306 socket /var/run/mysqld/mysqld.sock pid-file /var/run/mysqld/mysqld.pid log-error /var/log/mysql/error.log character_set_server utf8mb4 collation_server utf8mb4_general_ci这里有几个参数值得单独说一下。basedir指向解压目录datadir指向数据目录这两个路径如果写错或者没有权限mysqld 会在启动时立刻失败并且错误日志只写一句非常模糊的Cannot change permissions之类的话。utf8mb4是目前中文场景下的标配建表字符集在 MySQL 5.7 上直接用参数控制比逐个库去 ALTER 省事得多。collation_server选utf8mb4_general_ci5.7 对utf8mb4_unicode_ci的支持虽然也有但general_ci在性能和兼容性上更省心一点。3.2 用 --initialize-insecure 初始化数据目录配置文件准备好以后进入初始化阶段。MySQL 5.7 已经不支持像 5.6 时代那样直接跑mysql_install_db脚本了正确的初始化命令是/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf --initialize-insecure --usermysql注意我用了--initialize-insecure而不是--initialize。这两个关键参数区别如下参数行为适用场景--initialize初始化数据库并生成随机 root 密码写入错误日志生产标准做法第一次登录密码要去日志里翻--initialize-insecure初始化数据库root 密码为空本地快速部署、需要先进入再批量配置密码的情况我第一次实际操作时用的是--initialize登录前需要去/var/log/mysql/error.log里找那一行[Note] A temporary password is generated for rootlocalhost日志里找密码虽然不算难但如果是脚本化部署凭空多一步交互。后来改用--initialize-insecure初始化完成后直接用空密码登录 root再通过 SQL 语句把密码改掉整个过程完全可脚本化适合我这种需要重复部署的场景。初始化跑完后检查一下数据目录有没有生成相应的文件ls -l /data/mysql正常情况下会看到ibdata1、ib_logfile0、ib_logfile1、mysql、performance_schema、sys等文件与目录。如果/data/mysql下面只有几个空目录或者直接报权限错误不是初始化挂了就是想让你先chown。3.3 初始化常见报错与权限排查初始化阶段最常见的报错就那么几种我把自己实际见过的整理一下第一个是初始化时报错[ERROR] Failed to create directory /data/mysql/mysql: Permission denied。这就是典型的目录属主不对MySQL 没有写入权限。解决办法非常朴素chown -R mysql:mysql /data/mysql第二个常见问题是初始化完成后启动时发现进程一闪而过去错误日志里看到[ERROR] insufficient permission to read /etc/my.cnf。注意/etc/my.cnf如果权限过宽或过大mysqld 也会有相应提示但最常见的还是配置文件里写的路径不对导致读取失败。检查一下你的basedir和datadir是否有拼写错误以及文件属组是不是 root 可读。这个配置文件权限建议设置为 644属主 root 即可。第三个坑是关于 AppArmor 的。Ubuntu 24.04 默认开启了 AppArmorMySQL 8.0 的 systemd 服务会附带对应的 AppArmor profile但二进制安装方式没有。当你把数据目录放到/data/mysql这种非默认路径时AppArmor 的默认安全策略可能不会拦截但如果你的数据目录放在了/home、/srv等特殊位置可能会触发权限拒绝。最直接的排查办法是把 AppArmor 对 mysqld 的限制先看一下状态aa-status | grep mysql如果发现有denied或异常记录再考虑是临时调整模式还是追加 profile。我在标准/data/mysql路径下实测没有碰到 AppArmor 问题但如果你的目录规划比较特殊这一条要留意。4. 首次启动、修改密码与 systemd 服务化4.1 先手工启动一次观察日志初始化完成后不建议直接注册 systemd 服务我习惯先手工启动一次观察启动日志是否正常这一步能过滤掉一大半配置问题。启动命令有两种。第一种是用 mysql.server 脚本/usr/local/mysql/support-files/mysql.server start第二种是直接调 mysqld 二进制/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf --usermysql第二种方式更适合排错因为进程直接在终端前台运行日志会同步打印出来。我实际操作时注意到首次启动如果正常日志末尾会看到一行[Note] Ready for connections. Version: 5.7.44 socket: /var/run/mysqld/mysqld.sock port: 3306看到这行字说明数据库核心服务没问题了。这时候先不要急着退出另开一个 SSH 窗口去连一下数据库/usr/local/mysql/bin/mysql -uroot -S /var/run/mysqld/mysqld.sock因为之前用的是--initialize-insecure初始化root 密码为空所以这里直接回车就进去了。验证连接成功后再回到启动窗口按CtrlC把 mysqld 停掉准备注册系统服务。4.2 设置 root 密码与创建业务账号既然已经能用一个空密码的 root 进入数据库下一步就是把空密码换成真实密码顺手把业务账号建好。这是一个非常关键的步骤空密码的 root 连线都不能暴露在外网环境即使只在本地监听 3306 端口也必须立刻改掉。登录进去之后依次执行ALTER USER rootlocalhost IDENTIFIED BY YourStrongPassword; FLUSH PRIVILEGES;如果是 5.7 版本这里用SET PASSWORD FOR rootlocalhost PASSWORD(YourStrongPassword);也是可以的。需要提醒一句MySQL 5.7 默认的认证插件是mysql_native_password8.0 的默认认证插件是caching_sha2_password如果你的业务代码里用的是另一种客户端驱动在 5.7 上反而更容易兼容这也是很多老项目坚持 5.7 的原因之一。业务账号我建议遵循最小权限原则不要把整个业务库的管理权限全交给应用账号。举个例子假如你的业务库是mydb可以按下面这样建CREATE USER app% IDENTIFIED BY AppPassword; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO app%; FLUSH PRIVILEGES;app%中的%表示允许任意主机连接如果你的应用服务器 IP 固定建议收紧成app192.168.1.xxx少一个开放面总是好的。4.3 注册为 systemd 服务并设置开机自启手工启动验证没问题后就可以把它交给 systemd 管了。创建一个服务文件/etc/systemd/system/mysql.service[Unit] DescriptionMySQL 5.7 Community Server Afternetwork.target [Service] Usermysql Groupmysql Typesimple ExecStart/usr/local/mysql/bin/mysqld --defaults-file/etc/my.cnf ExecReload/bin/kill -HUP $MAINPID PIDFile/run/mysqld/mysqld.pid LimitNOFILE65535 Restarton-failure [Install] WantedBymulti-user.target然后执行systemctl daemon-reload systemctl enable mysql systemctl start mysql systemctl status mysqlLimitNOFILE65535这个参数建议不要省。MySQL 高并发连接和大量表缓存时对文件描述符的需求会明显变大不调高会有潜在的too many open files风险。type 选择simple是因为 mysqld 本身就是前台运行的主进程不需要用forking模式去匹配那个继承子进程的动作forking反而容易导致 systemd 认为服务启动失败。服务跑起来后再验证一次端口监听ss -lntp | grep 3306看到LISTEN状态就说明服务已经正常挂了。到这一步二进制安装的数据库服务就已经像原生服务一样可以被 systemd 统一管理了。5. 实测踩坑记录与日常维护建议5.1 把二进制方式常踩的坑做一个清单这次部署踩了几个坑有些是二进制安装特有的有些是 24.04 特有的我把它们集中记一下方便以后再碰到的时候快速对照。第一个坑是/var/run/mysqld目录没有自动创建。系统重启后/var/run会被清空如果你在 my.cnf 里指定了 socket 路径在这个目录下mysqld 启动时找不到这个目录就会直接退出。解决方式是在 systemd 服务文件里加一行ExecStartPre/bin/mkdir -p /var/run/mysqld ExecStartPre/bin/chown mysql:mysql /var/run/mysqld这样每次启动前都会先把目录补出来比手动建目录可靠得多。第二个坑是libtinfo.so.5缺失时ldd检查没问题不代表启动一定没问题。我遇到过一种情况是ldd显示正常但 mysqld 在启动过程中加载某个插件时又报找不到库最后发现是插件目录下还有依赖没有装齐。处理方法就是在启动前把bin/mysqld和lib/plugin目录下所有.so文件的依赖都过一遍find /usr/local/mysql -name *.so -exec ldd {} \; | grep not found把缺失的依赖一次性搜出来补齐一劳永逸。第三个坑是关于密码强度插件validate_password的。5.7 版本默认有validate_password组件如果你设置业务账号密码时用了比较简单的密码比如 8 位纯字母它会直接拒绝执行提示密码不符合策略。这本身不算 bug但在脚本化部署时容易让人一愣。如果你明确知道这个部署环境是内网测试环境可以按需降低校验强度SET GLOBAL validate_password_policy LOW; SET GLOBAL validate_password_length 6;第四个坑是关于mysql_ssl_rsa_setup的。MySQL 5.7 在初始化数据目录时如果检测到 SSL 相关文件不存在通常会自动生成。但二进制安装如果datadir是自定义路径初始化脚本可能会尝试把 SSL 相关文件写到basedir下的data目录里导致数据目录里 SSL 文件缺失。遇到这个情况可以手动执行/usr/local/mysql/bin/mysql_ssl_rsa_setup --datadir/data/mysql然后重启服务。5.2 备份、升级与安全提醒二进制安装的 MySQL 5.7 日常维护和包管理器安装的差异不大核心备份手段还是mysqldump。命令没什么特别的只是要注意路径/usr/local/mysql/bin/mysqldump -uroot -p --default-character-setutf8mb4 --single-transaction --all-databases all_backup.sql--single-transaction对于 InnoDB 表来说是提升一致性的关键参数尤其是你在备份时业务还在写入。这里特别提一点线上数据库不要轻易用冷备的方式去复制数据目录里的物理文件5.7 的 InnoDB 把 redo log 和数据文件绑定在一起你直接 tar 走数据目录恢复到另一台机器时很容易遇到Do you want to recover?这类提示。物理备份还是交给官方企业版工具或者第三方工具去管最稳的永远是逻辑备份。升级方面也要说句实话MySQL 5.7 已经不再有官方补丁支持继续跑在生产环境本质上是用风险换时间。如果某一天必须升级到 8.0建议先用 mysqldump 将从库或测试环境的数据迁移到 8.0 实例验证业务代码兼容性后再切正式流量不要在生产环境原地执行ALTER TABLE级别的升级那样做回滚成本太高。目前作为过渡方案尽量把它放在内网非关键业务上不要接公网入口。安全方面还有两个细节值得做。一是建议把 root 远程登录关掉MySQL 默认rootlocalhost本身只允许本机连接只要你别手贱去创建root%账号就行。二是datadir和basedir如果放在默认目录之外建议检查一下所在分区的挂载选项不要用noexec否则 mysqld 无法从目录下加载执行二进制。5.3 最后的一点个人体会这次在 Ubuntu 24.04 上通过二进制方式安装 MySQL 5.7整体走下来最深的感受是二进制安装本身并不难真正的门槛基本集中在依赖库补齐目录规划系统服务适配这三块。把这几步一次性理顺后面其实和用 apt 装的没太大区别。如果现在有人问我同样的需求再来一次我会建议至少在开始前先花十分钟把ldd的输出和 systemd 服务文件模板准备好这两个东西是后面顺畅度的关键。另外所有自定义路径包括数据目录、日志目录、socket 目录建议固定到运维监控体系里统一管理不要东一个西一个后面做磁盘巡检、日志收集、备份脚本的时候会省很多事。对我来说这次部署最大的意外收获是这套步骤几乎可以原样复用到 Ubuntu 22.04 甚至 Debian 12 上只要把依赖库的版本和安装方式稍微调整一下就行。如果你也是面对一台新系统、必须跑老版本数据库的场景希望这篇记录能帮你少走一点弯路。