Wordpress

資料庫連線錯誤?看 MySQL 錯誤碼揪出 4 個真正原因

整個網站忽然只剩下一行黑字:「Error establishing a database connection」,前台後台一起變成白畫面,連登入畫面都進不去。打開 wp-config.php,把 DB_NAME、DB_USER、DB_PASSWORD、DB_HOST 四個欄位一個字一個字比對,密碼複製貼上、資料庫名稱查了兩遍,全部正確,網站還是連不上。

這是資料庫連線錯誤裡最讓人洩氣的一種情況,憑證明明沒問題,MySQL 卻還是不放行。資料庫連線錯誤指的是 WordPress 在畫出任何一個頁面之前,都得先向 MySQL 資料庫伺服器要資料,一旦這一步要不到,整頁就只剩下這句通用訊息;問題是這句話本身完全不會說明卡在哪一關,只會讓人下意識覺得一定是帳密打錯了,重新確認一次還是同一句話,就這樣在原地打轉。

實際上,能造成這句訊息的原因至少有四種,而且都跟帳密字面對不對沒有關係:資料庫使用者的權限沒有跟著環境一起搬過來、DB_HOST 填的不是資料庫真正所在的位置、連線數被佔滿到擠不進去,或是資料表本身已經毀損。照著 MySQL 實際回傳的錯誤碼,就能把這四種情況一一拆開,不必透過 WordPress 也能直接問出真正原因。先從眼前這個畫面本身講起。

資料庫連線錯誤讓前台後台同時陷入空白

打開網站看到的畫面通常只有一句「Error establishing a database connection」,或是中文化後的「建立資料庫連線時發生錯誤」,沒有樣式、沒有選單、連頁尾都沒有,因為連載入樣式表跟選單資料都需要先查詢資料庫,這一步失敗,後面所有內容都無從產生。前台是這樣,打開 wp-admin 的登入頁面看到的也是同一句話,並不是巧合,因為 WordPress 驗證帳號密碼這個動作,本身就要先查詢資料庫裡的使用者資料表才做得到,資料庫連不上,連檢查你輸入的密碼對不對這件事都做不到,於是前台後台會同時倒下。

最基礎的檢查是把 wp-config.php 裡的四個官方欄位重新核對一次。DB_NAME 是 WordPress 要使用的資料庫名稱,DB_USER 是連線用的 MySQL 帳號,DB_PASSWORD 是這組帳號的密碼,DB_HOST 則是資料庫伺服器的位址,多數共用主機的預設值就是 localhost。但如果這四個欄位已經逐字核對過,接下來要先建立一個觀念,字面正確跟真的連得上,是兩件不同的事。四個欄位打對只代表沒有打錯字,不代表這個使用者對這個資料庫有存取權限、不代表 DB_HOST 填的正好是資料庫實際所在的那一台伺服器、不代表資料庫伺服器目前是活著的、也不代表資料表本身完好無損。這四個條件都沒有被驗證過,而後面四節要一一排查的,正是它們。

wp-config.php 的 DB_NAME、DB_USER、DB_PASSWORD、DB_HOST 四欄逐字對,不代表使用者有權限、DB_HOST 是實際位置、伺服器活著或資料表完好
四個欄位逐字核對只能確認沒有打錯字,真正能不能連上,還取決於權限、位置、伺服器與資料表這四個沒被驗證的條件。

這裡也順便把資料庫連線錯誤跟另外兩種常被搞混的狀況分開。500 Internal Server Error 通常是 PHP 程式執行到一半發生錯誤,伺服器的錯誤記錄檔裡會留下更明確的錯誤堆疊,跟連線失敗是不同層級的問題。另外,如果網站沒有掛頁面快取或 CDN,前台有時反而會因為瀏覽器或某層快取殘留舊畫面,短暫看起來一切正常,這種假象容易讓人誤判故障其實還沒發生,實際上資料庫早就連不上了,只是還沒有人去要求它重新產生頁面。

