
做開發(fā)這些年MySQL裝過不少次卸載的次數(shù)也著實(shí)不少。別覺得卸載是件小事——我見過太多人因?yàn)樾遁d不干凈導(dǎo)致重裝后端口沖突、密碼失效、數(shù)據(jù)目錄殘留一堆舊文件折騰一整天。尤其是Ubuntu系統(tǒng)apt的包管理和配置文件分散在好幾個(gè)目錄光靠一句sudo apt remove mysql-server根本清不干凈。這篇從完整卸載MySQL的實(shí)操流程講起把每一步背后的邏輯和踩過的坑一起說清楚照著走基本能一次清干凈。我對接得最多的場景無非這三種一是開發(fā)環(huán)境用亂了各種配置和臟數(shù)據(jù)堆在一起想推倒重來二是裝MySQL時(shí)圖省事選了系統(tǒng)默認(rèn)版本后來發(fā)現(xiàn)跟項(xiàng)目不匹配需要換版本三是轉(zhuǎn)向MariaDB或PostgreSQL徹底把MySQL移出系統(tǒng)。不管哪種只要涉及數(shù)據(jù)備份都該排在第一位。這不是保守是經(jīng)驗(yàn)。1. 卸載前先想清楚備份這件事不能跳過1.1 邏輯備份與配置文件備份很多人覺得我只是想卸個(gè)數(shù)據(jù)庫重裝就好了但重裝并不代表數(shù)據(jù)還在。MySQL的數(shù)據(jù)文件默認(rèn)存放在/var/lib/mysql這個(gè)目錄在apt purge時(shí)通常不會(huì)被自動(dòng)清除但你在深度清理殘留時(shí)會(huì)手動(dòng)刪掉它。一旦刪除加上沒有備份那基本就是終局。我現(xiàn)實(shí)中就見過開發(fā)者在卸載時(shí)順手執(zhí)行了rm -rf /var/lib/mysql結(jié)果想起來庫里有一份沒導(dǎo)出的報(bào)表腳本整個(gè)人都懵了。先備份數(shù)據(jù)邏輯備份用mysqldump最穩(wěn)妥mysqldump -u root -p --all-databases --single-transaction --routines --triggers all_databases.sql參數(shù)含義拆開講一下--all-databases導(dǎo)出所有數(shù)據(jù)庫包括mysql系統(tǒng)庫和information_schema這類元數(shù)據(jù)表。--single-transaction針對InnoDB的一致性快照導(dǎo)出全程不加表鎖在線執(zhí)行不影響業(yè)務(wù)。如果你用的是MyISAM表居多這個(gè)參數(shù)意義不大但不會(huì)報(bào)錯(cuò)。--routines和--triggers存儲(chǔ)過程、函數(shù)和觸發(fā)器。這兩個(gè)選項(xiàng)特別容易被漏掉恢復(fù)時(shí)才發(fā)現(xiàn)自定義函數(shù)全沒了而很多業(yè)務(wù)邏輯是靠存儲(chǔ)過程實(shí)現(xiàn)的。如果只備份業(yè)務(wù)庫把--all-databases換成具體庫名例如--databases order_db user_db。注意庫名之間用空格分隔。配置文件也別忘了。全局配置通常在/etc/mysql/個(gè)人配置散落在~/.my.cnf。如果你改過字符集、隔離級別、慢查詢閾值或者binlog保留天數(shù)卸載前先打包拷貝一份到工作目錄。軟件可以重裝但一堆參數(shù)重寫起來特別耗時(shí)拷貝兩分鐘的事真遇上需要對比舊參數(shù)的時(shí)候就知道值了。1.2 環(huán)境信息存檔執(zhí)行備份命令之后順手記錄當(dāng)前版本和服務(wù)狀態(tài)mysql --version systemctl status mysql這兩條輸出在卸載前后做對照非常有用。我記過幾次mysql版本號(hào)后來在排查重裝后配置不生效問題時(shí)靠版本差異對比定位到是8.0和5.7的認(rèn)證插件不兼容導(dǎo)致的。把版本和服務(wù)名寫進(jìn)筆記里比直接開干更穩(wěn)妥。2. 停服務(wù)與確認(rèn)運(yùn)行狀態(tài)2.1 干凈停服的常規(guī)做法卸載第一步不是刪包而是先讓MySQL服務(wù)停下來。很多人直接執(zhí)行apt remove系統(tǒng)在卸載腳本里也會(huì)嘗試停服務(wù)但服務(wù)進(jìn)程持有的文件句柄和socket文件在運(yùn)行狀態(tài)下處理起來容易留尾巴。我的習(xí)慣是先手動(dòng)停再進(jìn)入刪除流程。Ubuntu上停止MySQL常規(guī)命令sudo systemctl stop mysql服務(wù)名不一定是mysql。有的發(fā)行版叫mysqld有的帶版本后綴。先查一下再操作systemctl list-unit-files | grep -i mysql這個(gè)輸出能看到有哪些unit文件存在以及當(dāng)前自啟狀態(tài)。然后執(zhí)行systemctl status mysql只要出現(xiàn)inactive或dead服務(wù)就算停干凈了。如果停在activating轉(zhuǎn)圈也不用慌可能是長事務(wù)或慢查詢還沒結(jié)束等一會(huì)兒再看。2.2 服務(wù)停不下來時(shí)的處理實(shí)際工作中偶爾會(huì)遇到服務(wù)停不下來的情況尤其是系統(tǒng)里有監(jiān)控腳本反復(fù)拉起服務(wù)或者客戶端連接池還在不斷建連。systemd停服務(wù)后連接全部斷掉服務(wù)才會(huì)真正退出。這時(shí)候用進(jìn)程視角檢查ps -ef | grep mysqld | grep -v grep確認(rèn)是殘留的守護(hù)進(jìn)程還是連接導(dǎo)致的再?zèng)Q定下一步。如果確定要強(qiáng)制結(jié)束可以用kill命令。但這是應(yīng)急手段正常路徑不推薦。我處理這類問題時(shí)的原則是先看誰在占用再考慮強(qiáng)殺。一個(gè)找不到占用者的進(jìn)程強(qiáng)殺之后可能會(huì)讓數(shù)據(jù)文件處于不一致狀態(tài)后續(xù)清理時(shí)會(huì)多出不少麻煩。停服務(wù)之后我會(huì)順手關(guān)掉開機(jī)自啟sudo systemctl disable mysql這一步看似冗余實(shí)際很關(guān)鍵。我有一次在卸載過程中因?yàn)槟撤N原因服務(wù)被延遲啟動(dòng)的定時(shí)任務(wù)重新拉起來了導(dǎo)致purge過程中提示文件被占用。先把自啟關(guān)掉能少踩這個(gè)坑。3. 用apt完成軟件包的一層層拆除3.1 remove和purge到底差在哪這里先明確一個(gè)概念apt remove和apt purge是兩回事。remove只刪除可執(zhí)行文件、庫文件、文檔這些內(nèi)容保留配置文件。也就是說執(zhí)行remove之后/etc/mysql/my.cnf還在舊密碼、舊socket配置一個(gè)不落。下次重裝時(shí)如果配置變了那就會(huì)彈出一堆讓你確認(rèn)舊配置的界面甚至直接起不來。purge則會(huì)把配置文件一起清掉還會(huì)觸發(fā)postrm腳本把安裝時(shí)生成的臨時(shí)文件一并處理。我做卸載時(shí)一律用purge目的就是不想在下次重裝時(shí)夾帶一堆舊配置。3.2 核心包卸載命令與模擬演練卸載MySQL的三件套命令寫全sudo apt purge mysql-server mysql-client mysql-commonMySQL的包依賴關(guān)系有點(diǎn)像俄羅斯套娃。mysql-server是個(gè)meta包真正干活的是mysql-server-8.0這種帶版本號(hào)的包。你purge mysql-serverapt會(huì)自動(dòng)把依賴它的版本包也標(biāo)記為待刪除不需要手動(dòng)逐個(gè)列名。不過強(qiáng)烈建議先跑一遍模擬看清楚刪除范圍sudo apt --simulate purge mysql-server mysql-client mysql-common模擬輸出會(huì)告訴你哪些包將被刪除、哪些自動(dòng)安裝的依賴會(huì)變成孤兒包。重點(diǎn)看下列軟件包將被刪除那一段確認(rèn)里面沒有誤傷其他項(xiàng)目依賴的包。比如你系統(tǒng)里有個(gè)應(yīng)用依賴libmysqlclient那purge時(shí)就要注意它會(huì)不會(huì)一起被拖下水。如果確認(rèn)無誤把--simulate參數(shù)去掉再執(zhí)行。執(zhí)行purge時(shí)Ubuntu有時(shí)會(huì)彈出debconf界面詢問是否刪除MySQL配置文件。這里選擇是或刪除即可。如果apt卡在這個(gè)界面多半是終端顯示問題用Tab鍵切選項(xiàng)后回車就行。3.3 依賴清理與緩存清理包卸載完成后我習(xí)慣補(bǔ)上兩步清理sudo apt autoremove sudo apt cleanautoremove會(huì)把因?yàn)殛P(guān)聯(lián)MySQL而自動(dòng)安裝、現(xiàn)在不再被任何包依賴的庫文件清理掉。這一步很多人漏掉系統(tǒng)里積了一堆無用的so文件時(shí)間久了磁盤占用自然就上去了。apt clean則是清空本地緩存的deb安裝包釋放/var/cache/apt/archives下的空間。如果你用的Ubuntu比較老可能見過sudo apt-get purge mysql*這種通配符寫法。當(dāng)前20.04之后的版本不建議用通配符直接列包名更安全。用過通配符的人應(yīng)該都經(jīng)歷過系統(tǒng)把名字匹配mysql的所有包全切掉包括一些不相關(guān)的歷史遺留包。另外一個(gè)容易踩的坑如果系統(tǒng)里同時(shí)裝過MariaDB或者手動(dòng)編譯過MySQL其他變種包名可能不完全一致。動(dòng)手前用一條dpkg搜索先摸清底細(xì)dpkg -l | grep -i -E mysql|mariadb看到列表里有哪些包再?zèng)Q定怎么卸載比盲目復(fù)制網(wǎng)上的命令靠譜得多。4. 清理殘留目錄、配置、日志一個(gè)都別放過4.1 核心殘留目錄清單與處理apt purge執(zhí)行完之后系統(tǒng)層面的數(shù)據(jù)庫目錄、日志、運(yùn)行時(shí)文件并不會(huì)自動(dòng)刪除。原因不難理解Debian系包管理策略認(rèn)為保留用戶數(shù)據(jù)是更安全的選擇。這也意味著如果你想徹底卸載這些目錄必須手動(dòng)處理不能抱著purge就完了的心態(tài)。我整理了一張清理對照表每次操作都會(huì)對照著走路徑內(nèi)容說明處理方式/var/lib/mysql數(shù)據(jù)庫實(shí)體文件、ibdata1、undolog、binlogsudo rm -rf /var/lib/mysql/etc/mysql全局配置my.cnf及各子配置sudo rm -rf /etc/mysql/var/log/mysql錯(cuò)誤日志、慢查詢?nèi)罩緎udo rm -rf /var/log/mysql/var/run/mysqldsocket文件、PID文件sudo rm -rf /var/run/mysqld想一次搞定直接用這一條sudo rm -rf /var/lib/mysql /etc/mysql /var/log/mysql /var/run/mysqld在使用這條命令之前務(wù)必再回頭確認(rèn)一次備份文件確實(shí)存在于本機(jī)以外的地方至少不在這個(gè)命令的刪除范圍內(nèi)。我在一臺(tái)服務(wù)器上處理過一個(gè)問題那位開發(fā)者的備份文件就放在/var/lib/mysql旁邊的目錄他理所當(dāng)然地以為不在刪除范圍內(nèi)結(jié)果腳本路徑寫錯(cuò)了把整個(gè)家目錄刪了。慘痛教訓(xùn)提一次記一次。刪除/var/lib/mysql時(shí)如果提示設(shè)備或資源忙說明有進(jìn)程還在占用目錄內(nèi)文件句柄。先用sudo lsof D /var/lib/mysql查看占用進(jìn)程處理完之后再執(zhí)行刪除。4.2 系統(tǒng)級殘留搜索與用戶清理目錄刪完后全盤再掃一遍find / -name *mysql* -type d 2/dev/null這個(gè)命令會(huì)有一定消耗多等幾秒正常。如果你只關(guān)心當(dāng)前用戶的配置文件直接把范圍縮小就行l(wèi)s -la ~ | grep -i my重點(diǎn)看~/.my.cnf這是MySQL的客戶端默認(rèn)配置文件里面可能殘留著舊連接的用戶名和端口。用戶目錄下一般不會(huì)有其他大文件殘留主要是socket文件出現(xiàn)在/tmp目錄的情況比較多看到了順手刪掉。還有一個(gè)容易被忽略的對象安裝時(shí)自動(dòng)創(chuàng)建的mysql系統(tǒng)用戶和用戶組。大部分情況下保留這個(gè)用戶不影響后續(xù)使用但如果你追求完全純凈可以在確認(rèn)沒有進(jìn)程占用后處理掉sudo deluser mysql這里提醒一句某些備份工具或管理軟件會(huì)檢測mysql用戶的存在用來判斷系統(tǒng)是否具備MySQL能力。如果打算后續(xù)換用其他數(shù)據(jù)庫保留這個(gè)用戶反而能少一些依賴探測上的麻煩。所以這個(gè)操作屬于可選我一般建議保留除非系統(tǒng)里確實(shí)不需要任何mysql相關(guān)的身份標(biāo)識(shí)。清理完這些再開一個(gè)新的終端窗口重新登錄一下讓shell的環(huán)境變量和二進(jìn)制路徑緩存刷新。實(shí)測中新終端里的檢查結(jié)果比舊終端更可靠。5. 驗(yàn)證卸載結(jié)果怎么確定真的干凈了5.1 狀態(tài)、文件、服務(wù)、端口四類驗(yàn)證驗(yàn)證階段是很多人偷懶的地方裝完瞥一眼沒報(bào)錯(cuò)就算完事。但卸載這活兒驗(yàn)證不到位重裝時(shí)分分鐘讓你頭疼。我把驗(yàn)證流程固定成四步缺一不可。第一步查軟件包狀態(tài)dpkg -l | grep mysql正常情況這條命令沒有任何輸出。如果輸出中出現(xiàn)rc狀態(tài)說明包的配置文件仍在殘留。rc兩個(gè)字符分別代表期望狀態(tài)是移除、實(shí)際狀態(tài)是配置文件殘留。處理方式是再執(zhí)行一遍sudo dpkg -P 包名第二步查可執(zhí)行文件which mysql mysql --version正常情況下兩條命令都會(huì)提示找不到。如果which還能定位到/usr/bin/mysql說明二進(jìn)制文件留在系統(tǒng)里要么是某個(gè)包沒清理干凈要么是當(dāng)初手動(dòng)安裝的跟apt無關(guān)。MySQL缺少時(shí)mysql這個(gè)命令不一定立刻消失因?yàn)閟hell有hash緩存。我都是新開終端再測避免舊終端的命令路徑引用干擾判斷。第三步查服務(wù)狀態(tài)systemctl status mysql正常情況輸出應(yīng)該是Unit mysql.service could not be found而不是inactive或dead。如果顯示后者說明服務(wù)配置文件還在packaging沒清完。第四步查端口監(jiān)聽ss -tlnp | grep 3306MySQL默認(rèn)監(jiān)聽3306端口。卸載干凈后這個(gè)端口應(yīng)該空出來。如果還有進(jìn)程在監(jiān)聽按下一節(jié)的排查思路處理。5.2 重裝驗(yàn)證的終極大法前面四步都通過后我還會(huì)用最后一招在一個(gè)新終端里直接重新安裝一遍sudo apt install mysql-server如果安裝過程順利、初始化正常、沒有報(bào)目錄已存在或socket文件沖突說明之前的清理是徹底的。這個(gè)方法在開發(fā)機(jī)上成本很低但在生產(chǎn)服務(wù)器上可能有點(diǎn)興師動(dòng)眾。對測試環(huán)境我每次都順手驗(yàn)證一遍實(shí)測下來很穩(wěn)。6. 常見問題與排查實(shí)錄6.1 卸載后3306端口仍被占用這是最高頻的問題。先定位占用進(jìn)程ss -tlnp | grep 3306常見原因是MySQL進(jìn)程沒完全退出。用ps -ef | grep mysqld確認(rèn)后處理。還有一種情況是socket文件殘留導(dǎo)致進(jìn)程反復(fù)嘗試?yán)饳z查/var/run/mysqld目錄刪除后重啟一次系統(tǒng)就好。排除MySQL自身后還有一種可能系統(tǒng)里其他應(yīng)用比如Redis占用了3306端口。這屬于業(yè)務(wù)層沖突不是卸載遺留換個(gè)端口或改配置即可。6.2 dpkg提示軟件包狀態(tài)異常卸載過程中如果中斷過或者被強(qiáng)制終止dpkg的數(shù)據(jù)庫可能停留在半安裝狀態(tài)。此時(shí)先嘗試修復(fù)sudo dpkg --configure -a如果依然報(bào)錯(cuò)需要強(qiáng)制移除異常狀態(tài)的包sudo dpkg --remove --force-remove-reinstreq 包名這條命令有破壞性執(zhí)行前務(wù)必備份/var/lib/dpkg/status文件值得用它來兜底。6.3 卸載時(shí)提示依賴關(guān)系錯(cuò)誤這種問題大多發(fā)生在手動(dòng)改過依賴或者用源碼方式裝過MySQL的情況下。apt會(huì)拒絕繼續(xù)刪除。處理方式是用apt的自動(dòng)修復(fù)sudo apt --fix-broken install先把殘缺的依賴補(bǔ)齊或移除等apt狀態(tài)恢復(fù)正常再執(zhí)行purge。如果fix-broken無法定位具體問題去/var/log/apt/term.log翻日志找到阻塞的包名針對性處理。我遇到過某個(gè)庫里混著舊版本libmysqlclient就是靠這份日志鎖定的問題。6.4 卡在debconf交互界面purge時(shí)Ubuntu有時(shí)會(huì)彈出一個(gè)藍(lán)色界面問是否刪除配置文件。如果終端顯示不支持或字體太小看起來就像卡死。解決辦法是用Tab鍵切換選項(xiàng)回車確認(rèn)。也可以用環(huán)境變量徹底禁用交互界面sudo DEBIAN_FRONTENDnoninteractive apt purge mysql-server mysql-client mysql-common非交互模式下這類界面不會(huì)彈出適合腳本化操作。不過要注意這種方式下如果安裝腳本需要確認(rèn)某些參數(shù)它會(huì)直接用默認(rèn)值可能不是你想要的結(jié)果。6.5 數(shù)據(jù)目錄無法刪除提示權(quán)限或設(shè)備或資源忙權(quán)限問題用sudo基本能解決。資源忙則說明有進(jìn)程持有目錄內(nèi)文件句柄先用lsof定位占用進(jìn)程sudo lsof D /var/lib/mysql找到PID后處理掉再執(zhí)行rm -rf就順暢了。如果確認(rèn)完全沒有進(jìn)程但目錄仍然刪不掉檢查文件系統(tǒng)是不是以只讀方式掛載。mount命令看一下掛載選項(xiàng)里面多一個(gè)ro字段就解釋了一切。6.6 apt自動(dòng)更新干擾卸載流程Ubuntu的自動(dòng)更新服務(wù)偶爾會(huì)和apt操作搶鎖表現(xiàn)為could not get lock報(bào)錯(cuò)。通常等幾分鐘再執(zhí)行就好。如果確認(rèn)沒有其他apt進(jìn)程運(yùn)行而鎖文件確實(shí)殘留可以手動(dòng)清理sudo rm /var/lib/apt/lists/lock /var/lib/dpkg/lock這條命令要謹(jǐn)慎。鎖文件被真實(shí)占用時(shí)貿(mào)然刪除可能破壞正在進(jìn)行的apt操作。先檢查一下進(jìn)程列表里有沒有apt或dpkg相關(guān)進(jìn)程確認(rèn)無占用再用。寫在最后的一點(diǎn)點(diǎn)經(jīng)驗(yàn)卸載MySQL這件事做成腳本也就十幾行命令但真正決定成敗的是執(zhí)行順序和驗(yàn)證意識(shí)。備份永遠(yuǎn)排第一purge比remove干凈/var/lib/mysql必須手動(dòng)清最后一定要把驗(yàn)證命令完整跑一遍。最后再分享一個(gè)小技巧如果是在開發(fā)環(huán)境反復(fù)折騰MySQL建議把數(shù)據(jù)目錄單獨(dú)掛到一個(gè)獨(dú)立路徑比如編譯安裝時(shí)指定--datadir/data/mysql或者通過my.cnf配置datadir指向獨(dú)立目錄。下次重裝時(shí)只需清掉軟件包數(shù)據(jù)目錄邊界清楚處理起來非常清晰也不會(huì)誤刪。把上面的思路梳理下來其實(shí)就是一句卸載不是刪除是一次需要留后手、留證據(jù)的系統(tǒng)操作。按流程走任何版本都能干干凈凈退場。