在日常數據庫運維中,“掃表風暴”數次悄然而至——某條未走索引的 SQL 突然執行全表掃描,短短幾分鐘內吃光 IO、拖高 CPU,最終引發集群抖動甚至服務不可用。這樣的事故,你是否也曾經歷過?
全表掃描(Full Table Scan)是數據庫查詢中常見的性能殺手,尤其在數據量巨大的生產環境中,一條效率低下的SQL就足以引發連鎖性的系統故障。為從根本上防范此類風險,百度智能云數據庫在 MySQL 內核層面設計并實現了一套全表掃描動態管控機制,實現對低效SQL的實時檢測、靈活攔截與預警記錄,將運維控制權真正交到開發者以及DBA 手中。
策略機制:雙模式切換、智能管控掃描行為
百度智能云數據庫通常提供如下兩種策略,可通過會話級變量動態切換,例如:
- 攔截模式:主動阻斷全表掃描類SQL,直接報錯,避免其執行,防患于未然;
- 告警模式:放行執行但記錄詳細日志,用于監控、分析或審計,做到有跡可循。
用戶可根據業務時段、環境類型或運維策略,隨時開關相應模式,兼顧開發靈活性與生產安全性。
核心設計:變量控制+白名單機制
通常產品會引入兩個系統變量,用于控制全表掃描行為:
- gaia_prevent_full_table_scans(默認 OFF):一旦開啟,MySQL將在優化階段識別全表掃描操作并直接拋出錯誤
ER_TABLE_FULL_SCAN
,同時中斷查詢。 - gaia_full_table_scans_alarm_allowed(默認 ON):開啟后雖不攔截執行,但會向日志中寫入警告信息,說明發生全表掃描的 SQL 文本,輔助后續優化。
為保障系統內部查詢不受干擾,產品內置了對系統庫(如mysql
、sys
、information_schema
等)的白名單支持。
實現原理:深度鉤入查詢執行流程
此項能力并非通過外圍腳本或中間件實現,而是以內核補丁的方式深度集成在 MySQL 查詢執行流程中,例如在Query_expression::execute()
階段新增掃描檢查邏輯,優化完成后檢查執行計劃調用check_full_table_scan()
判斷當前 SQL 是否包含全表掃描。
其中攔截邏輯是這樣的:
如果gaia_prevent_full_table_scans=ON
且存在全表掃描:
拋出ER_TABLE_FULL_SCAN
錯誤;
中斷執行。
告警邏輯則是這樣的:
如果gaia_full_table_scans_alarm_allowed=ON
且存在全表掃描:
在日志打印WARNING
信息,記錄 SQL;
增加計數器table_full_scan_count
。
正常執行的情況如下,如果未命中限制條件則正常走執行ExecuteIteratorQuery(thd)
。
在check_full_table_scan()
中:
- 遍歷 JOIN 的
qep_tab
執行計劃; - 判斷
qep_tab->type()
是否為JT_ALL
(全表掃描); - 若表屬于白名單數據庫,則跳過檢查;
- 其他情況則標記
has_full_table_scan=true
。
偽代碼示例:
switch (qep_tab->type()) {case JT_ALL:if (非系統數據庫) {has_full_table_scan = true;}break;default:// 非全表掃描has_full_table_scan = false;
}
使用示例:明快的控制體驗
開啟攔截模式,如下:
// 將攔截模式開關打開。
SET SESSION gaia_prevent_full_table_scans =ON;
// 查詢一個全表掃描的語句。
SELECT * FROM t1;
// 查詢會被攔截,并且報錯。
--ERROR 12345 (HY000): There is a full table scan in sql. You can modify gaia_prevent_full_table_scans to turn off the restriction
啟用告警模式,如下:
// 關閉攔截模式,并且打開報警開關。
SET SESSION gaia_prevent_full_table_scans = OFF;
SET SESSION gaia_full_table_scans_alarm_allowed =ON;// 查詢一個全表掃描的語句。
SELECT * FROM t1;
// 查詢可以正常執行,但是會在日志中,打印涉及全表掃描的sql。
-- SQL 正常執行-- 日志打印: WARNING [Full table scan sql : SELECT * FROM t1;]
測試表現:精準識別、穩定可控
我們對該機制進行了多場景驗證,分別是:
- 正常索引查詢暢通無阻;全表掃描在攔截模式下準確中斷。
設置gaia_prevent_full_table_scans=ON
,執行全表掃描 SQL,確認報錯;
設置gaia_full_table_scans_alarm_allowed=ON
,確認日志輸出但 SQL 可執行。 - 系統庫查詢不受影響。
在mysql
數據庫執行SELECT * FROM user;
,確認不會報錯。 - 雙變量沖突時以攔截為優先策略,避免安全漏洞。
兩個變量都OFF
時,全表掃描允許執行且不告警;
兩個變量都ON
時,以prevent
優先。
總結:運維新利器、性能守護者
這套全表掃描限制機制雖在實現上簡潔高效,卻可為數據庫系統帶來立竿見影的收益:
- ?事前預防:攔截模式將風險查詢拒之門外,保障生產環境穩定;
- ?事中可見:告警模式記錄低效 SQL,便于跟蹤與優化;
- ?靈活調度:動態開關策略,適配不同業務時段與環境類型;
- ?無縫集成:內核級實現,無需修改業務 SQL,零侵入。
對于具備中大規模 MySQL 集群的企業來說,這類細粒度、內核級的管控工具,無疑是提升數據庫可靠性與運維效率的關鍵一步。