四種 MySQL 錯誤碼分辨連不上與連得上卻被拒

WordPress 顯示的這句訊息是包裝過的通用文字,本身不會告訴你連線是在哪一步失敗的,繼續盯著這句話核對帳密不會有進展。比較有效的做法是繞開 WordPress,直接寫一支不依賴 WP 的小型測試腳本去問 MySQL 本身,因為 MySQL 回傳的錯誤碼才是真正有意義的線索。

<?php
$link = mysqli_connect( 'DB_HOST', 'DB_USER', 'DB_PASSWORD', 'DB_NAME' );

if ( ! $link ) {
    echo 'connect_error: ' . mysqli_connect_errno() . ' - ' . mysqli_connect_error();
} else {
    echo 'connected OK';
}Code language: PHP (php)

把上面四個值換成 wp-config.php 裡的內容,用瀏覽器打開這支檔案,畫面會直接印出 MySQL 的原始錯誤代碼與訊息,而不是 WordPress 包裝過的那一句。測試完務必立刻刪除這支檔案,因為它會把資料庫帳密的連線結果暴露給任何能打開這個網址的人,留在伺服器上是安全隱患。

官方錯誤碼分辨連不上、被拒與過載三種故障

MySQL 官方的 Client Error Message Reference 與 Server Error Message Reference 把常見的連線錯誤定義得很清楚,整理成對照:

錯誤碼官方訊息代表意義
2002Can’t connect to local MySQL server through socket連線逾時或被拒絕,通常是 host 填錯,或資料庫伺服器根本沒在監聽(若 DB_HOST 填的是實際主機位址或 IP 而非 localhost,走 TCP 連線時對應的官方錯誤碼是 2003:Can’t connect to MySQL server on ‘host’,判斷方向相同)
1045Access denied for user …(using password)帳號或密碼字面真的不對
1044Access denied for user … to database …帳密通過驗證,但這個帳號對指定的資料庫沒有存取權
1040Too many connections連線數已經被佔滿

這四個代碼分別指向不同方向。2002(或走 TCP 連線時對應的 2003)代表根本連不到那台主機,指向後面「DB_HOST 不是資料庫真正的位置」與「伺服器過載到完全無回應」這兩種情況;1045 代表帳號或密碼字面真的打錯,這種情況要回頭重新逐字核對 wp-config.php,不在這篇深入討論;1044 代表帳密通過了驗證,但這個帳號對指定的資料庫沒有存取權,是這句訊息背後最常見、也最容易被誤判成帳密打錯的一種,指向資料庫使用者權限沒有跟著搬遷過來這一種故障;1040 代表連線數已經被佔滿,指向連線數用光的那一節。先用測試腳本印出實際錯誤碼再往下對照,比直接改密碼、改設定要有方向得多。

用測試腳本讓 MySQL 回傳錯誤碼,2002 指向 DB_HOST 或伺服器、1044 指向權限沒搬、1040 指向連線數佔滿、1045 才是帳密真的打錯
先繞開 WordPress 用測試腳本問 MySQL,拿到 2002、1045、1044、1040 這幾個錯誤碼,就能決定往哪個方向排查。

資料庫使用者權限沒有跟著搬遷過來

憑證,也就是帳號、密碼、資料庫名稱、host,字面上完全正確,使用者也確實能通過 MySQL 的連線驗證,但 MySQL 的存取控管其實分成兩個階段,先驗證你是誰,也就是核對帳密,通過之後再檢查你對這個資料庫、這張表有沒有做這個操作的權限。第一階段過關,不代表第二階段也會過關。

這種情況最常發生在資料庫剛搬過家的時候,換主機、還原備份、或是用 SQL 檔手動匯入資料表,新環境裡建立了一個同名的資料庫使用者,卻沒有把權限真正授權給它對應到新的資料庫。另一種常見情形是舊主機的帳號寫成 'user'@'localhost',新主機卻只認 'user'@'%',這兩者在 MySQL 眼中是完全不同的帳號,即使使用者名稱的文字一模一樣。這裡要先建立一個觀念,建立了一個帳號,跟這個帳號能不能動這個資料庫,是分開的兩件事,一台新主機把帳號建好了,不代表權限也一起帶過去了。要讓 WordPress 正常運作,這個帳號至少需要能查詢、寫入資料,也要有異動資料表結構的權限,才能安裝外掛、更新版本這類會動到資料庫結構的操作。

