- 優化SQL的一般步奏:
-
- 查看當前數據庫狀態
Com_select
|
執行 select 操作的次數,一次查詢只累加 1。
|
Com_insert
|
執行 INSERT 操作的次數, 對於批量插入的 INSERT 操作, 只累加一次。
|
Com_update
|
執行 UPDATE 操作的次數。
|
Com_delete
|
執行 DELETE 操作的次數。
|
Innodb_rows_
read |
select 查詢返回的行數。
|
Innodb_rows_
inserted |
執行 INSERT 操作插入的行數。
|
Innodb_rows_
updated |
執行 UPDATE 操作更新的行數。
|
Innodb_rows_
deleted |
執行 DELETE 操作刪除的行數。
|
Connections
|
試圖連接 MySQL 服務器的次數。
|
Uptime
|
服務器工作時間。
|
Slow_queries
|
慢查詢的次數。
|
- 定位執行效率低的sql語句
- 通過explain分析低效sql的執行計劃
select_type
|
select的類型, 常見的取值有 SIMPLE (簡單表, 即不使用表連接 或者子查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(UNION 中的第二個或 者後面的查詢語句)、SUBQUERY(子查詢中的第一個 SELECT)
|
table
|
輸出結果集的表
|
type
|
表的連接類型,性能由好到差:
|
possible_keys
|
表示查詢時,可能使用的索引
|
key
|
表示實際使用的索引
|
key_len
|
索引字段的長度
|
rows
|
掃描行的數量
|
Extra
|
執行情況的說明和描述
|
- 確定問題並採取優化。
- 通過上面的步奏,定位到哪個SQL, 這個SQL出現了什麼問題。
- 索引問題
- 查詢要使用索引最主要的條件是查詢條件中需要使用索引關鍵字,如果是多列索引,那 麼只有查詢條件使用了多列關鍵字最左邊的前綴時, 纔可以使用索引, 否則將不能使用索引。
- 對於創建的多列索引,只要查詢的條件中用到了最左邊的列,索引一般就會被使用。
- 對於使用 like 的查詢,後面如果是常量並且只有%號不在第一個字符,索引纔可能會 被使用。如果 like 後面跟的是一個列的名字,那麼索引也不會被使用。
- 如果對大的文本進行搜索,使用全文索引而不用使用 like ‘%…%’。
- 如果列名是索引,使用 column_name is null 將使用索引。
- 如果 MySQL 估計使用索引比全表掃描更慢,則不使用索引。例如如果列 key_part1 均勻分佈在 1 和 100 之間,下列查詢中使用索引就不是很好:SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90;
- 如果使用 MEMORY/HEAP 表並且 where 條件中不使用“=”進行索引列,那麼 不會用到索引。heap 表只有在“=”的條件下才會使用索引。
- 用 or 分割開的條件, 如果 or 前的條件中的列有索引, 而後面的列中沒有索引, 那麼涉及到的索引都不會被用到。
- 不是索引列的第一部分。
- 如果 like 是以%開始。
- 如果列類型是字符串,那麼一定記得在 where 條件中把字符常量值用引號引 起來,否則的話即便這個列上有索引。
- 通過命令: show status like 'Handler_read%' 查看
Handler_read_key
|
代表了一個行被索引值讀的 次數,很低的值表明增加索引得到的性能改善不高,因爲索引並不經常使用。
|
Handler_read_rnd_next
|
值高則意味着查詢運行低效,並且應該建立索引補救。這個值 的含義是在數據文件中讀下一行的請求數。如果正進行大量的表掃描, Handler_read_rnd_next 的值較高, 則通常說明表索引不正確或寫入的查詢沒有利用索引
|
- 實用的優化技巧
-
- 定期分析表和檢查表 :
分析表
|
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
|
本語句用於分析和存儲表的關鍵字分佈, 分析的結果將可以使得系統得到準確的統計信 息,使得 SQL 能夠生成正確的執行計劃。如果用戶感覺實際執行計劃並不是預期的執行計 劃, 執行一次分析表可能會解決問題。 在分析期間, 使用一個讀取鎖定對錶進行鎖定。 這對 於 MyISAM, BDB 和 InnoDB 表有作用。對於MyISAM 表,本語句與使用 myisamchk -a 相當。
|
檢查表
|
CHECK TABLE tbl_name [, tbl_name] ... [option] ... option = {QUICK | FAST | MEDIUM | EXTENDED
| CHANGED}
|
1 檢查表的作用是檢查一個或多個表是否有錯誤。 CHECK TABLE 對 MyISAM 和 InnoDB 表有作用。
對於 MyISAM 表,關鍵字統計數據被更新。
2 可以用於檢查視圖。
|
-
- 定期優化表
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
|
如果已經刪除了表的一大部分,或者如果已經對含有可變長度行的表(含有 VARCHAR、 BLOB 或 TEXT 列的表) 進行了很多更改, 則應使用 OPTIMIZE TABLE 命令來進行表優化。 這個 命令可以將表中的空間碎片進行合併, 並且可以消除由於刪除或者更新造成的空間浪費, 但 OPTIMIZE TABLE 命令只對 MyISAM、BDB 和 InnoDB 表起作用
|
- 常用SQL的優化
-
- 大批量插入數據
導入的數據按照主鍵順序排列
|
因爲 InnoDB 類型的表是按照主鍵的順序保存的, 所以將導入的數據按照主鍵的順 序排列,可以有效地提高導入數據的效率。
|
SET UNIQUE_CHECKS
|
在導入數據前執行 SET UNIQUE_CHECKS=0,關閉唯一性校驗,在導入結束後執行 SET UNIQUE_CHECKS=1,恢復唯一性校驗,可以提高導入的效率。
|
SET AUTOCOMMIT
|
如果應用使用自動提交的方式,建議在導入前執行 SET AUTOCOMMIT=0,關閉自 動提交,導入結束後再執行 SET AUTOCOMMIT=1,打開自動提交,也可以提高導入的效率。
|
-
- 優化insert語句
使用多個值表的 INSERT 語句
|
insert into test values(1,2),(1,3),(1,4)…
|
INSERT DELAYED
|
如果從不同客戶插入很多行,能通過使用 INSERT DELAYED 語句得到更高的速度。 DELAYED 的含義是讓 INSERT 語句馬上執行,其實數據都被放在內存的隊列中,並沒有 真正寫入磁盤,這比每條語句分別插入要快的多;LOW_PRIORITY 剛好相反,在所有其 他用戶對錶的讀寫完後才進行插入;
|
數據存放
|
將索引文件和數據文件分在不同的磁盤上存放(利用建表中的選項)
|
bulk_insert_buffer_size
|
如果進行批量插入,可以增加 bulk_insert_buffer_size 變量值的方法來提高速度,但是, 這隻能對 MyISAM 表使用
|
LOAD DATA INFILE
|
當從一個文本文件裝載一個表時, 使用 LOAD DATA INFILE。 這通常比使用很多 INSERT 語 句快 20 倍。
|
-
- 優化group by
-
- 優化order by
這些SQL的排序可以使用索引
|
SELECT * FROM t1 ORDER BY key_part1,key_part2,... ;
SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC, key_part2 DESC;
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;
|
這些SQL的排序不能使用索引
|
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
--order by 的字段混合 ASC 和 DESC
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
--用於查詢行的關鍵字與 ORDER BY 中所使用的不相同
SELECT * FROM t1 ORDER BY key1, key2;
--對不同的關鍵字使用 ORDER BY:
|
-
- 優化or條件
-
- 使用sql提示
USE INDEX
|
在查詢語句中表名的後面, 添加 USE INDEX 來提供希望 MySQL 去參考的索引列表, 就可 以讓 MySQL 不再考慮其他可用的索引。
explain select * from t1 use index(t1_idx) where id =4; --建議mysql使用索引t1_idx
|
IGNORE INDEX
|
如果用戶只是單純地想讓 MySQL 忽略一個或者多個索引,則可以使用 IGNORE INDEX 作 爲 HINT。
explain select * from t1 ignore index(t1_idx) where id =4; --建議mysql忽略索引t1_idx
|
FORCE INDEX
|
爲強制 MySQL 使用一個特定的索引,可在查詢中使用 FORCE INDEX 作爲 HINT。
explain select * from t1 force index(t1_idx) where id =4;
|