数据库卸载残留致端口占用?从端口反查和自启动清理彻底解决

📅 发布时间:2026/10/21 6:20:18
数据库卸载残留致端口占用?从端口反查和自启动清理彻底解决
最近处理了一台服务器上的端口占用问题。机器上卸载了某国产数据库的安全版 V4.5.10卸载程序和界面都提示完成结果重启后发现 6666 端口依然被监听进程名叫 db_ha。说白了就是卸载工具没把高可用组件的 agent 进程一起带走。这个问题在实施现场很典型尤其是带 HA 组件的数据库产品卸载不干净比安装失败更让人头疼。因为端口被占用后续要部署新实例、或者让其他服务使用这个端口都会卡在这一步而且来源还不容易一下子看出来。这篇内容主要讲清楚三件事怎么从端口反查残留进程为什么卸载之后 db_ha 还在跑以及如何把进程、自启动项、目录文件一次清干净。如果你是做数据库运维、实施交付、或者日常维护服务器的这篇可以直接当成一套排查模板用。即使你手上跑的并不是这款产品只要思路不变同样能迁移到其他中间件、其他带独立 agent 的服务上。文章里面有实际命令、有操作顺序、也有我踩过坑以后的复盘照着做基本能解决九成以上的“卸载残留导致端口占用”问题。1. 先描述现场卸载后留下了什么痕迹1.1 最直观的异常端口还在监听数据库卸载完成后第一件应该做的事就是检查端口是否释放。在这台服务器上执行ss -lntp | grep 6666的时候输出显示有什么进程在0.0.0.0:6666上监听PID 是一个固定的数字进程名就是db_ha。这台机器主要用途是跑数据库实例6666 是 HA 组件用于节点状态同步的专用端口正常情况下卸载后这个端口应该变成空闲状态。出现这种情况说明内核里还驻留有该组件的活动进程并且正在等待新的连接或定期发送心跳包。除了端口被占用这可能还会带来一些更隐蔽的副作用比如该进程持续占用少量 CPU网络连接显示为 ESTABLISHED 状态或者 LISTEN 状态长期不消退甚至在进程的日志文件里还在不断写入报错信息。不过这些变化都不如端口占用来得直接所以最先被发现的通常都是“新装的软件启动时报端口被占用”顺藤摸瓜才找到这个残留进程。1.2 周边痕迹自启动项、安装目录、日志文件继续排查会发现不止端口一个问题。/etc/systemd/system/下面还有一个以 db_ha 命名的 service 文件/opt/某数据库/ha目录完整存在日志目录/var/log/某数据库/ha里还在产生新的日志。再用rpm -qa | grep 某数据库查询已经查不到对应包名了说明软件主程序确实被卸载掉但辅助进程和配置文件并没有被清理干净。这种残留的危害主要体现在几个地方。如果后续要重新安装同版本或升级到新版本安装程序检测端口占用时直接报错整个部署流程被卡在中间。如果服务器上还有其他高可用组件用了同一个端口还会出现互相抢端口、监听失败的情况。残留的 service 文件更麻烦它会让系统在每次重启时自动把 agent 拉起来这就是为什么机器一重启端口又出现的直接原因。所以处理这类问题绝对不能只杀进程就算完事必须把自启动项和文件系统里的痕迹一起清理掉。2. 排查过程从端口反查到根因2.1 第一步永远是从端口反查进程处理端口占用我习惯的第一步不是去翻日志而是直接用命令看谁在监听端口。三个命令各有适用场景netstat -lnpt是老牌工具绝大多数系统都带ss -lntp是性能更好的替代品在连接数很多的服务器上优势明显lsof -i:6666最直观但需要额外安装 lsof 工具。实际排查时我一般直接输入ss -lntp | grep 6666输出里会包含协议、监听地址、端口和进程 PID。注意-t表示只看 TCP-l表示 listening 状态-n表示不做 DNS 反解-p必须配合 root 权限才能看到进程名否则只会显示一个 PID缺少这个字段后面就很难定位。如果服务器上没有 ss用netstat -lnpt效果一样。这里我推荐优先用 ss因为它在网络连接数量多的时候明显更快不会出现长时间卡住的情况。2.2 确认进程身份和所属安装包知道 PID 后要先确认这个进程到底是什么从哪里拉起来的。执行下面几条命令ps -fp 1234 readlink /proc/1234/exe cat /proc/1234/cmdline | tr \0 第一眼看到/opt/某数据库/ha/bin/db_ha就能判断是数据库的高可用组件还在运行。/proc/PID/exe符号链接会指向可执行文件的真实路径这一步非常关键。如果二进制文件已经被删除这条路径末尾可能会显示(deleted)但进程依然在内存里运行这种情况更需要强制终止因为普通停止信号可能已经无法让程序完整退出。/proc/PID/cmdline里的参数可以看到启动时带了哪些配置比如指定了端口、指定了配置文件路径这些信息对顺藤摸瓜清理残余文件很有帮助。2.3 找出它的父进程与守护关系单看 PID 不够还要弄清楚是谁启动了它。执行ps -o ppid -p 1234可以拿到父进程 PID再用pstree -p 1234看整棵进程树。如果父进程是 systemd说明存在对应的 service 文件要去 systemd 里处理如果父进程是一个监控脚本那就得找到脚本来源比如/etc/rc.local或某个计划任务把脚本本身也停掉否则前面刚杀掉的进程很快又会被拉起。这一步直接决定了后面清理自启动项的方向。我见过不少人卡在“杀了又活、活了又杀”的循环里其实就是没查父进程。比如这次处理的机器上db_ha 的父进程就是 systemd而 systemd 之所以会拉起它就是因为/etc/systemd/system/db_ha.service还躺在那里。如果不删掉这个 service 文件就算你杀了进程一百遍重启后它还是会原地复活。2.4 检查卸载后的包管理记录还可以用系统包管理器确认卸载状态执行rpm -qa | grep -i 某数据库 dpkg -l | grep -i 某数据库在这台机器上主程序包已经查询不到但 db_ha 的进程还在这说明卸载时主程序被移除而 HA 组件的卸载逻辑没有执行完整。还有一种更隐蔽的情况安装方式不是标准包安装而是直接解压到目录里的免安装模式。如果是免安装模式包管理器里永远查不到记录排查时依赖进程路径和自启动项反而更可靠。所以包管理器查询可以作为参考但不能作为唯一判断依据最终还是要回到readlink /proc/PID/exe和systemctl list-unit-files这两个方向上来。3. 解析根因为什么 db_ha 进程会被残留3.1 高可用组件 db_ha 的工作机制db_ha 本质上是数据库高可用体系里的一个代理进程负责节点间通信、健康检查、故障状态上报。它并不像数据库主进程那样跟随登录会话运行而是通常由系统服务管理器托管独立驻留后台。设计上它必须一直活着才能在数据库主进程异常退出时执行故障切换。所以它使用的端口比如 6666通常是固定的通信端口集群内其他节点通过这个端口探测本节点的存活状态。这个机制平时很可靠但到了卸载场景就变成麻烦。用户以为卸载数据库就是“停止服务、删除文件”但对高可用组件来说agent 进程是独立运行的生命周期它并不会因为主数据库程序被删掉就自觉退出。它甚至会认为数据库主进程的消失是一种故障状态于是反复尝试重连对端节点进一步延长了自身存活时间。3.2 卸载脚本只管了数据库主进程大多数数据库安装包的卸载脚本会按照“停止服务、删除文件、清理配置”的顺序执行但停止服务这一段通常只处理主实例没有遍历所有辅助进程。尤其高可用组件会由 HA agent 主动拉起卸载脚本并不知道还有独立运行的后台进程存在。结果就是主程序卸载完成agent 进程还留在内存里继续占用端口。在实际实施中问题还会被复杂化。如果环境里配置了两个节点卸载了一台节点上的数据库另一台节点的 HA 进程还在正常发送心跳包残留的 db_ha 接收到对端心跳后会认为当前节点只是暂时异常于是继续保持监听状态等待恢复这就导致进程异常顽固普通的 kill 信号可能根本不会让它干净退出。3.3 自启动机制被遗漏即便手动杀了残留 agent如果不处理 service 文件和 rc 脚本系统一重启 agent 会被再次拉起。这是“重启后端口又出现”的高频原因。systemd 的 service 单元是常见宿主卸载程序没有删除/etc/systemd/system/下对应文件也没有执行systemctl disable。另一种情况是/etc/rc.local里被人手动加过启动命令卸载脚本不可能知道这份改动。还有一种比较隐蔽的是临时写入的/etc/init.d/脚本。某些安装包在安装时会复制启动脚本到 init.d 目录卸载过程只删除了主目录却没有移除 init 脚本。所以清理必须走到自启动层面不能只停留在进程层面。我处理这个案例时最花时间的反而不是杀进程而是把所有可能调用 db_ha 的启动入口全部找出来。3.4 为什么是 6666 端口6666 并非随机出现它往往是 HA agent 的默认配置端口也可能由配置文件里的 port 参数指定。实际部署中如果安装了两个高可用组件、或者历史版本遗留了配置就可能造成端口互相占用。尤其在安全版软件里高可用组件的端口通常会被固定配置不会跟随主数据库端口一起变化。因此清理后不仅要确认 6666 不再被监听还要去配置文件里查看是否还有引用这个端口的参数。如果旧配置文件没有被删除干净后续安装新版时安装程序会读取到这个旧参数然后再次把 6666 配置给新的 HA 组件最终导致新旧进程冲突。这也是为什么我后面会强调清理残留必须连配置目录一起处理不能只看进程和端口。4. 完整清理实操从进程、自启动到残留文件4.1 先停服务再杀进程别急着 kill -9我看到有人一处理残留进程就直接kill -9这样确实能把进程干掉但并不可取。正确的顺序是先看有没有可控的停止方式。如果存在 systemd 服务先执行systemctl stop db_ha systemctl disable db_ha如果没有服务文件就先用kill PID发送 SIGTERM给进程一个做退出清理的时间。只有等待几秒后进程仍不退出才用kill -9 PID。强制终止的代价是可能丢失一些状态信息在集群场景下还可能让其他节点误判本节点故障触发不必要的切换动作所以能正常停就先正常停。实际操作中还有个细节执行kill之前最好先记录当前 PID 和可执行文件路径因为一旦进程被杀掉/proc/PID目录就没了再想通过它找回安装路径就要靠历史命令记录或者全局搜索麻烦得多。4.2 清掉自启动项进程杀掉只是第一步必须处理自启动。先查 systemd 单元systemctl list-unit-files | grep -i db_ha找到文件后执行systemctl disable db_ha rm -f /etc/systemd/system/db_ha.service systemctl daemon-reload这里disable和删除文件都要做因为光 disable 只是去掉开机自启service 文件还会留在系统里后续可能被其他工具扫描识别到认为软件还在安装状态。删除 service 文件后一定要执行systemctl daemon-reload让 systemd 重新读取配置否则已删除的 unit 状态还会被缓存。接着检查/etc/rc.local、/etc/init.d/grep -rn db_ha /etc/rc.local /etc/init.d/ 2/dev/null如果有匹配行在确认不是系统需要的内容后直接删除对应行。也可以用sed批量处理但手动打开编辑更稳妥因为rc.local里可能同时存在多条启动命令删错一行会影响其他服务的启动。4.3 清理文件系统残留进程和自启动项清理完后开始删除目录和文件。先用readlink /proc/原来的PID/exe已经定位到路径现在执行rm -rf /opt/某数据库/ha rm -rf /var/log/某数据库/ha /etc/某数据库/ha /var/opt/某数据库/ha这一步容易漏的是日志和配置目录。有些数据库版本会把 HA 配置放在/etc/某数据库/下面有些放在安装目录的 conf 子目录删除前最好用find / -iname *db_ha* 2/dev/null全局搜索一遍把零散文件清干净。清理前务必备份配置和数据目录尤其当服务器上还有生产数据时宁多留一份也不要急着删。删除前还要再确认一下是否还有其他进程正在使用这些目录里的文件。保险起见执行一遍lsof D /opt/某数据库确认没有 open 的文件句柄后再删除。如果一个文件还在被进程使用但目录已经被删系统日志里会持续出现 “text file busy” 之类的报错增加不必要的操作噪音。4.4 清理用户和权限某些安装包会创建专用系统用户比如dbha用户用来跑 HA 进程。此时要检查用户是否还有其他关联服务在运行。确认无关联后删除userdel -r dbha执行前可以用ps -u dbha查看该用户下是否还有活动进程。除了用户本身还要检查 sudo 配置或安全策略里是否有针对这个用户的残留规则如果存在就一并清理。这类清理虽然不影响端口占用但关系到系统安全基线不能忽略。4.5 验证端口释放全部操作完成后先验证端口释放情况ss -lntp | grep 6666这里应该没有任何输出。最好再重启一次系统然后执行同样的检查并观察服务器启动后的进程列表确认 db_ha 没有被任何开机项拉起来。这一步可以把自启动清理遗漏的问题提前暴露出来避免过了几天突然又发现端口被占用。我自己的习惯是重启后还会再跑一次systemctl list-unit-files | grep -i db_ha确认服务文件已经彻底不存在。这样才算真正收工而不是当下看起来没问题就算完成。5. 防患于未然卸载前、安装时应该注意什么5.1 卸载前的标准动作软件卸载前先记录现场。执行ss -lntp看所有监听端口执行ps -ef | grep -E 某数据库|db_ha看相关进程。把服务停止后再执行卸载程序不要卸载工具一提示“完成”就走人。卸载完成后立刻核对之前的记录确认所有端口都已释放所有进程都已消失。这个流程很简单但每次实施都能避免很大一部分残留问题。为什么很多人会遇到残留问题因为卸载过程里最容易被跳过的就是“先停止高可用组件”。数据库的 HA agent 一旦处于运行状态它的配置和日志文件会被持续占用卸载程序在删除文件时可能跳过正在使用的文件阶段性地造成残留。先停服务再删文件顺序对了残留概率会大幅降低。5.2 安装时规划好目录和端口实施经验里最容易被忽略的是安装时的规划。不要在服务器上直接使用默认端口、默认管理员用户、默认安装路径。把安装路径、数据目录、日志目录、监听端口、集群通信端口记录到服务器的归档文档里。将来卸载或者排障时这些信息能让排查时间缩短一半。如果同机需要部署多个实例端口规划更要避免交叉像 6666 这种常见的 HA 端口很容易重复。这里我特别想说一下文档化的重要性。很多时候排障难不是问题本身多复杂而是根本不知道当初装了哪些组件、用了哪些端口。每次安装完顺手写一行记录后续能省下大把时间。我自己吃过不少亏现在每装一套环境都会把端口、路径、用户列成一张表放在/root下面后面处理问题直接翻表效率提升非常明显。5.3 用脚本固化状态确认最后建议把确认工作做成一个简单的检查脚本每次卸载后执行一次。脚本核心是三段检查echo 监听端口 ss -lntp | grep -E 6666|5432 echo 相关进程 ps -ef | grep -E 某数据库|db_ha | grep -v grep echo 自启动项 systemctl list-unit-files | grep -E 某数据库|db_ha脚本可以持续维护在不同项目间复用。遇到其他软件残留只需要替换关键字。之所以把确认流程脚本化是因为人总有疲惫的时候尤其在割接窗口或者凌晨变更的场景下手工检查非常容易漏。脚本输出固定格式一眼就能看出有没有异常比靠记忆离线检查可靠得多。6. 常见问题速查6.1 端口被占用但 ss 里看不到进程名如果你执行ss -lntp时没有得到 root 权限进程名那一列会显示不出来。解决办法是sudo ss -lntp | grep 6666。如果加 sudo 后仍然看不到对应 PID但端口确实是在监听那可能是 selinux 或容器网络命名空间导致。容器环境下宿主机看到的端口空间和容器内部不一致排查时要注意当前 shell 所在的 namespace不要拿宿主机命令去查容器内进程。还有一种情况是端口处于非 TCP LISTEN 状态比如 UDP 监听或者 TIME_WAIT 状态。ss -lntp只显示 TCP 的 LISTEN 状态如果你要找的是 UDP就要换成ss -lnup。我在处理一些中间件服务时经常会遇到这个误区明明端口被占用却因为只查了 TCP 就误判为没占用。6.2 kill -9 后过一会儿进程又出现这种现象基本可以断定有守护进程在拉起。重点排查父进程和计划任务pstree -a -p PID、crontab -l、/etc/cron.d/、/etc/rc.local。另外还要看有没有监控 agent 在周期探测并复活服务。在数据库场景里HA 组件通常有自我恢复机制如果配置文件里还指定了另一节点的地址agent 被终止后可能由另一节点的心跳触发重新拉起这时候光杀本机进程是不够的还要把配置文件里的连接参数删掉或修改。我还遇到过一种诡异情况某个定时任务每分钟检查一次指定端口发现端口没监听就执行启动脚本。解决方案是先把 cron 里的相关行注释掉再杀进程最后再处理配置文件。如果顺序反了你杀完进程之后很快就会被 cron 拉起来给人感觉怎么都杀不死。6.3 服务已 disable 但重启后端口仍被占用disable 只取消了当前 unit 的开机自启但如果服务文件被其他目录或某个 target 引用仍然可能在启动阶段被调用。用systemctl is-enabled db_ha查看状态用find /etc/systemd -name *db_ha*找残留文件。还可以查看启动日志定位是谁拉起的journalctl -b -u db_ha --no-pager。通过日志能看到进程是在哪个启动阶段被拉起的顺着时间线去查通常能找到某个 target 或某个服务的依赖关系。这类问题的核心在于systemd 的 enable 状态和服务文件是否被其他单元依赖是两回事。有时候你明明 disable 了一个服务但另一个服务的Requires或Wants里引用了它开机时依然会被拉起。所以清理自启动项时要多想一步不只查目标服务本身还要查谁依赖了它。我把常见问题整理成一张表方便你对照排查现象可能原因解决方向端口被占用但看不到进程名权限不足或网络命名空间不同用 root 权限重新执行 sskill -9 后进程又出现存在守护进程或定时任务排查父进程、cron、rc.localdisable 后重启又出现service 文件被其他 target 依赖删除残留 unit查看启动日志我自己在多次实施里遇到过类似问题后来每次卸载带高可用组件的软件都会事前把端口、进程、服务文件列成三行清单卸载后再逐项核对。这次服务器上残留的 db_ha 就是个典型主程序卸载成功但 HA agent 还占着 6666 端口。清理完自启动项后重启验证才真正算收工。这个处理方法不只适用于这一款数据库产品其他带 agent、带守护进程的中间件软件遇到卸载残留时都可以按同样的思路排查抓住端口反查和自启动清理这两个关键点问题基本就能解决。