SHOW GRANTS 核對使用者實際持有的權限

如果還能透過主機控制台或指令列直接連上 MySQL,不透過 WordPress,可以執行下面這行指令看這個帳號實際被授予了什麼:

SHOW GRANTS FOR 'user'@'host';Code language: SQL (Structured Query Language) (sql)

輸出會是一行或多行 GRANT ... ON ... TO ...,重點是核對授權的資料庫名稱是不是就是 wp-config.php 裡的 DB_NAME,以及授權範圍有沒有涵蓋 WordPress 需要的操作。MySQL 官方的 GRANT 語法文件把權限層級分成三種:全域的 *.*、資料庫層級的 db_name.*、以及表層級的 db_name.tbl_name,WordPress 需要的是資料庫層級的完整讀寫與建表權限,不是只給表層級幾張零星的表。如果查到的授權只有 SELECT,卻漏了 INSERTUPDATECREATEALTER 這幾項,代表這個帳號能連上、能讀,但寫入資料或安裝外掛時就會出錯,這仍然屬於同一類根因,只是表現得更隱蔽。

GRANT 加 FLUSH PRIVILEGES 才讓權限真正生效

確認少了哪些權限之後,用一個有管理權限的帳號連上 MySQL,執行下面的指令把完整權限補上:

GRANT ALL PRIVILEGES ON db_name.* TO 'user'@'host';
FLUSH PRIVILEGES;Code language: SQL (Structured Query Language) (sql)

GRANT ALL ON db1.* TO 'jeffrey'@'localhost'; 是 MySQL 官方文件本身列出的範例寫法,把整個資料庫的完整權限授予一個帳號(ALLALL PRIVILEGES 是同義詞,兩種寫法效果相同)。用 GRANT 這種語法授權,MySQL 會立即生效,理論上不必再靠 FLUSH PRIVILEGES;但官方文件也提醒,如果現場改的是直接改動權限資料表這種更底層的做法,變更就一定要執行 FLUSH PRIVILEGES 才會被重新讀取,養成授權完就順手執行一次的習慣不會有壞處,也能排除這個變數。要特別提醒的是,db_name'user'@'host' 都要跟 wp-config.php 裡的 DB_NAMEDB_USER 逐字一致,host 段尤其容易被忽略。MySQL 帳號在系統內部是用完整的 'user_name'@'host_name' 字串識別,host 段不同就視為完全不同的帳號,如果新環境的帳號 host 段跟舊環境不一樣,補權限時要用新環境實際的 host 字串重新授權,照抄舊環境的紀錄不會生效。

代管主機把資料庫放上獨立的伺服器

多數共用主機的資料庫跟網站檔案放在同一台機器上,這種情況下 DB_HOSTlocalhost 剛好正確。但不少代管型 WordPress 主機,或是規模較大的架構,會把資料庫獨立放到專用的資料庫伺服器上做效能隔離。這種架構下 DB_HOSTlocalhost 的意思是去問這台機器上有沒有 MySQL 在監聽,但資料庫根本不在這台機器上,於是出現的是連不到,也就是前面提到的錯誤碼 2002,而不是被拒絕。

這種情況最容易在剛搬到代管主機、或主機商調整了資料庫架構之後發生,帳密可能完全正確,只是那一行 host 填的位址錯了。判斷方向其實不難,如果測試腳本印出的是 2002,而權限也已經確認沒問題,下一步該懷疑的就是 DB_HOST 這一行,而不是繼續核對帳密。

DB_HOST 可以帶連接埠或 Socket 路徑

