首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL 性能優化實戰:覆蓋索引與 SQL_NO_CACHE 深度解析

     編輯:MySQL 綜合教學

優化場景

某電商系統圖片表 pics,數據量近 100 萬行。高頻查詢:根據 user_id 獲取用戶圖片集合。

-- 表結構簡化
CREATE TABLE pics (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    picname VARCHAR(255),
    smallimg VARCHAR(255),
    INDEX idx_user_id (user_id)  -- 原有單列索引
);
-- 目標查詢
SELECT picname, smallimg FROM pics WHERE user_id = 17853;

優化前:單列索引分析

為排除查詢緩存干擾,使用 SQL_NO_CACHE 強制繞過緩存:

SELECT SQL_NO_CACHE picname, smallimg FROM pics WHERE user_id = 17853;

執行 10 次平均耗時 40ms。

EXPLAIN 執行計劃

EXPLAIN SELECT SQL_NO_CACHE picname, smallimg FROM pics WHERE user_id = 17853;

優化前 EXPLAIN

  • type=ref:非唯一索引查找
  • key=idx_user_id:使用單列索引
  • Extra:無 Using index,需回表 查詢 picname、smallimg

優化後:覆蓋索引實戰

創建聯合索引 (user_id, picname, smallimg),使查詢列完全包含於索引中:

ALTER TABLE pics ADD INDEX idx_user_pic (user_id, picname, smallimg);

重新執行 10 次,平均耗時降至 30ms(提升 25%)。

EXPLAIN 驗證

優化後 EXPLAIN

  • key=idx_user_pic:使用新聯合索引
  • Extra=Using index:覆蓋索引生效,無需回表

核心原理:InnoDB 聚簇索引葉子節點存放完整行數據,輔助索引葉子節點存放索引列值 + 主鍵值。覆蓋索引將查詢所需列全部包含在輔助索引中,MySQL 僅掃描索引樹即可滿足查詢,避免了「索引查主鍵 → 回表查完整行」的雙重 IO。


深度擴展:MySQL 查詢緩存與 SQL_NO_CACHE

查詢緩存架構

查詢緩存架構圖

查詢緩存狀態變量

關鍵參數解讀

參數含義調優建議
have_query_cache是否支持查詢緩存YES 表示啟用
query_cache_size緩存總內存大小,0 為禁用根據命中率調整,過大反增鎖開銷
query_cache_type緩存模式:ON/DEMAND/OFFDEMAND 配合 SQL_CACHE 更可控
query_cache_limit單次查詢結果最大可緩存大小默認 1MB,大結果集不宜緩存
query_cache_min_res_unit內存分配最小塊(默認 4KB)見下文計算公式調優

SQL_NO_CACHE vs SQL_CACHE 真相

-- 禁止本次查詢結果被緩存(不影響本次是否讀取緩存)
SELECT SQL_NO_CACHE picname, smallimg FROM pics WHERE user_id = 17853;

-- 顯式要求緩存(需 query_cache_type=DEMAND)
SELECT SQL_CACHE picname, smallimg FROM pics WHERE user_id = 17853;

關鍵誤區:SQL_NO_CACHE 並非「本次查詢不使用緩存」,而是「本次查詢結果不存入緩存供後續查詢使用」。若需強制繞過緩存讀取,需重置緩存 RESET QUERY CACHE 或修改表數據導致緩存失效。

內存碎片與調優公式

緩存分配按 query_cache_min_res_unit 分塊,未用完的內存會被 Trim 歸還,產生碎片。關鍵指標:

  • Qcache_free_blocks 過大 → 碎片嚴重 → FLUSH QUERY CACHE 整理(不清空內容)
  • Qcache_lowmem_prunes 過大 → 緩存空間不足 → 增加 query_cache_size

最佳單元大小計算:

query_cache_min_res_unit = (query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache

覆蓋索引擴展案例

案例 1:分頁查詢優化

原查詢:

SELECT id, title, content FROM article ORDER BY created DESC LIMIT 10000, 10;

問題:大偏移量導致掃描大量行再丟棄。
優化:子查詢只取主鍵 + 覆蓋索引,再回表:

SELECT a.id, a.title, a.content
FROM article a
INNER JOIN (
    SELECT id FROM article ORDER BY created DESC LIMIT 10000, 10
) b USING (id);

創建聯合索引 (created, id)(InnoDB 輔助索引自帶主鍵),子查詢走 Using index,極速定位 10 個 ID,主查詢僅 10 次主鍵回表。

案例 2:InnoDB 主鍵天然覆蓋

InnoDB 輔助索引葉子節點存儲主鍵值,故主鍵查詢永遠覆蓋索引。例如 SELECT id FROM table WHERE secondary_col = ? 直接命中輔助索引無需回表。


總結

  1. 覆蓋索引是高頻查詢的核心優化手段:將 WHERE/JOIN/SELECT 涉及列建立聯合索引,滿足最左前綴原則
  2. SQL_NO_CACHE用於基準測試與非重複查詢,防止緩存污染測試數據
  3. 查詢緩存在高併發寫入場景下鎖競爭劇烈,MySQL 8.0 已移除,建議遷移至 Redis 等外部緩存
  4. 分頁優化:大偏移改用子查詢 + 覆蓋索引 + 主鍵回表,性能提升數量級
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved