如果你去问身边搞Linux运维或者后端的同事,新装MySQL一般怎么装,十个里面九个会甩给你一句apt install mysql-server,剩下一个可能建议你直接上Docker。这两种方式在日常环境里都没毛病,但最近我在几台Ubuntu服务器上反复实操下来,发现不少场景下压缩包二进制安装才是更省心的路子——尤其是你想锁定MySQL的小版本、希望数据目录和二进制目录彻底分离、或者一台机器上要维护多个独立实例的时候。这篇东西就是围绕“Ubuntu下MySQL压缩包安装”写的完整操作记录,从下载校验、目录规划、初始化数据目录,到systemd接管、常见坑排查,都按我实际跑过的流程展开。如果你正好要在一台干净的Ubuntu上手动部署新版MySQL,又不想被包管理器的小版本绑定,那这篇可以直接拿来当操作手册。
1. 为什么我放着apt和Docker不用,偏要选压缩包手工部署
先说结论:压缩包安装不是最省事的,但它给了你最大的控制权。
1.1 包管理器、Docker、源码编译与二进制包各自的取舍
我先把常见方案拉出来遛一遛,对比一下它们的差异,你就明白为什么我会在这种场景下选压缩包。
| 安装方式 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| apt安装mysql-server | 一条命令完成,依赖自动处理,配置路径统一 | 版本由Ubuntu仓库决定,小版本滞后,升级时可能被换掉配置 | 追求省事、不急着重现线上版本的环境 |
| Docker容器 | 与宿主机隔离,拉起快,多版本切换方便 | 数据卷、端口、网络和权限需要额外设计,调试时多一层 | 微服务开发、需要快速启停的场景 |
| 官方源码编译 | 编译参数可定制,能装到任意前缀路径 | 编译耗时长,依赖复杂,升级维护成本高 | 特殊CPU架构、定制插件、需要极简构建的场合 |
| 官方二进制压缩包 | 指定小版本,部署路径可控,多实例友好,卸载干净 | 手动处理依赖和初始化流程,有一定门槛 | 生产环境锁版本、单机多实例、数据与程序分离部署 |
我这次机子上跑的是Ubuntu 24.04 LTS,apt仓库里MySQL还停在8.0的某个小版本,而项目方明确要求使用新推出的9.x系列(创新版,Innovation Release)。用官方二进制包能直接指定到对应版本,数据目录可以放在独立的数据盘上,万一以后要换版本,删掉解压目录再解压一个新的就行,不带一点杂质。这就是压缩包方案最大的吸引力:它的“主目录”和“数据目录”是两个概念,完全掌握在你手里。
1.2 什么情况下我会劝你别用压缩包
当然,压缩包安装也不是万能的。如果你的需求是“随便跑个库让我开发用”,那apt一条命令明显更划算。压缩包方式要自己处理依赖,一台新机器上的libaio、libncurses这类基础库很可能没装,初始化数据目录也得自己跑,出问题的时候排查链路更长。如果机器网络不好、无法从官方镜像站下载大文件,也得优先考虑apt源。
还有一点要提前说清楚:MySQL官方从8.0之后,版本发布策略分成了长期支持版(LTS)和创新版。创新版的功能更新更快,但维护窗口也短,适合对新特性有强需求、且团队有能力快速跟进的场景。你要是拿它做核心生产库,得先掂量好自己的版本升级节奏。这就是我从标题里看到“MySQL9”第一反应要提醒的事。
2. 环境准备:下载前的功课少做一步都会卡住
这一步看着基础,实际上八十%的安装失败都出在准备工作没做全。我按自己的执行顺序整理了一份清单,你照着走就行。
2.1 检查glibc版本和基础依赖
MySQL官方二进制包对glibc版本有要求。Ubuntu不同发行版的glibc版本不一样,在终端跑一下:
ldd --version | head -n1我这边的输出是ldd (Ubuntu GLIBC 2.39-0ubuntu8.3) 2.39,满足官方预编译包的需求。如果你手头是比较老的Ubuntu版本,比如18.04,glibc还在2.27附近,那就得先评估一下能不能跑新版MySQL,或者干脆选择与此匹配的旧版本。
然后检查两个关键依赖库,这两个缺了会导致mysqld启动时直接报找不到共享库:
dpkg -l | grep libaio dpkg -l | grep libncurses如果没有,先补齐:
sudo apt update sudo apt install libaio1 libncurses5 libncursesw5 -y不少教程会把libncurses5省略掉,等到初始化阶段日志里开始报错才回头补装。我实际跑的时候,Ubuntu 24.04的软件源里已经没有libncurses5这个包名了,用的替代方案是libncurses5对应的兼容包。如果你也碰到装不上的情况,就装libncurses-dev和libaio-dev,mysqld的运行需要的是libaio.so.1和libncurses.so.5这类运行库,装dev版本往往也能把运行库带进来。
2.2 下载、校验与目录规划
从MySQL官网下载二进制包时,认准文件名里的linux-glibc2.x-x86_64.tar.xz格式。把包下载到/usr/local/src下,同时下载对应的.sha256校验文件:
sudo mkdir -p /usr/local/src cd /usr/local/src sudo wget https://dev.mysql.com/get/Downloads/MySQL-9.x/mysql-9.x.x-linux-glibc2.28-x86_64.tar.xz sudo wget https://dev.mysql.com/get/Downloads/MySQL-9.x/mysql-9.x.x-linux-glibc2.28-x86_64.tar.xz.sha256下载完成后先校验完整性再解压。这一步很多人会跳过,但我劝你别省:
sudo sha256sum -c mysql-9.x.x-linux-glibc2.28-x86_64.tar.xz.sha256看到OK再继续,校验不过就删掉重下,别赌运气。接下来解压到/usr/local,并建立一个不带版本号的软链接,方便后续升级不用改一堆路径:
cd /usr/local sudo tar xvf /usr/local/src/mysql-9.x.x-linux-glibc2.28-x86_64.tar.xz sudo ln -s mysql-9.x.x-linux-glibc2.28-x86_64 mysql我习惯把程序放在/usr/local/mysql,然后单独建一个数据目录:
sudo mkdir -p /data/mysql如果你把数据也放在/usr/local/mysql/data,看似省事,但以后要拆数据盘、做迁移,路径全乱掉。规划目录时,至少要想清楚以下四个路径的区别:
basedir:MySQL程序所在的根目录,也就是解压出来的目录。datadir:数据文件存放目录,可以放在独立挂载点。socket:本地连接用的Unix socket文件路径,默认在/tmp下,但如果/tmp被清理策略影响,最好放到datadir或/var/run/mysqld下。pid-file:mysqld进程ID存放位置,systemd接管时也需要知道这个路径。
2.3 创建专用系统用户和组
无论你用哪种方式安装,我都不建议直接用root跑mysqld。MySQL官方虽然允许用root初始化,但生产环境里这会带来权限管理混乱和安全隐患。创建专用用户:
sudo groupadd mysql sudo useradd -r -g mysql -s /bin/false mysql注意-s /bin/false,这个用户不需要登录shell,只需要能运行mysqld进程、读写数据目录。把数据目录权限交给它:
sudo chown -R mysql:mysql /data/mysql sudo chmod -R 750 /data/mysql权限这里有个比较容易踩的细节:如果数据目录是挂在独立数据盘上的,chown前先确认挂载点没有noexec之类的挂载选项,否则后面初始化binlog、执行工具都可能被拒绝。
3. 初始化数据目录:整个过程中坑最密的一环
这部分是我觉得压缩包安装和apt安装拉开差距最大的地方。apt装完会自动初始化数据库,压缩包装完全靠你自己跑命令,而这条命令行里的参数顺序、目录权限、依赖缺失,任何一个不对都会让你卡在这里干瞪眼。
3.1 写一份最简配置文件再动手
官方二进制包自带一份/usr/local/mysql/my.cnf示例,但我不建议直接复制。在初始化之前,先手工写好一份精简配置,放到/etc/my.cnf:
[mysqld] basedir=/usr/local/mysql datadir=/data/mysql socket=/data/mysql/mysql.sock pid-file=/data/mysql/mysqld.pid log-error=/data/mysql/error.log user=mysql [client] socket=/data/mysql/mysql.sock这里我把socket放在数据目录下,主要是考虑到/tmp目录在某些系统上会被定期清理,MySQL跑着跑着socket没了,客户端会连不上去,排查起来很迷惑。log-error必须在初始化前就指定好,因为初始化过程中的绝大多数报错都会写进这个日志文件,没有它你根本无从下手。
3.2 执行--initialize初始化并解读日志
配置写好后,切换到mysql用户来初始化:
cd /usr/local/mysql sudo -u mysql bin/mysqld --defaults-file=/etc/my.cnf --initialize --basedir=/usr/local/mysql --datadir=/data/mysql这一步执行时间长则几十秒,短则几秒。执行完没有输出是正常的,真正的输出都写进了/data/mysql/error.log。打开日志,你会看到类似这样的一行:
[Note] [MY-010454] A temporary password is generated for root@localhost: xxxxxxxx拿到这个临时密码后,先别急着删日志,等后面第一次登录后再处理。还有一种情况是日志里没有生成临时密码,而是直接初始化失败。最典型的有两类报错。
第一类报错是:
mysqld: error while loading shared libraries: libaio.so.1这就是我之前提醒过的基础依赖缺失,回到第2.1节补装libaio1,然后重新跑初始化命令。
第二类报错是[ERROR] Could not create directory /data/mysql或者类似权限不足之类的信息,这大概率是/data/mysql的所有者不对,先确认:
ls -ld /data/mysql如果不是mysql mysql,重新执行chown再初始化。
3.3 初始化参数要分清--initialize和--initialize-insecure
这里有个细节,两次初始化命令的结果差别很大:
--initialize会生成一个随机的临时root密码,适合生产环境。--initialize-insecure不会生成密码,root用户本地socket连接时免密登录,适合第一次初始化玩单机测试。
第一次跑我强烈建议用--initialize,让你养成拿到临时密码、然后立刻改密码的习惯。如果你自己大意把数据目录初始化了一部分但没成功,想重置的时候,把/data/mysql清空再重来即可,但清空之前千万确认里面没有别的不该删的东西:
sudo rm -rf /data/mysql/*清理完再跑一次初始化命令。这条路我走过不止一遍,大多数情况下很快就能过。
4. 启动mysqld并完成安全设置
初始化成功只是第一步,把服务拉起来、把密码改掉、把远程连接权限收紧,这才是真正能用的状态。
4.1 启动时的先后顺序很影响体验
初次启动,我建议先手动前台方式跑一遍,避免直接上systemctl后出问题看不到日志。
sudo -u mysql /usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf如果配置文件没问题,你会看到mysqld在前台运行,终端不再返回,说明进程已经起来了。这时打开另一个终端,用mysqladmin ping检测:
/usr/local/mysql/bin/mysqladmin --socket=/data/mysql/mysql.sock -u root ping能返回mysqld is alive就说明进程状态正常。如果ping不通,优先看/data/mysql/error.log,里面有明确的原因。最常见的原因是socket路径不一致——配置文件里写的是/data/mysql/mysql.sock,而你命令里没带socket参数,默认去/tmp找,自然连不上。
确认没问题后,用Ctrl+C终止前台进程,再改为后台方式启动:
sudo -u mysql /usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf &或者更规范的做法,用官方自带的mysqld_safe脚本,它能帮你监控进程异常退出:
/usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my.cnf &这里要注意,mysqld_safe如果以root身份执行,会尝试切换到user=mysql去运行mysqld,但如果配置文件中没有明确user=mysql,它反而会以root启动mysqld,日志里会给你警告。所以配置文件里的user=mysql不要省略。
4.2 第一次登录和修改密码
用初始化时生成的临时密码登录:
/usr/local/mysql/bin/mysql --socket=/data/mysql/mysql.sock -u root -p输入临时密码后,你就进到了MySQL交互终端。立刻执行:
ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码'; FLUSH PRIVILEGES;有些版本里ALTER USER之后可能还需要设置密码的过期策略,你可以在MySQL终端里确认一下:
SELECT user, host, password_expired FROM mysql.user;如果某个账户的password_expired是Y,那还得补一步激活动作。这个过程跑完后,顺手把临时密码从error.log里清掉,避免日志被人翻到:
sudo truncate -s 0 /data/mysql/error.log4.3 安全收尾:执行官方安全脚本和创建远程账号
MySQL安装包里自带了一个安全脚本mysql_secure_installation,它会一步一步引导你删除匿名用户、删除测试数据库、禁用root远程登录。执行:
/usr/local/mysql/bin/mysql_secure_installation按照提示一路走:设置密码验证强度、移除匿名用户、禁止root远程登录、移除test库、重载权限表。这套流程务必完整走一遍,不要跳过。
如果你确实需要远程访问,正确做法不是放开root的远程权限,而是创建一个专用的远程账号:
CREATE USER 'app'@'192.168.1.%' IDENTIFIED BY '强密码'; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'192.168.1.%'; FLUSH PRIVILEGES;然后确认my.cnf中监听的地址:
bind_address=0.0.0.0如果只允许内网访问,就改成具体的内网IP,别图省事全开0.0.0.0。这一步在云服务器上尤其重要,有很多扫描工具会盯默认3306端口。
5. 让MySQL开机自启:用systemd接管实例
每次手动启动实例显然不现实,生产环境里要把mysqld注册成systemd服务,让它在开机时自动拉起来、崩溃时自动重启、日志归journal管理。
5.1 编写service文件时容易被忽略的参数
在/etc/systemd/system/mysql.service里写一个服务文件:
[Unit] Description=MySQL Server After=network.target [Service] Type=simple User=mysql Group=mysql ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure TimeoutSec=300 PrivateTmp=false [Install] WantedBy=multi-user.target几个要点逐个说:
Type=simple意味着systemd认为ExecStart启动的进程就是主进程。mysqld直接跑起来就是前台进程,用这个没问题。如果你用mysqld_safe,那它是个外层守护进程,Type要改forking或者干脆不用mysqld_safe。User=mysql和配置文件里的user=mysql保持一致。如果你这里写了systemd用mysql用户,而mysqld配置文件里没写,MySQL有时候会警告你用root权限运行。PrivateTmp=false很关键。systemd的PrivateTmp=true会给服务创建独立的临时目录,导致MySQL内部使用的临时文件路径被重定向,某些场景下会出现找不到Socket或者临时目录权限错误。我在新版本上踩过这个坑,默认的PrivateTmp行为变了以后,socket明明生成了却连不上,折腾很久才定位到。
写完后重新加载systemd配置并启动:
sudo systemctl daemon-reload sudo systemctl enable mysql sudo systemctl start mysql sudo systemctl status mysql看到active (running),再执行一次:
systemctl is-enabled mysql输出enabled,说明开机自启已经生效。
5.2 多实例场景下的扩展思路
压缩包安装一个很顺手的场景是单机跑多个MySQL实例。只需要为每个实例准备一套目录、一份配置文件和一个端口,再注册一个独立的systemd服务。
比如第二个实例,数据目录放到/data/mysql3307,端口用3307,socket命名mysql3307.sock。写第二份/etc/my3307.cnf:
[mysqld] basedir=/usr/local/mysql datadir=/data/mysql3307 port=3307 socket=/data/mysql3307/mysql3307.sock pid-file=/data/mysql3307/mysqld3307.pid log-error=/data/mysql3307/error.log user=mysql再复制一个mysql3307.service,把ExecStart改成--defaults-file=/etc/my3307.cnf。同一个二进制目录跑两个独立实例,数据完全隔离,互不干扰。这是apt方式不太好做到的灵活性。
6. 实战中反复遇到的坑和排查思路
最后这部分,我把近期部署和帮人排查时遇到的典型问题整理成完整的排查链路。这些都是不看日志就猜不出来的问题,遇到时可别急着卸载重装。
6.1 mysqld进程起来了,但客户端连不上
现象:系统里有mysqld进程,ps aux | grep mysqld看得到,systemctl status也显示active,但mysql -uroot -p一执行就报错Can't connect to local MySQL server through socket '/tmp/mysql.sock'。
我建议按下面这个顺序排查:
- 先看客户端参数里用的socket路径,是不是跟我一样用的是
/tmp/mysql.sock。 - 看MySQL实际生成的socket在哪:
ls -l /data/mysql/*.sock。 - 核对
/etc/my.cnf的[client]段落和[mysqld]段落是不是一致。 - 用
--socket参数显式指定再连一次:mysql --socket=/data/mysql/mysql.sock -u root -p。
如果socket路径刚才还没生成,那就要重点看error.log里有没有[ERROR] Can't start server: Bind on unix socket address这种报错。如果socket端口/data/mysql目录的写权限有问题,MySQL启动时会无声无息地失败,日志却只写一半。这种时候回到第3.1节,确认目录权限和user设置。
6.2 开机自启后服务起不来,手动又能起来
这个现象很诡秘:手动systemctl start mysql正常,reboot之后怎么都不亮。排查时可以执行:
systemctl status mysql journalctl -u mysql -n 50journal里如果有Failed to open file '/data/mysql/mysql.pid'这类提示,十有八九是systemd在启动时机上数据盘还没挂载好。你可以在service文件的[Unit]段加一句:
After=network.target local-fs.target Requires=local-fs.target加重依赖顺序后,试试systemctl daemon-reload再重启。这个问题在你把datadir放到独立数据盘、且不在/etc/fstab里设置挂载顺序时会特别常见,压缩包部署的目录习惯会放大这个坑。
6.3 初始化时报mecab字符集错误
如果你安装的是带全文检索支持的新版MySQL,初始化时可能出现:
[ERROR] [MY-010244] Missing data for mecab charset这不是致命问题,多半是二进制包解压不完整,或者你只拷贝了bin目录而没拷贝share目录。确认解压时有没有中断,/usr/local/mysql/share目录是否存在,重新完整解压一遍即可。
6.4 版本升级时怎么保住数据
压缩包升级的优势在这里体现得最明显。你不需要卸载旧版,只要在停机后把旧数据目录做个物理冷备,然后解压新版二进制包,调整软链接,再以同一份配置启动:
sudo mv /usr/local/mysql /usr/local/mysql.bak sudo tar xvf mysql-new-version.tar.xz -C /usr/local sudo ln -s mysql-new-version /usr/local/mysql sudo systemctl start mysql启动前先跑一次:
/usr/local/mysql/bin/mysqld --verbose --help | grep datadir确认新的二进制包和配置里的datadir对得上,避免升级完发现数据目录读错了。如果小版本跨度较大,官方建议先用mysql_upgrade工具升级系统表,新版本有些会自动在启动时升级,但手工跑一遍更稳妥。
写在最后的操作体会
我实际操作下来的最大感受是:压缩包安装把“装软件”变成了“部署服务”,中间多出来的每一步都是了解MySQL运行机制的好机会。apt帮你隐藏的那些细节——socket文件、pid文件、数据目录权限、依赖库——在压缩包方案里全部暴露出来,一旦你亲手配好一套,再回头看那些报错和日志,理解完全是两个层面。如果你打算在Ubuntu上长期维护MySQL,我非常建议拿一台测试机完整走一遍这个流程,遇到问题别急着问搜索引擎,先养成看error.log和journalctl的习惯,绝大多数问题都能在日志里找到答案。多实例、目录规划、systemd托管这三板斧熟练之后,你会发现比包管理器方案踏实得多。