1. 程式人生 > 實用技巧 >Mysql查詢優化checklist

Mysql查詢優化checklist

摘要

本文是一份 Mysql 資料表的建立和優化checklist,含表設計、索引的建立及使用原則、SQL 優化以及一些配置、事務、架構層的優化手段。
本文會持續更新,文末附更新記錄。

資料表設計

在滿足業務需求的前提下:

  1. 數值型別優於字元型別。
  2. 字元型別越短越好。
  3. 定長字元使用。 CHAR,變長字元使用 VARCHAR。
  4. 關聯查詢較多時,可以考慮在表中增加冗餘欄位,以空間換時間。
  5. 使用非字元型別做主鍵。
  6. 熱點欄位可以考慮從邏輯上降低併發度,將一行記錄拆分為多行。比如賬戶額度,可以拆分成多條記錄,更新時隨機選擇一條記錄更新。

索引建立

建立索引,需要考慮:

  1. 儘量使用 NOT NULL 約束。
  2. 該欄位值的重複度。重複度高,一般不建立索引。特殊情況是,雖然重複度高,但比例相差大,為了查詢比例最小的那部分資料,而建立索引。
  3. 聯合索引的欄位數量不要過多。
  4. 聯合索引的欄位之間,關聯性儘可能低。
  5. 利用索引優化 GROUP BY、 ORDER BY 欄位。這兩列不同時,可以建立聯合索引。
  6. WHERE 經常查詢的欄位應該建立索引。
  7. DISTINCT 欄位建立索引。
  8. 表連線欄位建立索引。
  9. 表記錄很少時,不需要建立索引。
  10. 頻繁更新的欄位,儘量不要建立索引。
  11. 不要濫用索引。索引過多,會提高索引的維護成本,降低優化器選擇索引的評估效率。可以藉助 pt-query-digest 統計索引的使用頻率。也可以查詢 performance_schema
    table_io_waits_summary_by_index_usage 表,這個表統計了每個索引的 IO 等待事件,COUNT_STAR 是事件次數。

索引使用優化

  1. WHERE 條件中,不要對欄位值進行函式運算。
  2. 遵循最左字首原則。
  3. 有多個 WHERE 查詢條件,區分度最大的欄位儘可能放在左側。
  4. 使用覆蓋索引。
  5. 使用索引下推,需 Mysql 版本大於 5.6。SET optimizer_switch = 'index_condition_pushdown=on'
  6. 有多個 WHERE 查詢條件時,OR 條件的一個欄位沒有索引,該查詢語句就不會使用索引。
  7. LIKE 模糊查詢的字串使用 % 開頭,不會使用索引。

SQL優化

  1. EXISTS 和 IN 子查詢,使用小表驅動大表。主表更大時,使用 IN;主表更小時,使用 EXISTS。(有一點需要注意,IN 會忽略子查詢中是 NULL 的記錄,EXISTS 會查出 NULL 的記錄)
  2. 當優化器選擇了錯誤的索引,首先嚐試重新統計索引資訊 analyze table [table],其次可以使用 FORCE INDEX(column) 語法、刪除錯誤的索引或重寫 SQL 語句來優化。
  3. 統計全表資料行時,COUNT(*) ≈ COUNT(1) > COUNT(欄位)COUNT(*)優先選用更短的二級索引。

其他

配置方面還有這些優化項:

  1. 增加緩衝池大小,開啟多個緩衝池,InnoDB 的配置項是 innodb_buffer_pool_sizeinnodb_buffer_pool_size,MyISAM 的配置項是 key_buffer_size
  2. 開啟自適應雜湊索引,配置項是 innodb_adaptive_hash_index,預設是開啟狀態。
  3. 查詢優化器的一些基礎配置可以根據物理機的配置進行修改,以提升優化器評估索引代價的準確度。比如 io_block_read_cost 從磁碟中讀取一頁資料的代價,修改後通過 FLUSH OPTIMIZER_COSTS 更新到記憶體。更多引數見 mysql.server_costmysql.engine_cost 表。

事務的鎖會大大影響資料庫的查詢效能,因此需要:
3. 對長事務進行監控和優化。如果可以,將長事務拆分為短事務。
4. 在一個事務中,如果涉及多個行鎖,將最可能造成鎖衝突的鎖放在最後。
5. 可以考慮在中介軟體或業務層控制同時操作熱點資料行的執行緒數。

架構方面:
6. 主從架構,讀寫分離。rpl_semi_sync_master_wait_for_slave_count 配置主從同步的強一致的從庫數量。
7. 分庫分表。

資料庫問題定位相關

慢查詢:

-- 檢視慢查詢是否開啟和日誌位置
show variables like '%slow_query_log';

-- 開啟慢查詢
set global slow_query_log='ON';

-- 檢視慢查詢閾值(單位:s)
show variables like '%long_query_time%';

-- 修改慢查詢閾值
set global long_query_time = 3;

PROFILE:

-- 檢視PROFILE是否開啟
show variables like 'profiling';

-- 開啟PROFILE
set profiling = 'ON';

-- 檢視當前會話的PROFILES
show profiles;

-- 檢視上一個查詢開銷
show profile;

-- 檢視指定QUERY ID的開銷
show profile for query 2;
show profile cpu, block io for query 2;

有一點需要注意,SHOW PROFILE 命令將被棄用,可以從 information_schema 中的 profiling 資料表進行檢視。

EXPLAIN 語句的 type 型別顯示索引的使用情況。一般需要重點優化 all(全表掃描) 和 index(全索引表掃描)。
其他的值:
range 代表採用了索引範圍掃描。
index_merge 代表使用了兩個及以上的索引,最後取了交集或並集。
ref 表示採用了非唯一索引,或者唯一索引的非唯一性字首。
eq_ref、const 使用了主鍵或唯一索引。
system 一般用於 MyISAM 或 Memory 表。


更新記錄

2020.09.28 第一版。