DB_HOST 這個欄位並不是只能填主機名稱或 IP 位址,WordPress 官方文件列出了幾種替代寫法。使用非預設連接埠時可以寫成:

define( 'DB_HOST', '127.0.0.1:3307' );Code language: PHP (php)

或是 'mysql.example.com:3307' 這種在主機位址後面加冒號帶埠號的格式。使用 Unix Socket 時則寫成 '127.0.0.1:/var/run/mysqld/mysqld.sock',也就是「主機加路徑」的格式。這代表 DB_HOST 這一欄本身可能需要比「填對主機名稱」更多的資訊,如果主機商用的是非預設連接埠、或要求走特定的 Socket 檔案,只填對主機位址、沒帶對埠號或路徑,一樣連不上。

主機商是查證正確 DB_HOST 值的唯一管道

如果懷疑問題出在 DB_HOST,官方文件給的建議相當務實,先用預設值 localhost 試試看,不行的話就要向主機商確認正確值,而不是自己用猜的。如果主機提供 phpMyAdmin 之類的資料庫管理介面,登入後畫面上通常會顯示目前連線所在的伺服器資訊,可以拿來反向核對,但最終還是要以主機商提供的正確位址為準,尤其是代管型主機,資料庫伺服器的位址通常不對外公開,只有主機商知道正確值。花時間逐一試連接埠或猜位址不會有結果,這種情況下開一張工單反而比較快。

連線數用光時,帳密正確也擠不進資料庫

還有一種憑證完全沒問題、卻就是連不上的情況,而且往往是間歇性的,有時候正常,有時候噴錯,重新整理幾次可能又恢復了。MySQL 用系統變數 max_connections 畫出同時允許的連線數上限,一旦某個時間點的連線數,不管是同一個網站流量暴衝,還是同一台主機上其他網站佔滿了資源,這在共用主機特別常見,撞到這個上限,新的連線請求就會被直接拒絕,回傳 Too many connections

這種情況跟前面兩節不一樣。不是憑證錯,也不是位址錯,是資源被佔滿,解法方向也完全不同,不是改帳密或改 host,而是要處理連線數本身。

比 max_connections 多留一位管理名額的連線上限

可以用下面這行指令查目前的連線數上限:

SHOW VARIABLES LIKE 'max_connections';Code language: SQL (Structured Query Language) (sql)

如果有管理權限,可以用下面這行動態調整:

SET GLOBAL max_connections = 200;Code language: SQL (Structured Query Language) (sql)

要注意的是這種調整方式重開機後就會失效,永久生效要把這個設定寫進 MySQL 設定檔的 [mysqld] 區塊。另外 MySQL 官方文件也說明,實際允許的連線數其實是 max_connections 再加 1,多出的這一個名額保留給具備 CONNECTION_ADMINSUPER 權限的帳號,讓管理者即使在連線數滿載時仍然能連進伺服器診斷問題。這也解釋了為什麼有時候一般帳號連不上,主機商後台的管理工具卻還能看到資料庫狀態,用的正是這個多留出來的名額。

時好時壞的間歇性斷線是伺服器過載的典型特徵

這種故障的行為特徵,其實可以拿來當初步判斷的依據,不用每次都先寫測試腳本才知道方向。如果是持續性的,每次重新整理都得到同樣的錯誤碼,八成是憑證或 host 這種設定層的問題;如果是特定時段、流量高峰、或重新整理幾次就恢復正常,方向就要往連線數過載去想。這是因為連線數用光的本質是資源競爭,不是設定錯誤,錯誤發生在所有可用連線都被佔用的那個瞬間,會隨著同時段的連線數增減而變化,不會是固定不變的錯誤。共用主機因為多個網站共用同一組資源上限,即使自己網站的流量沒有變化,也可能被同一台主機上其他網站的流量波及,這種時候光盯著自己網站的流量報表不會看出問題。

資料表毀損時,重新輸入密碼救不回連線

