優化SQL的一般步驟:
1. 通過show status 和應用特點了解各種SQL 的執行頻率。
通過SHOW STATUS可以提供服務器狀態信息, 也可以使用mysqladmin extendedstatus命令獲得。 SHOW STATUS可以根據需要顯示session級別的統計結果和global級別的統計結果。
以下幾個參數對Myisam和Innodb存儲引擎都計數:
1. Com_select 執行select操作的次數,一次查詢只累加1;
2. Com_insert 執行insert操作的次數,對於批量插入的insert操作, 只累加一次;
3. Com_update 執行update操作的次數;
4. Com_delete 執行delete操作的次數;
以下幾個參數是針對Innodb存儲引擎計數的,累加的算法也略有不同:
1. Innodb_rows_read select查詢返回的行數;
2. Innodb_rows_inserted執行Insert操作插入的行數;
3. Innodb_rows_updated 執行update操作更新的行數;
4. Innodb_rows_deleted 執行delete操作刪除的行數;
通過以上幾個參數, 可以很容易的瞭解當前數據庫的應用是以插入更新爲主還是以查詢操作爲主, 以及各種類型的SQL大致的執行比例是多少。
對於更新操作的計數,是對執行次數的計數,不論提交還是回滾都會累加。對於事務型的應用, 通過Com_commit和Com_rollback可以瞭解事務提交和回滾
的情況,對於回滾操作非常頻繁的數據庫,可能意味着應用編寫存在問題。
此外,以下幾個參數便於我們瞭解數據庫的基本情況:
1. Connections 試圖連接Mysql服務器的次數
2. Uptime 服務器工作時間
3. Slow_queries 慢查詢的次數
定位執行效率較低的SQL語句:
可以通過以下兩種方式定位執行效率較低的SQL語句:
1. 可以通過慢查詢日誌定位那些執行效率較低的sql語句,用--log-slowqueries[=file_name]選項啓動時, mysqld寫一個包含所有執行時間超過
long_query_time秒的SQL語句的日誌文件。
2. 慢查詢日誌在查詢結束以後才紀錄, 所以在應用反映執行效率出現問題的時候查詢慢查詢日誌並不能定位問題,可以使用show processlist命令查看當前MySQL在
進行的線程,包括線程的狀態,是否鎖表等等,可以實時的查看SQL執行情況, 同時對一些鎖表操作進行優化。
過 EXPLAIN分析低效SQL的執行計劃:
通過以上步驟查詢到效率低的SQL後,我們可以通過explain或者desc 獲取MySQL如何執行SELECT語句的信息,包括select語句執行過程表如何連接和連接的次序。
explain可以知道什麼時候必須爲表加入索引以得到一個使用索引來尋找記錄的更快的SELECT。
mysql> explain select sum(moneys) from sales a,companys b where a.company_id = b.id and a.year = 2006;
+----------------+----------+-----------+----------------+----------------+---------
-+-----------+----------------+
| select_type | table | type | possible_keys| key | key_len | rows
| Extra |
+----------------+----------+-----------+----------------+----------------+---------
-+-----------+----------------
| SIMPLE | b | index | PRIMARY | PRIMARY | 4 | 1 | Using index
|
| SIMPLE | a | ALL | NULL | NULL | NULL | 12 | Using where
|
+----------------+----------+-----------+----------------+----------------+---------
-+-----------+----------------+
2 rows in set (0.02 sec)
select_type: select 類型
table: 輸出結果集的表
type: 表示表的連接類型
當表中僅有一行是type的值爲system是最佳的連接類型;
當select操作中使用索引進行表連接時type的值爲ref;
當select的表連接沒有使用索引時,經常會看到type的值爲ALL,表示對該表
進行了全表掃描,這時需要考慮通過創建索引來提高表連接的效率。
possible_keys: 表示查詢時,可以使用的索引列.
key: 表示使用的索引
key_len: 索引長度
rows: 掃描範圍
Extra: 執行情況的說明和描述
確定問題,並採取相應的優化措施:
經過以上步驟,基本可以確認問題出現的原因,可以根據情況採取相應的措施,進行優化提高執行的效率。
例如上面的例子,我們確認是對a表的全表掃描導致效率的不理想,我們對a表的year字段創建了索引,查詢需要掃描的行數明顯較少。
mysql> explain select sum(moneys) from sales a,companys b where a.company_id =
b.id and a.year = 2006;
+----------------+----------+-----------+----------------+----------------+---------
-+-----------+----------------+
| select_type | table | type | possible_keys| key | key_len | rows
| Extra |
+----------------+----------+-----------+----------------+----------------+---------
-+-----------+----------------+
| SIMPLE | b | index | PRIMARY | PRIMARY | 4 | 1 | Using index
|
| SIMPLE | a | ref | year | year | 4 | 3 | Using
where |
+----------------+----------+-----------+----------------+----------------+---------
-+-----------+----------------+
2 rows in set (0.02 sec)
索引問題
索引的存儲分類
myisam表的數據文件和索引文件是自動分開的; innodb的數據和索引是存儲在同一個表空間裏面,但可以有多個文件組成。
創建索引語法如下:
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
[USING index_type]
ON tbl_name (index_col_name,...)
index_col_name:
col_name [(length)] [ASC | DESC]
索引的存儲類型目前只有兩種( btree和hash) ,具體和表的模式相關:
myisam btree
innodb btree
memory/heap hash, btree
mysql目前不支持函數索引,只能對列的前一部分( length)進行索引,例:
create index ind_test on table1(name(5)),對於char和varchar列,使用前綴索引將大大節省空間。
MySQL如何使用索引
索引用於快速找出在某個列中有一特定值的行。對相關列使用索引是提高SELECT操作性能的最佳途徑。
查詢要使用索引最主要的條件是查詢條件中需要使用索引關鍵字, 如果是多列索引,那麼只有查詢條件使用了多列關鍵字最左邊的前綴時,纔可以使用索引,否則將不能使用索引。
下列情況下, Mysql不會使用已有的索引:
1. 如果mysql估計使用索引比全表掃描更慢,則不使用索引。例如:如果key_part1均勻分佈在1和100之間,下列查詢中使用索引就不是很好:
SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90
2. 如果使用heap表並且where條件中不用=索引列,其他> 、 <、 >=、 <=均不使用索引;
3. 如果不是索引列的第一部分;
4. 如果like是以%開始;
5. 對where後邊條件爲字符串的一定要加引號, 字符串如果爲數字mysql會自動轉爲字符串,但是不使用索引。
查看索引使用情況
如果索引正在工作, Handler_read_key的值將很高,這個值代表了一個行被索引值讀的次數, 很低的值表明增加索引得到的性能改善不高, 因爲索引並不經常使用。
Handler_read_rnd_next的值高則意味着查詢運行低效,並且應該建立索引補救。這個值的含義是在數據文件中讀下一行的請求數。如果你正進行大量的表掃描,
該值較高。通常說明表索引不正確或寫入的查詢沒有利用索引。
語法:
mysql> show status like 'Handler_read%';
兩個簡單實用的優化方法
1 定期分析表:
ANALYZE TABLE
語法:
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
本語句用於分析和存儲表的關鍵字分佈。 在分析期間, 使用一個讀取鎖對錶進行鎖定。 這對於MyISAM, BDB和InnoDB表有作用。 對於MyISAM表,本語句與使用myisamchk -a相當。
CHECK TABLE
語法:
CHECK TABLE tbl_name [, tbl_name] ... [option] ...
option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}
檢查一個或多個表是否有錯誤。 CHECK TABLE對MyISAM和InnoDB表有作用。對於MyISAM表,關鍵字統計數據被更新。CHECK TABLE也可以檢查視圖是否有錯誤,比如在視圖定義中被引用的表已不存在。
CHECKSUM TABLE
語法:
CHECKSUM TABLE tbl_name [, tbl_name] ... [ QUICK | EXTENDED ]
報告一個表校驗和。
2 使用 optimize table:
OPTIMIZE TABLE
語法:
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
如果已經刪除了表的一大部分,或者如果您已經對含有可變長度行的表(含有VARCHAR, BLOB或TEXT列的表)進行了很多更改,則應使用OPTIMIZE TABLE。
被刪除的記錄被保持在鏈接清單中,後續的INSERT操作會重新使用舊的記錄位置。您可以使用OPTIMIZE TABLE來重新利用未使用的空間,並整理數據文件的碎片。
OPTIMIZE TABLE只對MyISAM, BDB和InnoDB表起作用。
常用SQL的優化
1 大批量插入數據:
1. 對於Myisam類型的表,可以通過以下方式快速的導入大量的數據。
ALTER TABLE tblname DISABLE KEYS;
loading the data
ALTER TABLE tblname ENABLE KEYS;
這兩個命令用來打開或者關閉Myisam表非唯一索引的更新。在導入大量的數據到一個非空的Myisam表時,通過設置這兩個命令,可以提高導入的效率。
對於導入大量數據到一個空的Myisam表,默認就是先導入數據然後才創建索引的,所以不用進行設置。
2. 而對於Innodb類型的表, 這種方式並不能提高導入數據的效率。 對於Innodb類型的表,我們有以下幾種方式可以提高導入的效率:
a. 因爲Innodb類型的表是按照主鍵的順序保存的,所以將導入的數據按照主鍵的順序排列,可以有效的提高導入數據的效率。如果Innodb表沒有主鍵,那麼系統會默認
創建一個內部列作爲主鍵, 所以如果可以給表創建一個主鍵, 將可以利用這個優勢提高導入數據的效率。
b. 在導入數據前執行SET UNIQUE_CHECKS=0, 關閉唯一性校驗, 在導入結束後執行SET UNIQUE_CHECKS=1,恢復唯一性校驗,可以提高導入的效率。
c. 如果應用使用自動提交的方式,建議在導入前執行SET AUTOCOMMIT=0,關閉自動提交,導入結束後再執行SET AUTOCOMMIT=1,打開自動提交,也可以提高導入的效率。
2 優化 insert語句:
3.如果你同時從同一客戶端插入很多行,使用多個值表的INSERT語句。這比使用分開INSERT語句快(在一些情況中幾倍)。
Insert into test values(1,2),(1,3),(1,4)…
4.如果你從不同客戶端插入很多行, 能通過使用INSERT DELAYED語句得到更高的速度。Delayed的含義是讓insert 語句馬上執行,其實數據都被放在內存的隊列中,並
沒有真正寫入磁盤;這比每條語句分別插入要快的多; LOW_PRIORITY剛好相反,在所有其他用戶對錶的讀寫完後才進行插入;
5. 將索引文件和數據文件分在不同的磁盤上存放(利用建表中的選項) ;
6. 如果進行批量插入,可以增加bulk_insert_buffer_size變量值的方法來提高速度,但是,這隻能對myisam表使用;
7. 當從一個文本文件裝載一個表時,使用LOAD DATA INFILE。這通常比使用很多INSERT語句快20倍;
8. 根據應用情況使用replace語句代替insert;
9. 根據應用情況使用ignore關鍵字忽略重複記錄。
3 優化 groupby語句:
默認情況下, MySQL排序所有GROUP BY col1, col2, ....。查詢的方法如同在查詢中指定ORDER BY col1, col2, ...。如果顯式包括一個包含相同的列的ORDER BY
子句, MySQL可以毫不減速地對它進行優化,儘管仍然進行排序。
如果查詢包括GROUP BY但你想要避免排序結果的消耗,你可以指定ORDER BY NULL禁止排序。
例如:
INSERT INTO foo SELECT a, COUNT(*) FROM bar GROUP BY a ORDER BY NULL;
4 優化 order by語句:
在某些情況中, MySQL可以使用一個索引來滿足ORDER BY子句,而不需要額外的排序。where條件和order by使用相同的索引, 並且order 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;
但是以下情況不使用索引:
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:
5 優化 join語句:
Mysql4.1開始支持SQL的子查詢。這個技術可以使用SELECT語句來創建一個單列的查詢結果, 然後把這個結果作爲過濾條件用在另一個查詢中。 使用子查詢可以一次性
的完成很多邏輯上需要多個步驟才能完成的SQL操作, 同時也可以避免事務或者表鎖死, 並且寫起來也很容易。 但是, 有些情況下, 子查詢可以被更有效率的連接(JOIN)..替代。
假設我們要將所有沒有訂單記錄的用戶取出來,可以用下面這個查詢完成:
SELECT * FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID
FROM salesinfo )
如果使用連接(JOIN).. 來完成這個查詢工作, 速度將會快很多。 尤其是當salesinfo表中對CustomerID建有索引的話,性能將會更好,查詢如下:
SELECT * FROM customerinfo
LEFT JOIN salesinfo ON customerinfo.CustomerID=salesinfo.CustomerID
WHERE salesinfo.CustomerID IS NULL
連接(JOIN).. 之所以更有效率一些,是因爲 MySQL不需要在內存中創建臨時表來完成這個邏輯上的需要兩個步驟的查詢工作。
6 mysql如何優化 or條件:
對於or子句,如果要利用索引,則or之間的每個條件列都必須用到索引;如果沒有索引,則應該考慮增加索引。
7 查詢優先還是更新( insert、 update、 delete)優先:
MySQL還允許改變語句調度的優先級,它可以使來自多個客戶端的查詢更好地協作, 這樣單個客戶端就不會由於鎖定而等待很長時間。 改變優先級還可以確保特定類型的查詢被處理得更快。
我們首先應該確定應用的類型, 判斷應用是以查詢爲主還是以更新爲主的, 是確保查詢效率還是確保更新的效率,決定是查詢優先還是更新優先。
下面我們提到的改變調度策略的方法主要是針對Myisam存儲引擎的,對於Innodb存儲引擎,語句的執行是由獲得行鎖的順序決定的。
MySQL的默認的調度策略可用總結如下:
1. 寫入操作優先於讀取操作。
2. 對某張數據表的寫入操作某一時刻只能發生一次, 寫入請求按照它們到達的次序來處理。
3. 對某張數據表的多個讀取操作可以同時地進行。
MySQL提供了幾個語句調節符,允許你修改它的調度策略:
1. LOW_PRIORITY關鍵字應用於DELETE、 INSERT、 LOAD DATA、 REPLACE和UPDATE。
2. HIGH_PRIORITY關鍵字應用於SELECT和INSERT語句。
3. DELAYED關鍵字應用於INSERT和REPLACE語句。
如果寫入操作是一個LOW_PRIORITY(低優先級)請求,那麼系統就不會認爲它的優先級高於讀取操作。在這種情況下,如果寫入者在等待的時候,第二個讀取者到達了,那麼
就允許第二個讀取者插到寫入者之前。 只有在沒有其它的讀取者的時候, 才允許寫入者開始操作。這種調度修改可能存在LOW_PRIORITY寫入操作永遠被阻塞的情況。
SELECT查詢的HIGH_PRIORITY(高優先級)關鍵字也類似。它允許SELECT插入正在等待的寫入操作之前,即使在正常情況下寫入操作的優先級更高。另外一種影響是,高優先級的SELECT在正常的SELECT語句之前執行,因爲這些語句會被寫入操作阻塞。
如果你希望所有支持LOW_PRIORITY選項的語句都默認地按照低優先級來處理,那麼請使用--low-priority-updates選項來啓動服務器。通過使用 INSERTHIGH_PRIORITY來把INSERT語句提高到正常的寫入優先級,可以消除該選項對單個INSERT語句的影響。
8 使用 SQL提示:
SELECTSQL_BUFFER_RESULTS...
將強制MySQL生成一個臨時結果集。只要所有臨時結果集生成後,所有表上的鎖定均被釋放。這能在遇到表鎖定問題時或要花很長時間將結果傳給客戶端時有所幫助。
當處理一個會讓客戶端耗費點時間才能處理的大結果集時,可以考慮使用SQL_BUFFER_RESULT 提示字。這樣可以告訴MySQL將結果集保存在一個臨時表中, 這樣可以儘早的釋放各種鎖。
USE INDEX
在你查詢語句中表名的後面,添加 USEINDEX 來提供你希望MySQ去參考的索引列表,就可以讓MySQL不再考慮其他可用的索引。
Eg:SELECT * FROM mytable USE INDEX(mod_time,name)...
IGNORE INDEX
如果你只是單純的想讓MySQL忽略一個或者多個索引,可以使用IGNORE INDEX作爲Hint。
Eg:SELECT * FROM mytale IGNORE INDEX(priority)...
FORCE INDEX
爲強制MySQL使用一個特定的索引,可在查詢中使用FORCE INDEX作爲Hint。
Eg:SELECT * FROM mytable FORCE INDEX(mod_time)...
9 其他優化措施
1. 使用持久的連接數據庫以避免連接開銷。
2. 經常檢查所有查詢確實使用了必要的索引。
3. 避免在頻繁更新的表上執行復雜的 SELECT查詢,以避免與鎖定表有關的由於讀、寫衝突發生的問題。
4. 對於沒有刪除的行操作 的MyISAM表,插入操作和查詢操作可以並行進行,因爲沒有刪除操作的表查詢期間不會阻塞插入操作. 對於確實需要執行刪除操作的表, 儘量在空閒時間進行批量刪除操作,避免阻塞其他操作。
5. 充分利用列有默認值的事實。只有當插入的值不同於默認值時,才明確地插入值。這減少MySQL需要做的語法分析從而提高插入速度。
6. 對經常訪問的可以重構的數據使用內存表,可以顯著提高訪問的效率。
7. 通過複製可以提高某些操作的性能。可以在複製服務器中分佈客戶的檢索以均分負載。爲了防止備份期間對應用的影響,可以在複製服務器上執行備份操作。
8. 表的字段儘量不使用自增長變量, 在高併發情況下該字段的自增可能對效率有比較大的影響,推薦通過應用來實現字段的自增長。