iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
Claude AI

用 AI Agent 重構一套無框架的 legacy PHP 系統系列 第 25 篇

Day 25:案例——大表 DDL 差點拖垮整個資料庫實例

  • 分享至 

  • xImage
  •  

前言:「加一個索引」,聽起來是最安全的操作了吧?

在所有資料庫維運操作裡,「加一個索引」大概是最常被當成無腦安全動作的一種——不改資料、不改邏輯,理論上只是幫查詢加速,怎麼想都不該出事。今天的案例,就是這個「理論上」翻車的具體過程。

今日目標

  • 看一個「加索引」這個看似安全的操作,怎麼差點拖垮整台資料庫實例
  • 理解「這個操作安不安全」跟「這個環境撐不撐得住這個操作」是兩個不同的問題
  • 認識一個反直覺的細節:調整某個 session 層級參數為什麼沒有用
  • 學會用 PROCESSLIST 的 STATE 欄位分辨「還在跑」跟「被鎖卡住」

症狀:一個索引,拖垮了整台實例

情境是這樣:某個開發環境的資料庫實例,buffer pool 只設定了 512MB——這是一個相對受限的規格。對一張已經累積到千萬列以上的大表下 ADD INDEX,這個操作直接把整台實例的 I/O 資源吃光,連完全不相關的其他資料庫、簡單到只是 MAX() 這種聚合查詢,都撞上了 120 秒逾時。

這裡最容易被忽略的判斷落差:「加索引是安全的維運操作」這句話本身沒有錯,錯的是把它當成一個放諸四海皆準的結論,而沒有把「這個操作在這個環境的資源規格下,是不是還安全」納入查證範圍。 索引本身的語意沒有風險,但建索引這個過程需要的 I/O 資源,是跟表的大小、索引的複雜度、當下實例的資源餘裕直接相關的——這正是主題句的另一種樣貌:「已確認安全」的範圍,只涵蓋了「這個操作語意上做什麼」,沒有涵蓋「這個操作在這個具體環境下要付出什麼代價」。

用一組對照來看差異:

❌ 籠統的安全判斷:
「加索引是安全操作,不改資料不改邏輯,可以直接下。」
→ 判斷只涵蓋了操作的語意,沒有把「這張表多大」
  「這個環境的資源規格是什麼」納入考量

✅ 把環境規格納入判斷:
「加索引本身安全,但這張表有千萬列以上,
 這個環境的 buffer pool 只有 512MB,
 這個組合會不會讓建索引的 I/O 需求超過實例能承受的範圍?
 先查一下這個環境的資源規格,再決定要不要直接下。」
→ 判斷範圍涵蓋了操作本身跟執行環境的交互影響

一個反直覺的細節:調整 session 參數沒有用

當時第一個直覺反應是調整跟 DDL 建構過程相關的某個 session 層級參數,期待能緩解 I/O 壓力——但實際調整後完全沒有改善。原因是瓶頸不在這個 session 變數控制的範圍內,而是實例本身的資源規格。 session 層級的參數能調整的是「這個連線在建構索引時,怎麼使用它被分配到的資源」,但如果問題根源是「整台實例的資源餘裕本來就不夠」,那不管怎麼調整這個連線內部的行為,都動不了那個更上層的瓶頸。

這個細節值得記住的原因,是它示範了另一種「診斷方向可能沒錯,但解法用錯層級」的情況——跟 Day 24 講的「加鎖降低症狀頻率但沒解決根因」是類似的教訓:調整一個看起來相關的參數、症狀有沒有改善,本身就是一個檢驗訊號——如果調了沒用,代表問題不在這個參數能控制的層級,該往上一層去找瓶頸,而不是繼續在同一個參數上打轉。

另一個反直覺的細節:DROP INDEX 是瞬間完成的

跟「建索引拖垮整台實例」形成強烈對比的是:刪除索引幾乎是瞬間完成的,因為 DROP INDEX 本質上只是一個中繼資料操作——資料庫只需要更新內部記錄「這個索引不存在了」,不需要真正去掃描、重建任何資料結構。建索引需要真正走過整張表的資料、排序、寫入索引結構,兩者在計算量上完全不對稱。

這個不對稱本身是個值得記住的技術細節:「加」跟「拿掉」同一個東西,代價可以天差地遠,不能用「反正是同一種操作的正反面」來類推風險。

診斷技巧:怎麼分辨「還在跑」跟「被鎖卡住」

事後回頭看,這次事件也留下一個實用的診斷技巧:透過 PROCESSLIST 檢查資料庫當下在做什麼時,光看 TIME(已經跑了多久)跟 INFO(執行的語句是什麼)這兩個欄位,沒辦法分辨「這個操作真的還在正常執行中」跟「這個操作其實已經卡住、在等一個拿不到的鎖」——兩種情況下 TIME 都會持續累加,INFO 也都會顯示同一句語句。

真正能分辨這兩種情況的,是 STATE 欄位:如果顯示的是類似「正在改表」這種狀態,代表操作確實還在進行中;如果顯示的是類似「等待資料表中繼資料鎖」這種狀態,代表這個操作根本沒有在推進,只是卡在等別的連線釋放鎖。這兩種狀況需要的處理方式完全不同——前者只能等,後者要去找是誰持有那個鎖、要不要介入處理。

今日思考題

回想你上一次執行一個「理論上安全」的維運操作:你有沒有先確認過,這個操作在當下環境的資源規格下,是不是也真的安全?還是只憑操作本身的語意(不改資料、不改邏輯)就判斷「應該沒問題」?

今日重點回顧

  • 「加索引安全」這個判斷只涵蓋操作語意,沒有涵蓋這個操作在特定資源規格下要付出的代價
  • 調整一個 session 層級參數沒有改善,代表瓶頸在更上層(實例規格本身),不在這個參數能控制的範圍
  • DROP INDEX 幾乎瞬間完成、ADD INDEX 可能拖垮整個實例,兩者計算量完全不對稱,不能用「反正是同一種操作」類推風險
  • PROCESSLIST 的 STATE 欄位(不是 TIME/INFO)才能分辨操作是「還在跑」還是「被鎖卡住」

明日預告

明天是另一種樣貌的案例:一個日誌去重的分組鍵設計錯誤,只有在正式環境的真實資料規模跟樣態下才會踩到——本機測試環境完全看不出問題。


上一篇
Day 24:案例——一次「以為是非同步問題,其實是重複鍵設計錯誤」的除錯過程
下一篇
Day 26:案例——日誌去重分組鍵設計錯誤,正式環境資料才踩到的坑
系列文
用 AI Agent 重構一套無框架的 legacy PHP 系統 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言