第四個根因,也是最容易被誤判成憑證又打錯了的一種,其實是資料表本身已經壞掉。常見成因是非正常關機、磁碟寫入中途被中斷、或是某個外掛或佈景主題執行到一半的資料庫操作被打斷,留下不完整或損毀的表。這種情況下前台訪客看到的仍然是同一句「Error establishing a database connection」,但如果當下還能勉強進到後台,可能會先看到另一則更具體的訊息:「One or more database tables are unavailable. The database may need to be repaired.」,這則訊息才是資料表毀損的明確信號,跟前面三種連線層的問題已經不在同一個層次,這時連線本身是通的,是連上之後讀資料失敗了。

WP_ALLOW_REPAIR 這個常數負責開啟內建修復模式

WordPress 內建了一個不需要額外工具的修復方式。在 wp-config.php 裡加入下面這一行:

define( 'WP_ALLOW_REPAIR', true );Code language: PHP (php)

然後在瀏覽器打開網域加上 /wp-admin/maint/repair.php,會看到「修復資料庫」與「修復並優化資料庫」兩個選項,先選單純修復即可,救站優先於順便優化。要特別注意這個頁面被刻意設計成不需要登入就能存取,理由很直接,WordPress 這時候連不上資料庫,本來就沒辦法驗證登入身分,所以修復完成後一定要立刻把那一行程式碼刪掉,否則任何人都能打開這個頁面。

MyISAM 吃得下 REPAIR TABLE,InnoDB 要靠別的機制

這裡要補上前面內建修復工具沒講清楚的限制。WordPress 的修復功能底層執行的是 MySQL 的 REPAIR TABLE 指令:

CHECK TABLE wp_options;
REPAIR TABLE wp_options;Code language: SQL (Structured Query Language) (sql)

REPAIR TABLE 只支援 MyISAM、ARCHIVE、CSV 這幾種儲存引擎,不支援 InnoDB。WordPress 本身的資料表結構並沒有指定引擎,用的是資料庫伺服器當下的預設值;MySQL 自 5.5 版(2010 年發布)起把預設引擎從 MyISAM 換成 InnoDB,所以只要主機的 MySQL/MariaDB 夠新,新安裝的站台建出來的表多半就是 InnoDB,比較舊的站台,或當初裝在更早版本資料庫上的,則可能還留著 MyISAM 的舊表。如果毀損的剛好是 InnoDB 表,內建修復工具幫不上忙,需要的是伺服器層級的 innodb_force_recovery 機制,從最低的等級 1 開始逐步往上試,讓 MySQL 能先啟動、把資料導出來。MySQL 官方文件明確警告,等級 4 以上可能永久毀損資料檔案,只能在先備份、且已經在另一份實體複本上測試成功過的前提下才用在正式站台,建議一律從等級 1 開始逐步往上嘗試,不要直接跳到高等級。這已經超出一般網站管理者自己動手的安全範圍,是判斷該找主機商的明確界線。

連線恢復不代表故障已經排除

修完任何一項根因之後,接下來要做的是完整驗證,而不是看到首頁恢復正常就結束。第一步是重新執行前面用來分辨錯誤碼的測試腳本,確認不再出現任何 MySQL 錯誤碼,確認完立刻刪除那支測試檔案。第二步是確認 wp-admin 能正常登入,而不只是前台看起來正常,因為前台可能還在吃快取,看起來沒事不代表資料庫真的通了。

如果原本走的是資料表修復或權限修復,要回頭確認後台不再跳出資料表需要修復之類的提示。如果根因是連線數過載這種間歇性問題,恢復後的當下正常不代表根因已經排除,要多觀察一段時間,留意是否在特定時段又復發,因為過載型故障的本質是資源競爭,臨時的流量低點只是暫時掩蓋了問題,不是問題本身消失了。最後,任何為了排查而暫時加上的設定都要清乾淨,WP_ALLOW_REPAIR 那一行、或為了看更詳細錯誤而暫時開啟的 WP_DEBUG_DISPLAY,排查結束都應該關掉或移除,留著會變成安全隱患。

