網站慢如牛,元兇竟是它?資深工程師帶你用 EXPLAIN 解剖 WordPress 資料庫,揪出 MySQL 效能惡棍!

2025/09/15 | 架構與效能優化

網站慢如牛,元兇竟是它?資深工程師帶你用 EXPLAIN 解剖 WordPress 資料庫,揪出 MySQL 效能惡棍!

嗨,我是浪花科技的資深工程師 Eric。身為一個整天跟程式碼和伺服器打交道的工程師,最常聽到的抱怨之一就是:「Eric,我們的網站最近越來越慢,後台卡到不行,是不是主機不夠力了?」

每次聽到這個,我的雷達就響了。當然,主機規格、快取設定、圖片優化都很重要,但根據我多年的經驗,真正的效能瓶頸,那個躲在幕後、拖垮整個網站的「效能惡棍」,十之八九就是—資料庫查詢。特別是在 WordPress 這個高度依賴資料庫的系統中,一個寫得不好的查詢、一個忘記加的索引,就足以讓你的高效能主機變成一台老舊的 586 電腦。今天,我就要帶你拿起手術刀,學會使用 `EXPLAIN` 這個終極武器,直接剖析 WordPress 的心臟—MySQL 資料庫,把那些效能惡棍一個個揪出來解決掉!

為什麼你的網站越來越慢?兇手往往躲在你看不到的地方

你的 WordPress 網站上的每一篇文章、每一則留言、每一筆訂單、每一個使用者設定,全都儲存在 MySQL 資料庫裡。當使用者瀏覽一個頁面時,WordPress 會在背景執行數十甚至上百個 SQL 查詢,從資料庫中把需要的資料撈出來,再組合成你看到的網頁。

問題來了,當你的網站內容越來越多,資料庫裡的資料表(Table)可能會有數十萬、甚至數百萬筆資料。如果這時候的查詢方式跟大海撈針一樣,MySQL 就需要逐筆掃描整個資料表來找到那幾筆符合條件的資料,這個過程我們稱之為「全表掃描」(Full Table Scan)。這就是災難的開始,也是網站變慢最常見的元兇。

索引是什麼鬼?把它想像成一本書的目錄

那要怎麼避免大海撈針呢?答案就是「索引」(Index)。

你可以把資料庫索引想像成一本書最後面的「索引目錄」。如果你想在《哈利波特》裡找到所有提到「佛地魔」的頁數,你不會從第一頁開始翻到最後一頁吧?你會直接翻到書末的索引,找到「佛地魔」這個詞,旁邊就清楚標示了它出現的所有頁碼。MySQL 索引就是做完全一樣的事情,它為特定欄位建立一個快速查找的結構(通常是 B-Tree),讓資料庫可以直接「跳」到資料存放的位置,而不是傻傻地從頭掃到尾。

聽起來很棒對吧?但身為工程師,我得囉嗦一下:索引不是免費的午餐。它會佔用額外的硬碟空間,而且雖然它能極大地加速 `SELECT`(查詢)操作,卻會稍微拖慢 `INSERT`、`UPDATE`、`DELETE`(寫入、更新、刪除)操作,因為每次資料變動時,索引本身也需要被更新。所以,亂加一通索引反而會造成反效果,這就是為什麼我們需要精準地進行 MySQL 索引優化與效能調教

第一步:揪出效能惡棍!如何找出拖慢網站的 SQL 查詢

在動手術之前,我們得先找到病灶。找出是哪些 SQL 查詢在拖慢你的網站是首要任務。

方法一:用外掛當你的偵探—Query Monitor

對於大多數 WordPress 使用者或開發者來說,Query Monitor 這款外掛是你的超級好朋友。安裝並啟用後,它會在每個頁面的頂部管理列顯示詳細的偵錯資訊,其中就包含了該頁面執行的所有資料庫查詢、執行時間,以及是哪個外掛或佈景主題觸發了它們。如果你看到某個查詢的執行時間特別長(例如超過 0.5 秒),那它就是頭號嫌疑犯!

方法二:工程師的專業武器—MySQL Slow Query Log

如果你想更專業、更全面地監控,那就要啟用 MySQL 的「慢查詢日誌」(Slow Query Log)。這個功能會自動記錄所有執行時間超過你設定閾值的查詢。你需要在你的伺服器 MySQL 設定檔(通常是 `my.cnf` 或 `mariadb.conf.d/50-server.cnf`)中加入以下設定:

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1

