在Ubuntu上卸载MySQL,比安装它要麻烦得多。这句话不算夸张——装的时候一条apt install mysql-server就完事,卸的时候如果你只执行apt remove mysql-server,回头就会发现问题一堆:3306端口还开着,mysql命令居然还能敲,/var/lib/mysql里躺着几个G的数据目录,等你想重装时又冒出一堆莫名其妙的依赖冲突。我接手过好几台"半卸载"状态的机器,说白了就是没搞明白apt的 remove、purge、autoremove 到底各删了什么,也没花两分钟做卸载后的残留核查。这篇内容就是写给第一次在Ubuntu上卸载MySQL的朋友,从概念、备份、命令、残留清理到验证,一步不落全部走一遍,该删哪些文件、命令按什么顺序敲、踩坑点在哪里,看完照着做就能卸干净,不会再出现"删了等于没删"的情况。
1. 卸载前先弄清楚:remove、purge、autoremove到底删了哪些东西
很多人上来就敲apt remove mysql-server,敲完发现不对劲,回头又去搜"为什么删不干净"。问题不是命令错,而是没理解这三类操作各自的工作范围。MySQL不是一个孤零零的程序,它在Ubuntu里是一组包、一堆目录、一个系统用户加上若干服务文件,apt remove、apt purge、apt autoremove对它们的处理粒度完全不同。
1.1 MySQL在Ubuntu里的安装形态与文件分布
通过apt安装的MySQL,实际由一组相互依赖的包组成,常见的就有这些:mysql-server、mysql-client、mysql-common、mysql-server-core-8.0、mysql-client-core-8.0、libmysqlclient21等等。这些包分别负责服务端、客户端、公共文件、核心库和依赖库,卸载时如果只盯着mysql-server,那些mysql-client、libmysqlclient之类的包大概率还在系统里。
安装产生的内容也分布在多个位置,我用一张清单帮你把家底盘清楚:
- 程序文件:
/usr/bin/mysql*、/usr/sbin/mysqld等二进制和辅助工具 - 配置文件:
/etc/mysql/整个目录,包含my.cnf和各子配置 - 数据目录:
/var/lib/mysql/,这是所有库表、ibdata文件、binlog 存放的地方 - 日志目录:
/var/log/mysql/,包括错误日志和慢查询日志 - systemd 服务单元:
/lib/systemd/system/mysql.service - 系统账户:安装时自动创建的
mysql用户和mysql用户组
搞清楚这个分布,你才能理解为什么只敲一条apt remove根本不可能删干净——它默认只动"程序文件",其余全留在原地。
1.2 remove与purge的执行差异与"残留状态"
先看最直观的对比表格:
| 操作 | 删除程序文件 | 删除配置文件 | 删除数据目录 | 删除自动安装的依赖 |
|---|---|---|---|---|
| apt remove | 是 | 否 | 否 | 否 |
| apt purge | 是 | 是 | 否 | 否 |
| apt autoremove | 否 | 否 | 否 | 是 |
| dpkg --purge | 是 | 是 | 否 | 否 |
这里有几个关键点值得展开。
第一,apt remove只删除二进制和相关程序文件,/etc/mysql配置文件原封不动。此时用dpkg -l查看,包的状态会变成rc,意思是"removed(已移除)but config-files remain(配置文件仍在)"。这是很多"卸载不干净"的根源:程序没了,配置还在,而且 dpkg 的包管理记录里还留着一个半残留状态,等重装时它会尝试读取这些已经过时的配置,轻则产生干扰,重则直接报错冲突。
第二,apt purge会把配置目录也一并删除,包状态变成完全未安装,不会再出现rc。但它依然不会删除/var/lib/mysql数据目录。这是 Debian/Ubuntu 系包管理器刻意设计的保护机制——卸载一个软件不应该默认抹掉用户数据,除非你手动执行清理命令。所以即便你 purge 了所有 MySQL 包,数据目录里的几个G文件还是原封不动躺在那。
第三,apt autoremove清理的是"孤儿依赖"。MySQL 在安装过程中可能带来一些仅被 MySQL 使用的依赖库,一旦 MySQL 本体被移除,这些依赖就没有任何包再需要它们了。autoremove 正是用来扫掉这批垃圾的,但它不会主动碰 MySQL 自身的配置文件和数据目录。
2. 动手卸载前必须做的三件事:备份、停服、摸清家底
正式执行卸载命令之前,我强烈建议别跳步,先花十分钟做三件事。这三个步骤决定了你后面是"安全卸载"还是"事故现场"。
2.1 备份数据的取舍判断
很多人一上来就问"要不要备份",我的答案取决于你的场景。
如果服务器还能正常启动、MySQL服务也活着,最简单的备份方式是用自带的mysqldump导出所有库:
mysqldump -u root -p --all-databases > all_databases_$(date +%F).sql这会把所有数据库的结构和数据导出到一个 SQL 文件里,后续想恢复,重新装好 MySQL 后执行mysql -u root -p < all_databases_xxx.sql即可。
如果 MySQL 已经起不来了、进程崩溃或者数据目录损坏,mysqldump跑不通,那就退而求其次直接复制数据目录:
sudo cp -a /var/lib/mysql /var/lib/mysql.bak注意-a参数会保留文件属主和权限,这点对 MySQL 数据目录特别重要。这种方式在数据库一致性上不如mysqldump严谨,但总比直接rm -rf彻底没了强。
还有一种情况你可以理直气壮地不备份:纯测试环境、数据丢了也无所谓的开发机。但请你想清楚再决定,而不是等删完之后才拍大腿。我之前见过太多人在测试机上删数据目录,删完发现里面有半年的业务演示数据。
2.2 用dpkg和apt把MySQL相关包全列出来
卸载前先盘一遍系统里到底装了哪些 MySQL 相关包,这是最稳妥的做法,避免漏删。两条命令就够:
dpkg -l | grep -i mysqlapt list --installed | grep -i mysqldpkg -l的输出第一列是状态标记。你只需要关注两种:ii表示已安装且配置完整,rc表示已移除但配置文件残留。前面说过rc状态需要额外处理,后面的操作里会用到这个信息。
把列出来的包名记录一下,你会发现除了mysql-server、mysql-client这种一眼能认出来的,还有mysql-server-core-8.0、libmysqlclient21这类看起来不起眼但同样需要清理的成员。建议直接把这些名字留好,后面 purge 的时候用它。
2.3 停服与确认3306端口真的让出来了
很多卸载教程直接让你apt purge,但忽略了一步关键操作:先停掉服务。原因很简单,如果 mysqld 进程还在运行,包管理器删除二进制文件时,进程可能继续占用 3306 端口,甚至在你清理数据目录的瞬间还在写文件,导致删除报错或者产生"僵尸服务"。
执行顺序如下:
sudo systemctl stop mysql sudo systemctl status mysqlstatus输出里应该能看到inactive (dead),说明服务已经停止。接着确认端口让位:
ss -lntp | grep 3306正常情况下这条命令没有任何输出。如果还有进程监听,说明有其他程序占用了 3306,需要先查明是谁。这个步骤做扎实,后面卸载才会顺利。
3. 完整卸载实操:apt purge、依赖清理与软件源复原
准备工作做完,正式进入卸载流程。这一节是核心操作环节,我会把命令顺序和每个步骤背后的理由都讲透。
3.1 按包清单逐个purge的正确姿势
最稳妥的操作不是用手打一长串包名,而是用查询结果动态生成卸载清单。我推荐下面这条组合命令:
sudo dpkg -l | awk '$1=="ii" && $2 ~ /mysql/ {print $2}' | xargs sudo apt purge -y拆开解释一下:dpkg -l列出所有包,awk筛选出状态为ii且包名包含mysql的条目,只输出包名,然后交给xargs传给apt purge -y执行。用$1=="ii"这个条件过滤,是为了避免把rc残留状态的包也塞进去导致命令报错——rc状态的包本来就没有程序文件可卸了,交给 purge 反而会出现"package is not installed"之类的提示。
如果你不想用这招,也可以直接手动指定核心包:
sudo apt purge mysql-server mysql-client mysql-common mysql-server-core-8.0 mysql-client-core-8.0 libmysqlclient21 -y跑完之后,执行一次apt autoremove --purge -y。我把--purge强制带上是为了让那些被自动安装的依赖也在卸载时一并清掉配置,这和默认的 autoremove 会有区别。此时系统里"已安装"状态的 MySQL 包应该已经清零了。
3.2 清理PPA与自定义软件源:防止apt update把MySQL带回来
这是最容易被忽略的一步。很多人安装 MySQL 时不是用的 Ubuntu 默认软件源,而是手动添加了 MySQL 官方提供的 apt 源或者某个第三方 PPA。卸载包的时候,包是删了,但你加的这个软件源还在系统里。
后果是什么?下次执行apt update时,系统依然会去这个源拉取索引;再往后你一旦装别的依赖,apt 可能因为源里的包版本信息不一致而报错。更麻烦的是,如果有人误以为"装一下就好",重新执行安装命令,它可能拉回一个和你预期完全不同的版本。
先检查一下系统里有没有残留的源配置:
grep -rni mysql /etc/apt/sources.list /etc/apt/sources.list.d/如果有输出,说明确实添加过。看到形如deb https://xxx/mysql/apt ...的行,直接把这个行注释掉或删除;如果是在sources.list.d下的独立文件,最干净的方式是把对应文件删掉:
sudo rm /etc/apt/sources.list.d/mysql.list删完之后执行sudo apt update刷新软件源索引,确认没有报错。
这里额外提醒一句:如果你是通过下载 tar.gz 包手动解压安装的 MySQL,apt层面根本感知不到它,上述所有 apt 命令都无效。手动安装的清理方式是反向操作——停进程、删解压目录、清理 PATH 或软链接、删数据目录。因为情况各异,这里不展开,但如果你确实属于这类,请先确认自己的安装方式,别照着 apt 教程白忙一场。
3.3 收尾:处理rc残留与apt缓存
purge 和 autoremove 之后,再用dpkg -l | grep mysql看一眼。此时可能出现rc状态的行。rc意味着包本体已经移除,但 dpkg 的配置文件记录还在,这会干扰后续重装。处理方式:
dpkg -l | awk '$1=="rc" && $2 ~ /mysql/ {print $2}' | xargs sudo dpkg --purge同样是先筛选再批量处理,安全且高效。最后顺手清一下 apt 缓存,释放几个G也不算亏:
sudo apt autoclean sudo apt cleanautoclean只清理已下载的过时安装包,clean会把/var/cache/apt/archives下的所有安装包缓存清空,按需选用即可。
4. 档案级清理:数据目录、日志、配置和systemd的残留
到了这一步,包层面的卸载已经完成,但还不能算"彻底卸载"。数据库和配置文件还在磁盘上躺着,systemd 里可能还残留着服务单元记录,系统中还挂着一个mysql用户。这一节就是把这些"档案"逐一处理掉。
4.1 手动删除数据与日志目录:动这一刀前请三思
包管理器不会碰数据目录,需要手动执行。MySQL 在 Ubuntu 上常见的目录包括:
sudo rm -rf /var/lib/mysql sudo rm -rf /var/lib/mysql-files sudo rm -rf /var/lib/mysql-keyring sudo rm -rf /etc/mysql sudo rm -rf /var/log/mysql sudo rm -rf /var/log/mysql.* sudo rm -rf /run/mysqld简单说明每个目录的用途:/var/lib/mysql是核心数据目录,所有库和表都在这里,mysql-files和mysql-keyring是 MySQL 8.0 里用来放导入导出临时文件和加密密钥的目录;/etc/mysql是所有配置文件的根目录;/var/log/mysql不用多说;/run/mysqld是运行时生成的 socket 和 pid 文件目录,卸载后一般也不会主动清。
这一刀下去数据就彻底没了,所以执行前再确认一遍第 2 节里的备份是否已经做好。一个常规检查动作是:
ls -ld /var/lib/mysql.bak 2>/dev/null && echo "备份尚在" || echo "没有备份,请确认数据是否可放弃"看到备份确认存在,再继续删除。
4.2 清理系统用户、systemd单元和AppArmor配置
安装 MySQL 时系统会创建一个独立的mysql用户,用来运行 mysqld 服务,避免以 root 权限跑数据库。卸载包时 apt 有时会顺手删掉它,有时不会。检查一下:
id mysql如果有输出,说明用户还在。想要彻底清理,执行:
sudo userdel mysql sudo groupdel mysql 2>/dev/null不过这里有一个取舍:如果你接下来还要重新安装 MySQL,我是不推荐删除 mysql 用户的。重装时包管理器会尝试重新创建同名用户,遇到残留用户可能出现 UID 冲突或权限异常,反而给自己添堵。只有在确认长期不装、或者想把系统恢复到"从未装过 MySQL"的状态时,才建议删掉。
systemd 方面,正常 purge 后服务单元文件会被移除,但个别情况会有残留或手动添加的 unit 文件。验证方式:
systemctl list-unit-files | grep mysql如果没有输出就说明干净。如果有残留文件,先删掉再重新加载:
sudo rm -f /etc/systemd/system/mysql.service sudo rm -f /etc/systemd/system/mysqld.service sudo systemctl daemon-reload sudo systemctl reset-failed另外 Ubuntu 上 MySQL 还带一个 AppArmor 配置文件,位于/etc/apparmor.d/usr.sbin.mysqld,它用来限制 mysqld 的文件访问权限。purge 时通常会被清理,如果还在就删掉:
sudo rm -f /etc/apparmor.d/usr.sbin.mysqld sudo systemctl restart apparmor4.3 检查dpkg信息目录是否还有MySQL的尾巴
最后一个容易被忽视的地方是 dpkg 的信息目录。正常情况下包被 purge 后,这里对应的条目会消失,但如果之前出现过rc状态或者某些异常中断,可能还有残留文件:
ls /var/lib/dpkg/info | grep mysql有输出的话,把这些mysql*开头的文件移走或删除:
sudo rm -f /var/lib/dpkg/info/mysql*这个操作比较底层,一般场景用不到,但如果重装时报"package is in a very bad inconsistent state"之类的错误,问题十有八九出在这里。
5. 卸载后的体检:五条命令确认MySQL彻底离场
清理动作做完,你以为完事了?还差最后一步:验证。卸载是否彻底,不看当时敲了多少命令,而是看最终系统状态。我习惯用五条命令从不同维度确认。
5.1 进程、端口、命令、目录、服务逐项核实
把这五条命令挨个执行一遍,结果应该和你预想的一致:
which mysql没有输出说明命令行工具已经移除。
dpkg -l | grep -i mysql没有输出说明包层面彻底干净,连rc残留都没有。
ss -lntp | grep 3306没有输出说明 3306 端口没有被任何进程占用。
ps -ef | grep -i mysql | grep -v grep没有输出说明没有残留进程。
systemctl status mysql正常输出是Unit mysql.service could not be found,说明服务单元不存在了。
再检查核心目录是否消失:
ls -ld /var/lib/mysql /etc/mysql /var/log/mysql 2>/dev/null没有输出说明这些目录已被删除。
5.2 全盘扫描与重装前的最后检查
五条命令覆盖了常规维度,但如果你想做到"掘地三尺"的干净,可以跑一次全盘扫描:
find / -iname '*mysql*' -not -path '/proc/*' -not -path '/sys/*' 2>/dev/null这条命令会把系统中所有文件名带 mysql 的文件和目录列出来。正常情况输出应该很少,可能只剩我们不想动的备份文件(比如/var/lib/mysql.bak)或者无关的应用程序数据。如果扫出大量/usr/share/mysql、/usr/lib/mysql之类的结果,说明还有残留,需要回去检查是不是有漏删的包。
做完这步,你的 Ubuntu 系统就是"从未安装过 MySQL"的状态了。此时如果你想重新装一个干净的全新实例,直接sudo apt update然后sudo apt install mysql-server即可,不会有旧配置干扰,也不会再问你要不要保留旧数据。
6. 从卸载看运维:几个高频翻车场景与一条保命建议
这一节聊点实战中经常出现的问题。MySQL 卸载这个操作本身不复杂,三分钟能敲完,但翻车场景花样百出,我挑三个最典型的复盘给你,顺便纠正一个常见误区。
6.1 三个翻车场景复盘
第一个场景:purge 之后 mysqld 进程还在跑。有人绕过systemctl stop直接删包,结果发现进程没被杀掉,3306 端口还被占用。原因是包管理器移除的是磁盘上的二进制文件,已经在内存里运行的进程不会立刻结束,端口自然也不会释放。解决办法是先查进程再手动终止:
ps -ef | grep mysqld | grep -v grep找到 PID 后kill它,必要时用kill -9。但更推荐的做法还是像我前面写的:purge 之前先把服务停干净,就不会有这种烂摊子。
第二个场景:数据目录顺手被删,备份在别处找不到。你可能会想,教程里都写了/var/lib/mysql要手动删,那删之前看一眼备份怎么了?但实际中太多人是在执行rm -rf之后才突然想起"刚才那个 SQL 文件好像存在另一个机器上没拷贝过来"。我的建议是把备份动作写进整个流程的第一步,并且备份完先验证文件大小和内容可读性,确认无误再开始动刀。备份不是"做了就行",而是要"确认能用"。
第三个场景:重装时遇到 package in a bad state 报错。多半是rc残留没有清干净,dpkg 数据库里还记录着旧包状态。按第 3.3 节的方式把rc包全部 purge 掉,然后清空 dpkg 信息目录中的 mysql 残留,重装就不会再被旧状态卡住。
6.2 关于"忘记root密码就重装"的重新思考
最后我想纠正一个很多新手根深蒂固的习惯:MySQL 密码忘了,第一反应就是卸载重装。这是个代价最高的方案,而且大概率是白折腾。
Ubuntu 通过 apt 安装的 MySQL,默认情况下 root 账户使用auth_socket认证插件,也就是说系统 root 用户可以直接通过 socket 登录数据库,根本不需要密码。如果你忘了密码,先试这一条:
sudo mysql如果直接进了 MySQL 命令行,说明你压根不需要重装,在库内执行 ALTER USER 把密码改掉就行。哪怕确实设置过密码且忘了,也完全可以通过临时加skip-grant-tables参数的方式重置,这在网上有大量成熟方案,几分钟就能解决。卸载重装反而是最重、最容易出意外的操作。
我自己现在处理任何一台服务器上的 MySQL,动手之前必做三件事:看一眼systemctl status mysql、列出dpkg -l | grep mysql、检查/var/lib/mysql的占用和备份状态。这三条命令加起来不到二十秒,但它能让你在十分钟后想反悔时,还留有退路。数据库这个东西,删之前再怎么谨慎都不为过。