伺服器層級的修復,只能留給主機商執行

排查到這裡,該劃出一條界線,分清楚哪些是網站管理者自己能處理的,哪些必須交給主機商。需要 SSH 或伺服器 root 權限才能執行的操作屬於後者,例如永久調整 max_connections 要改到 MySQL 設定檔並重啟服務,或是執行 innodb_force_recovery 這種等級 4 以上就可能永久毀損資料的高風險操作,都不是一般管理者帳號碰得到的層級。

資料庫連線錯誤的排查界線:核對 wp-config、查補權限、改 DB_HOST、修 MyISAM 屬自己能修,innodb_force_recovery 與永久調 max_connections 等要交給主機商
改 wp-config、查補權限、修 MyISAM 表這些自己能處理;要 SSH 或 root 權限的伺服器層級操作,交給主機商比較安全。

DB_HOST 的正確值查不到也是同一類情況,尤其代管主機常把資料庫放在對外不公開的內部網路位址,只有主機商知道正確值,靠猜的不會有結果。如果資料庫伺服器完全沒有回應,而且同一台主機上的其他網站也一起掛掉,代表問題出在主機層級的共用資源,不是單一網站的設定能解決的。資料表毀損的是 InnoDB、內建修復工具無效、又沒有把握自己操作 innodb_force_recovery 的情況同樣屬於這一類,這幾種都不是把 wp-config.php 改對就能解決的問題,硬要自己動手,風險遠高於效益。

四種根因排查到這裡,帳密字面正不正確早就不是重點,真正有用的是那支測試腳本印出來的錯誤碼,以及照著錯誤碼往對應的方向走。下次再看到同一句「Error establishing a database connection」,與其把密碼重新輸入一次,不如先問 MySQL 本身,它實際回了什麼。

常見問答

本區問答由 AI 依文章內容自動整理,僅供快速參考,正式內容仍以全文為準。

為什麼要寫測試腳本直接問 MySQL 錯誤碼?

因為 WordPress 顯示的是包裝過的通用訊息,不會說明卡在哪一關;繞開 WordPress 用一支小型測試腳本連線 MySQL,就能印出原始錯誤代碼與訊息,比反覆核對帳密更有方向。

資料庫搬家後,為什麼帳密對了卻連不上?

因為 MySQL 的存取控管分成兩階段,先核對帳密,通過後才檢查對這個資料庫有沒有操作權限;搬家、還原備份或用 SQL 手動匯入時,新環境常建立了同名帳號,卻沒有把權限一併授權給新的資料庫。

DB_HOST 填 localhost 為什麼還是連不上?

因為部分代管主機或大型架構會把資料庫獨立放在專用伺服器上,這時 DB_HOST 填 localhost 只是問網站所在這台機器有沒有 MySQL 在監聽,資料庫根本不在這台機器上,連線因此直接失敗。

資料庫連線時好時壞可能是什麼原因?

可能是 MySQL 的連線數被同時段的流量佔滿,撞到 max_connections 上限後新連線就會被拒絕;這種故障通常間歇出現,流量高峰時報錯、恢復平常後又能連上,跟帳密或設定錯誤的持續性故障不同。

資料表毀損時,後台會出現什麼提示?

後台可能會看到資料表無法使用、需要修復這類更具體的提示,代表連線本身其實是通的,是連上之後讀取資料失敗了,跟前面幾種連線層的問題不在同一個層次。

資料來源
  1. MySQL 8.0 Error Reference :: Client Error Message Reference — MySQL
  2. MySQL 8.0 Error Reference :: Server Error Message Reference — MySQL
  3. MySQL 8.0 Reference Manual :: 15.7.1.6 GRANT Statement — MySQL
  4. wp-config.php – Common APIs Handbook — WordPress
  5. MySQL 8.0 Reference Manual :: 7.1.8 Server System Variables — MySQL
  6. MySQL 8.0 Reference Manual :: 17.22.2 Forcing InnoDB Recovery — MySQL