這段設定的意思是:

  • slow_query_log = 1: 啟用慢查詢日誌。
  • slow_query_log_file: 指定日誌檔案的路徑。
  • long_query_time = 1: 定義「慢」的標準,這裡是執行超過 1 秒的查詢就會被記錄下來。

設定完後記得重啟 MySQL 服務。接著你就可以定期去檢查這個日誌檔,裡面記錄的都是需要被優化的效能惡棍。

終極武器登場:學會看懂 `EXPLAIN` 的體檢報告

好,現在我們抓到嫌犯了(也就是那條慢查詢 SQL),接下來就要對它進行「審問」。`EXPLAIN` 就是我們的測謊機。只要在你的 SQL 查詢語句前面加上 `EXPLAIN` 關鍵字,MySQL 就不會真的去執行它,而是回傳一份「執行計畫」,告訴你它打算用什麼方式來執行這個查詢。

例如,我們把慢查詢日誌裡的一條 SQL 拿來分析:

EXPLAIN SELECT * FROM wp_posts WHERE post_author = 10 AND post_status = 'publish';

執行後,你會看到一張表格,裡面充滿了各種資訊。別怕,我們只需要關注幾個關鍵欄位。

`EXPLAIN` 報告關鍵欄位全解析

  • `type`: 這是最重要的欄位,沒有之一! 它描述了 MySQL 如何找到所需的資料。理想情況下,我們希望看到的是 `ref`, `eq_ref`, `const`。最需要警惕的是 `ALL`,它代表的就是前面提到的「全表掃描」,是效能殺手!如果看到 `ALL`,基本上就代表你的索引沒有被正確使用,或是根本沒建立索引。
  • `possible_keys`: 顯示 MySQL 認為「可能」可以使用的索引。
  • `key`: 顯示 MySQL 「實際」決定使用的索引。如果這個欄位是 `NULL`,但 `possible_keys` 有值,那代表 MySQL 覺得掃全表可能比用索引還快(通常發生在資料量很小或索引選擇性不佳時)。
  • `rows`: 預估需要掃描的資料筆數。這個數字越小越好。如果 `type` 是 `ALL`,這裡的數字基本上就是整個資料表的總筆數。
  • `Extra`: 包含額外的執行資訊。如果你在這裡看到 `Using filesort` 或 `Using temporary`,這也是個壞消息。它們代表 MySQL 需要在記憶體或硬碟中進行額外的排序或建立暫存表,這都會嚴重拖慢查詢速度。

實戰演練:手術刀級的 MySQL 索引優化

理論講完了,我們來點實際的。下面是兩個在 WordPress 開發中最常見的優化場景。

案例一:`wp_postmeta` 的無底洞

WordPress 的 `postmeta` 資料表設計非常有彈性,但也因此成為效能陷阱。假設我們要查詢一個自訂欄位 `event_date` 為 `2025-12-25` 的所有文章:

SELECT post_id FROM wp_postmeta WHERE meta_key = 'event_date' AND meta_value = '2025-12-25';

如果你用 `EXPLAIN` 去分析這條語句,在一個沒有優化的網站上,你八成會看到 `type: ALL`。因為 WordPress 預設只在 `meta_key` 上建立了索引,但我們的查詢條件是 `meta_key` 和 `meta_value` 兩個欄位。這時候,我們需要建立一個「複合索引」:

ALTER TABLE wp_postmeta ADD INDEX idx_meta_key_value (meta_key, meta_value(191));

這裡有幾個工程師的囉嗦點:

  1. 我們建立了一個包含 `meta_key` 和 `meta_value` 兩個欄位的複合索引。
  2. `meta_value` 欄位的型別是 `LONGTEXT`,不能直接被完整索引。我們必須指定一個前綴長度,`meta_value(191)` 表示我們只索引這個欄位的前 191 個字元。為什麼是 191?這是為了相容 `utf8mb4` 編碼下的 InnoDB 儲存引擎限制。

加上這個索引後,你再執行一次 `EXPLAIN`,會神奇地發現 `type` 變成了 `ref`,`rows` 的數量也大幅下降。你的查詢速度可能從幾秒鐘縮短到幾毫秒!

案例二:複合索引的順序,差一點就差很多!

