Mysql查詢優化checklist
阿新 • • 發佈:2020-10-04
摘要
本文是一份 Mysql 資料表的建立和優化checklist,含表設計、索引的建立及使用原則、SQL 優化以及一些配置、事務、架構層的優化手段。
本文會持續更新,文末附更新記錄。
資料表設計
在滿足業務需求的前提下:
- 數值型別優於字元型別。
- 字元型別越短越好。
- 定長字元使用。 CHAR,變長字元使用 VARCHAR。
- 關聯查詢較多時,可以考慮在表中增加冗餘欄位,以空間換時間。
- 使用非字元型別做主鍵。
- 熱點欄位可以考慮從邏輯上降低併發度,將一行記錄拆分為多行。比如賬戶額度,可以拆分成多條記錄,更新時隨機選擇一條記錄更新。
索引建立
建立索引,需要考慮:
- 儘量使用 NOT NULL 約束。
- 該欄位值的重複度。重複度高,一般不建立索引。特殊情況是,雖然重複度高,但比例相差大,為了查詢比例最小的那部分資料,而建立索引。
- 聯合索引的欄位數量不要過多。
- 聯合索引的欄位之間,關聯性儘可能低。
- 利用索引優化 GROUP BY、 ORDER BY 欄位。這兩列不同時,可以建立聯合索引。
- WHERE 經常查詢的欄位應該建立索引。
- DISTINCT 欄位建立索引。
- 表連線欄位建立索引。
- 表記錄很少時,不需要建立索引。
- 頻繁更新的欄位,儘量不要建立索引。
- 不要濫用索引。索引過多,會提高索引的維護成本,降低優化器選擇索引的評估效率。可以藉助 pt-query-digest 統計索引的使用頻率。也可以查詢
performance_schema
table_io_waits_summary_by_index_usage
表,這個表統計了每個索引的 IO 等待事件,COUNT_STAR
是事件次數。
索引使用優化
- WHERE 條件中,不要對欄位值進行函式運算。
- 遵循最左字首原則。
- 有多個 WHERE 查詢條件,區分度最大的欄位儘可能放在左側。
- 使用覆蓋索引。
- 使用索引下推,需 Mysql 版本大於 5.6。
SET optimizer_switch = 'index_condition_pushdown=on'
。 - 有多個 WHERE 查詢條件時,OR 條件的一個欄位沒有索引,該查詢語句就不會使用索引。
- LIKE 模糊查詢的字串使用 % 開頭,不會使用索引。
SQL優化
- EXISTS 和 IN 子查詢,使用小表驅動大表。主表更大時,使用 IN;主表更小時,使用 EXISTS。(有一點需要注意,IN 會忽略子查詢中是 NULL 的記錄,EXISTS 會查出 NULL 的記錄)
- 當優化器選擇了錯誤的索引,首先嚐試重新統計索引資訊
analyze table [table]
,其次可以使用FORCE INDEX(column)
語法、刪除錯誤的索引或重寫 SQL 語句來優化。 - 統計全表資料行時,
COUNT(*) ≈ COUNT(1) > COUNT(欄位)
,COUNT(*)
優先選用更短的二級索引。
其他
配置方面還有這些優化項:
- 增加緩衝池大小,開啟多個緩衝池,InnoDB 的配置項是
innodb_buffer_pool_size
、innodb_buffer_pool_size
,MyISAM 的配置項是key_buffer_size
。 - 開啟自適應雜湊索引,配置項是
innodb_adaptive_hash_index
,預設是開啟狀態。 - 查詢優化器的一些基礎配置可以根據物理機的配置進行修改,以提升優化器評估索引代價的準確度。比如
io_block_read_cost
從磁碟中讀取一頁資料的代價,修改後通過FLUSH OPTIMIZER_COSTS
更新到記憶體。更多引數見mysql.server_cost
、mysql.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 第一版。