建立複合索引時,欄位的「順序」至關重要。MySQL 的索引遵循「最左前綴原則」。想像一下查電話簿,你一定是先按「姓」,再按「名」來找。如果你只知道一個人的名,不知道他的姓,電話簿的排序對你就沒什麼幫助。

同樣的道理,如果我們建立了一個索引 `INDEX(A, B, C)`,那麼這個索引可以被 `WHERE A=…`、`WHERE A=… AND B=…`、`WHERE A=… AND B=… AND C=…` 這類的查詢用到。但是,如果你的查詢是 `WHERE B=…` 或 `WHERE C=…`,這個索引就完全派不上用場。

所以在建立複合索引時,你必須思考你的查詢最常以哪個欄位作為篩選條件,並把它放在最前面。

索引不是萬靈丹:何時該踩煞車?

看到這裡,你可能很興奮地想把所有欄位都加上索引。等等!請踩煞車。就像我前面囉嗦過的,索引是有成本的。過多的索引會讓寫入操作變慢,也會佔用大量磁碟空間。

一個基本原則是:不要在「低基數」(low cardinality)的欄位上建立索引。所謂低基數,就是欄位的值重複性非常高。例如,文章的 `post_status`(狀態)欄位,值通常只有 `publish`, `draft`, `pending`, `trash` 等幾種。在這種欄位上建立索引,效益非常低,MySQL 可能寧願選擇全表掃描。

總結:成為資料庫的指揮家,而不是被它綁架

MySQL 索引優化與效能調教 看似深奧,但核心概念其實很直觀:幫資料庫建立好目錄,讓它用最快的方式找到資料。學會使用 `Query Monitor` 和 `Slow Query Log` 找到問題,再用 `EXPLAIN` 深入分析並驗證你的解決方案,你就掌握了 WordPress 效能優化的關鍵鑰匙。

這不是一次性的工作,而是一種持續的維護。隨著網站成長,新的效能瓶頸可能會出現。但現在,你已經擁有了診斷和解決問題的能力。別再讓你的使用者等到天荒地老,也別再讓你的伺服器為低效的查詢而哀嚎了。動手吧!

延伸閱讀

如果你覺得資料庫的世界太過複雜,或者你的網站問題盤根錯節,需要專業的協助來進行全面的效能健檢與優化,歡迎與浪花科技的團隊聯繫。我們很樂意為你打造一個飛快、穩定的 WordPress 網站!

常見問題 (FAQ)

Q1: 到底什麼是 MySQL 索引?可以講得更白話一點嗎?

A: 當然可以。把你的資料庫想像成一本沒有目錄的超厚字典。每次你要查一個單字(一筆資料),你都得從第一頁翻到最後一頁,這就是「全表掃描」,非常慢。索引就像是為這本字典加上了部首或字母順序的目錄,讓你可以快速定位到你要找的單字,查詢速度能提升數十甚至數千倍。

Q2: 如何快速找出是哪個查詢拖慢了我的 WordPress 網站?

A: 對於開發者或網站管理員,最快的方式是安裝「Query Monitor」這個免費外掛。它會直接在頁面上顯示每個查詢的執行時間,哪個查詢最慢一目了然。如果需要更系統化的監控,可以請主機商或工程師協助開啟伺服器的「MySQL Slow Query Log」(慢查詢日誌),它會自動記錄所有執行超時的查詢。

Q3: 是不是把所有欄位都加上索引,網站就會變得最快?

A: 千萬不要!這是個常見的迷思。索引會加速「讀取」(SELECT),但會拖慢「寫入」(INSERT、UPDATE、DELETE),因為每次資料變動時,索引也要跟著更新。過多的索引會讓後台儲存文章、更新商品等操作變慢,並且會佔用大量硬碟空間。索引應該要精準地建立在最常用來篩選(WHERE)、排序(ORDER BY)或連接(JOIN)的欄位上。

Q4: 我用了 `EXPLAIN`,但報告好複雜,我應該先看哪個指標?

A: 請先盯著 `type` 這個欄位看。如果它的值是 `ALL`,那通常就是最大的問題所在,代表 MySQL 正在進行「全表掃描」,你的索引沒有生效。你的目標是透過優化,讓 `type` 變成 `ref`、`eq_ref` 或 `index`。接著再看 `rows` 欄位,這個數字代表預計掃描的筆數,它應該要越小越好。

 
立即諮詢,索取免費1年網站保固