日常工作中常見的mysql優化技巧
1.介紹一下MYSQL常用的優化技巧.
MySQL 自帶 slow log 的分析工具 mysqldumpslow ,但是沒有說明。本文通過分析該腳本,介紹了其用法。
slow log 是 MySQL 根據 SQL 語句的執行時間設定,寫入的一個文件,用於分析執行較慢的語句。
只要在 my.cnf 文件中配置好:
log-slow-queries = [slow_query_log_filename]
即可記錄超過默認的 10s 執行時間的 SQL 語句。
如果要修改默認設置,可以添加:
long_query_time = 5
設定為 5s 。
如果要記錄所有 SQL 語句,可以寫入:
log-long-format
# t=time, l=lock time, r=rows
# at, al, 以及 ar 是對應的平均值
mysqldumpslow 可以接受的參數有:
‘v+’, # verbose
‘d+’, # debug
‘s=s’, # 排序 (t, at, l, al, r, ar etc)
‘r!’, # 倒排序 (largest last instead of first)
‘t=i’, # 顯示最高的 n 個查詢
‘a!’, # 不把所有的數字以 N ,字符串以 ‘S’ 顯示
‘n=i’, # abstract numbers with at least n digits within names
‘g=s’, # grep: only consider stmts that include this string
‘h=s’, # hostname of db server for *-slow.log filename (can be wildcard)
‘i=s’, # name of server instance (if using mysql.server startup script)
‘l!’, # don’t subtract lock time from total time
mysql explain的使用說明
explain顯示了mysql如何使用索引來處理select語句以及連接表。可以幫助選擇更好的索引和寫出更優化的查詢語句。
使用方法,在select語句前加上explain就可以了:
如:explain select surname,first_name form a,b where a.id=b.id
分析結果形式如下:
引用
mysql> explain SELECT * FROM `whisper` WHERE to_id = 6696 AND del = 0 AND whisper=0 ORDER BY `send_time` DESC LIMIT 4;
+—-+————-+———+——+—————+——-+———+——-+——+—————————–+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+————-+———+——+—————+——-+———+——-+——+—————————–+
| 1 | SIMPLE | whisper | ref | to_id | to_id | 4 | const | 1 | Using where; Using filesort |
+—-+————-+———+——+—————+——-+———+——-+——+—————————–+
1 row in set (0.00 sec)
EXPLAIN列的解釋:
table 顯示這一行的數據是關於哪張表的
type 這是重要的列,顯示連接使用了何種類型。從最好到最差的連接類型為const、eq_reg、ref、range、indexhe和ALL
possible_keys 顯示可能應用在這張表中的索引。如果為空,沒有可能的索引。可以為相關的域從WHERE語句中選擇一個合適的語句
key 實際使用的索引。如果為NULL,則沒有使用索引。很少的情況下,MYSQL會選擇優化不足的索引。這種情況下,可以在SELECT語句中使用USE INDEX(indexname)來強制使用一個索引或者用IGNORE INDEX(indexname)來強制MYSQL忽略索引
key_len 使用的索引的長度。在不損失精確性的情況下,長度越短越好
ref 顯示索引的哪一列被使用了,如果可能的話,是一個常數
rows MYSQL認為必須檢查的用來返回請求數據的行數
Extra 關於MYSQL如何解析查詢的額外信息。將在表4.3中討論,但這裡可以看到的壞的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,結果是檢索會很慢
extra 列返回的描述的意義
Distinct 一旦MYSQL找到了與行相聯合匹配的行,就不再搜索了
Not exists MYSQL優化了LEFT JOIN,一旦它找到了匹配LEFT JOIN標准的行,就不再搜索了
Range checked for each
Record(index map:#)沒有找到理想的索引,因此對於從前面表中來的每一個行組合,MYSQL檢查使用哪個索引,並用它來從表中返回行。這是使用索引的最慢的連接之一
Using filesort 看到這個的時候,查詢就需要優化了。MYSQL需要進行額外的步驟來發現如何對返回的行排序。它根據連接類型以及存儲排序鍵值和匹配條件的全部行的行指針來排序全部行
Using index 列數據是從僅僅使用了索引中的信息而沒有讀取實際的行動的表返回的,這發生在對表的全部的請求列都是同一個索引的部分的時候
Using temporary 看到這個的時候,查詢需要優化了。這裡,MYSQL需要創建一個臨時表來存儲結果,這通常發生在對不同的列集進行ORDER BY上,而不是GROUP BY上
Where used 使用了WHERE從句來限制哪些行將與下一張表匹配或者是返回給用戶。如果不想返回表中的全部行,並且連接類型ALL或index,這就會發生,或者是查詢有問題
不同連接類型的解釋(按照效率高低的順序排序)
system 表只有一行:system表。這是const連接類型的特殊情況
const 表中的一個記錄的最大值能夠匹配這個查詢(索引可以是主鍵或惟一索引)。因為只有一行,這個值實際就是常數,因為MYSQL先讀這個值然後把它當做常數來對待
eq_ref 在連接中,MYSQL在查詢時,從前面的表中,對每一個記錄的聯合都從表中讀取一個記錄,它在查詢使用了索引為主鍵或惟一鍵的全部時使用
ref 這個連接類型只有在查詢使用了不是惟一或主鍵的鍵或者是這些類型的部分(比如,利用最左邊前綴)時發生。對於之前的表的每一個行聯合,全部記錄都將從表中讀出。這個類型嚴重依賴於根據索引匹配的記錄多少—越少越好
range 這個連接類型使用索引返回一個范圍中的行,比如使用>或<查找東西時發生的情況
index 這個連接類型對前面的表中的每一個記錄聯合進行完全掃描(比ALL更好,因為索引一般小於表數據)
ALL 這個連接類型對於前面的每一個記錄聯合進行完全掃描,這一般比較糟糕,應該盡量避免
mysql常用的hint(原創)
mysql常用的hint
對於經常使用oracle的朋友可能知道,oracle的hint功能種類很多,對於優化sql語句提供了很多方法。同樣,在mysql裡,也有類似的hint功能。下面介紹一些常用的。
[b]強制索引 FORCE INDEX[/b]
SELECT * FROM TABLE1 FORCE INDEX (FIELD1) …
以上的SQL語句只使用建立在FIELD1上的索引,而不使用其它字段上的索引。
[b]忽略索引 IGNORE INDEX[/b]
SELECT * FROM TABLE1 IGNORE INDEX (FIELD1, FIELD2) …
在上面的SQL語句中,TABLE1表中FIELD1和FIELD2上的索引不被使用。
[b]關閉查詢緩沖 SQL_NO_CACHE[/b]
SELECT SQL_NO_CACHE field1, field2 FROM TABLE1;
有一些SQL語句需要實時地查詢數據,或者並不經常使用(可能一天就執行一兩次),這樣就需要把緩沖關了,不管這條SQL語句是否被執行過,服務器都不會在緩沖區中查找,每次都會執行它。
[b]強制查詢緩沖 SQL_CACHE[/b]
SELECT SQL_CALHE * FROM TABLE1;
如果在my.ini中的query_cache_type設成2,這樣只有在使用了SQL_CACHE後,才使用查詢緩沖。
[b]優先操作 HIGH_PRIORITY[/b]
HIGH_PRIORITY可以使用在select和insert操作中,讓MYSQL知道,這個操作優先進行。
SELECT HIGH_PRIORITY * FROM TABLE1;
[b]滯後操作 LOW_PRIORITY[/b]
LOW_PRIORITY可以使用在insert和update操作中,讓mysql知道,這個操作滯後。
update LOW_PRIORITY table1 set field1= where field1= …
[b]延時插入 INSERT DELAYED[/b]
INSERT DELAYED INTO table1 set field1= …
INSERT DELAYED INTO,是客戶端提交數據給MySQL,MySQL返回OK狀態給客戶端。而這是並不是已經將數據插入表,而是存儲在內存裡面等待排隊。當mysql有 空余時,再插入。另一個重要的好處是,來自許多客戶端的插入被集中在一起,並被編寫入一個塊。這比執行許多獨立的插入要快很多。壞處是,不能返回自動遞增 的ID,以及系統崩潰時,MySQL還沒有來得及插入數據的話,這些數據將會丟失。
[b]強制連接順序 STRAIGHT_JOIN[/b]
SELECT TABLE1.FIELD1, TABLE2.FIELD2 FROM TABLE1 STRAIGHT_JOIN TABLE2 WHERE …
由上面的SQL語句可知,通過STRAIGHT_JOIN強迫MySQL按TABLE1、TABLE2的順序連接表。如果你認為按自己的順序比MySQL推薦的順序進行連接的效率高的話,就可以通過STRAIGHT_JOIN來確定連接順序。
[b]強制使用臨時表 SQL_BUFFER_RESULT[/b]
SELECT SQL_BUFFER_RESULT * FROM TABLE1 WHERE …
當我們查詢的結果集中的數據比較多時,可以通過SQL_BUFFER_RESULT.選項強制將結果集放到臨時表中,這樣就可以很快地釋放MySQL的表鎖(這樣其它的SQL語句就可以對這些記錄進行查詢了),並且可以長時間地為客戶端提供大記錄集。
[b]分組使用臨時表 SQL_BIG_RESULT和SQL_SMALL_RESULT[/b]
SELECT SQL_BUFFER_RESULT FIELD1, COUNT(*) FROM TABLE1 GROUP BY FIELD1;
一般用於分組或DISTINCT關鍵字,這個選項通知MySQL,如果有必要,就將查詢結果放到臨時表中,甚至在臨時表中進行排序。SQL_SMALL_RESULT比起SQL_BIG_RESULT差不多,很少使用。
查詢是數據庫技術中最常用的操作。查詢操作的過程比較簡單,首先從客戶端發出查詢的SQL語句,數據庫服務端在接收到由客戶端發來的 SQL語句後, 執行這條SQL語句,然後將查詢到的結果返回給客戶端。雖然過程很簡單,但不同的查詢方式和數據庫設置,對查詢的性能將會有很在的影響。
因此,本文就在MySQL中常用的查詢優化技術進行討論。討論的內容如:通過查詢緩沖提高查詢速度;MySQL對查詢的自動優化;基於索引的排序;不可達查詢的檢測和使用各種查詢選擇來提高性能。
一、 通過查詢緩沖提高查詢速度
一般我們使用SQL語句進行查詢時,數據庫服務器每次在收到客戶端發來SQL後,都會執行這條SQL語句。但當在一定間隔內(如1分鐘內),接到完 全一樣的SQL語句,也同樣執行它。雖然這樣可以保證數據的實時性,但在大多數時候,數據並不要求完全的實時,也就是說可以有一定的延時。如果是這樣的 話,在短時間內執行完全一樣的SQL就有些得不償失。
幸好MySQL為我們提供了查詢緩沖的功能(只能在MySQL 4.0.1及以上版本使用查詢緩沖)。我們可以通過查詢緩沖在一定程度上提高查詢性能。
我們可以通過在MySQL安裝目錄中的my.ini文件設置查詢緩沖。設置也非常簡單,只需要將query_cache_type設為1即可。在設 置了這個屬性後,MySQL在執行任何SELECT語句之前,都會在它的緩沖區中查詢是否在相同的SELECT語句被執行過,如果有,並且執行結果沒有過 期,那麼就直接取查詢結果返回給客戶端。但在寫SQL語句時注意,MySQL的查詢緩沖是區分大小寫的。如下列的兩條SELECT語句:
1. SELECT * from TABLE1
2.
3. SELECT * FROM TABLE1
上面的兩條SQL語句對於查詢緩沖是完全不同的SELECT。而且查詢緩沖並不自動處理空格,因此,在寫SQL語句時,應盡量減少空格的使用,尤其是在SQL首和尾的空格(因為,查詢緩沖並不自動截取首尾空格)。
雖然不設置查詢緩沖,有時可能帶來性能上的損失,但有一些SQL語句需要實時地查詢數據,或者並不經常使用(可能一天就執行一兩次)。這樣就需要把 緩沖關了。當然,這可以通過設置query_cache_type的值來關閉查詢緩沖,但這就將查詢緩沖永久地關閉了。在MySQL 5.0中提供了一種可以臨時關閉查詢緩沖的方法:
1. SELECT SQL_NO_CACHE field1, field2 FROM TABLE1
以上的SQL語句由於使用了SQL_NO_CACHE,因此,不管這條SQL語句是否被執行過,服務器都不會在緩沖區中查找,每次都會執行它。
我們還可以將my.ini中的query_cache_type設成2,這樣只有在使用了SQL_CACHE後,才使用查詢緩沖。
1. SELECT SQL_CALHE * FROM TABLE1
二、MySQL對查詢的自動優化
索引對於數據庫是非常重要的。在查詢時可以通過索引來提高性能。但有時使用索引反而會降低性能。我們可以看如下的SALES表:
1. CREATE TABLE SALES
2.
3. (
4.
5. ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
6.
7. NAME VARCHAR(100) NOT NULL,
8.
9. PRICE FLOAT NOT NULL,
10.
11. SALE_COUNT INT NOT NULL,
12.
13. SALE_DATE DATE NOT NULL,
14.
15. PRIMARY KEY(ID),
16.
17. INDEX (NAME),
18.
19. INDEX (SALE_DATE)
20.
21. );
假設這個表中保存了數百萬條數據,而我們要查詢商品號為1000的商品在2004年和2005年的平均價格。我們可以寫如下的SQL語句:
SELECT AVG(PRICE) FROM SALES
WHERE ID = 1000 AND SALE_DATE BETWEEN ’2004-01-01′ AND ’2005-12-31′;
如果這種商品的數量非常多,差不多占了SALES表的記錄的50%或更多。那麼使用SALE_DATE字段上索引來計算平均數就有些慢。因為如果使 用索引,就得對索引進行排序操作。當滿足條件的記錄非常多時(如占整個表的記錄的50%或更多的比例),速度會變慢,這樣還不如對整個表進行掃描。因 此,MySQL會自動根據滿足條件的數據占整個表的數據的比例自動決定是否使用索引進行查詢。
對於MySQL來說,上述的查詢結果占整個表的記錄的比例是30%左右時就不使用索引了,這個比例是MySQL的開發人員根據他們的經驗得出的。然而,實際的比例值會根據所使用的數據庫引擎不同而不同。
三、 基於索引的排序
MySQL的弱點之一是它的排序。雖然MySQL可以在1秒中查詢大約15,000條記錄,但由於MySQL在查詢時最多只能使用一個索引。因此,如果WHERE條件已經占用了索引,那麼在排序中就不使用索引了,這將大大降低查詢的速度。我們可以看看如下的SQL語句:
1. SELECT * FROM SALES WHERE NAME = “name” ORDER BY SALE_DATE DESC;
在以上的SQL的WHERE子句中已經使用了NAME字段上的索引,因此,在對SALE_DATE進行排序時將不再使用索引。為了解決這個問題,我們可以對SALES表建立復合索引:
1. ALTER TABLE SALES DROP INDEX NAME, ADD INDEX (NAME, SALE_DATE)
這樣再使用上述的SELECT語句進行查詢時速度就會大副提升。但要注意,在使用這個方法時,要確保WHERE子句中沒有排序字段,在上例中就是不能用SALE_DATE進行查詢,否則雖然排序快了,但是SALE_DATE字段上沒有單獨的索引,因此查詢又會慢下來。
四、 不可達查詢的檢測
在執行SQL語句時,難免會遇到一些必假的條件。所謂必假的條件是無論表中的數據如何變化,這個條件都為假。如WHERE value < 100 AND value > 200。我們永遠無法找到一個既小於100又大於200的數。
如果遇到這樣的查詢條件,再去執行這樣的SQL語句就是多此一舉。幸好MySQL可以自動檢測這種情況。如我們可以看看如下的SQL語句:
1. SELECT * FROM SALES WHERE NAME = “name1” AND NAME = “name2”
以上的查詢語句要查找NAME既等於name1又等於name2的記錄。很明顯,這是一個不可達的查詢,WHERE條件一定是假。MySQL在執行 SQL語句之前,會先分析WHERE條件是否是不可達的查詢,如果是,就不再執行這條SQL語句了。為了驗證這一點。我們首先對如下的SQL使用 EXPLAIN進行測試:
1. EXPLAIN SELECT * FROM SALES WHERE NAME = “name1”
上面的查詢是一個正常的查詢,我們可以看到使用EXPLAIN返回的執行信息數據中table項是SALES。這說明MySQL對SALES進行操作了。再看看下面的語句:
1. EXPLAIN SELECT * FROM SALES WHERE NAME = “name1” AND NAME = “name2”
我們可以看到,table項是空,這說明MySQL並沒有對SALES表進行操作。
五、 使用各種查詢選擇來提高性能
SELECT語句除了正常的使用外,MySQL還為我們提供了很多可以增強查詢性能的選項。如上面介紹的用於控制查詢緩沖的SQL_NO_CACHE和SQL_CACHE就是其中兩個選項。在這一部分,我將介紹幾個常用的查詢選項。
1. STRAIGHT_JOIN:強制連接順序
當我們將兩個或多個表連接起來進行查詢時,我們並不用關心MySQL先連哪個表,後連哪個表。而這一切都是由MySQL內部通過一系列的計算、評估,最後得出的一個連接順序決定的。如下列的SQL語句中,TABLE1和TABLE2並不一定是誰連接誰:
1. SELECT TABLE1.FIELD1, TABLE2.FIELD2 FROM TABLE1 ,TABLE2 WHERE …
如果開發人員需要人為地干預連接的順序,就得使用STRAIGHT_JOIN關鍵字,如下列的SQL語句:
1. SELECT TABLE1.FIELD1, TABLE2.FIELD2 FROM TABLE1 STRAIGHT_JOIN TABLE2 WHERE …
由上面的SQL語句可知,通過STRAIGHT_JOIN強迫MySQL按TABLE1、TABLE2的順序連接表。如果你認為按自己的順序比MySQL推薦的順序進行連接的效率高的話,就可以通過STRAIGHT_JOIN來確定連接順序。
2. 干預索引使用,提高性能
在上面已經提到了索引的使用。一般情況下,在查詢時MySQL將自己決定是否使用索引,使用哪一個索引。但在一些特殊情況下,我們希望MySQL只使用一個或幾個索引,或者不希望使用某個索引。這就需要使用MySQL的控制索引的一些查詢選項。
限制使用索引的范圍
有時我們在數據表裡建立了很多索引,當MySQL對索引進行選擇時,這些索引都在考慮的范圍內。但有時我們希望MySQL只考慮幾個索引,而不是全部的索引,這就需要用到USE INDEX對查詢語句進行設置。
1. SELECT * FROM TABLE1 USE INDEX (FIELD1, FIELD2) …
從以上SQL語句可以看出,無論在TABLE1中已經建立了多少個索引,MySQL在選擇索引時,只考慮在FIELD1和FIELD2上建立的索引。
限制不使用索引的范圍
如果我們要考慮的索引很多,而不被使用的索引又很少時,可以使用IGNORE INDEX進行反向選取。在上面的例子中是選擇被考慮的索引,而使用IGNORE INDEX是選擇不被考慮的索引。
1. SELECT * FROM TABLE1 IGNORE INDEX (FIELD1, FIELD2) …
在上面的SQL語句中,TABLE1表中只有FIELD1和FIELD2上的索引不被使用。
強迫使用某一個索引
上面的兩個例子都是給MySQL提供一個選擇,也就是說MySQL並不一定要使用這些索引。而有時我們希望MySQL必須要使用某一個索引(由於 MySQL在查詢時只能使用一個索引,因此只能強迫MySQL使用一個索引)。這就需要使用FORCE INDEX來完成這個功能。
1. SELECT * FROM TABLE1 FORCE INDEX (FIELD1) …
以上的SQL語句只使用建立在FIELD1上的索引,而不使用其它字段上的索引。
3. 使用臨時表提供查詢性能
當我們查詢的結果集中的數據比較多時,可以通過SQL_BUFFER_RESULT.選項強制將結果集放到臨時表中,這樣就可以很快地釋放MySQL的表鎖(這樣其它的SQL語句就可以對這些記錄進行查詢了),並且可以長時間地為客戶端提供大記錄集。
1. SELECT SQL_BUFFER_RESULT * FROM TABLE1 WHERE …
和SQL_BUFFER_RESULT.選項類似的還有SQL_BIG_RESULT,這個選項一般用於分組或DISTINCT關鍵字,這個選項通知MySQL,如果有必要,就將查詢結果放到臨時表中,甚至在臨時表中進行排序。
1. SELECT SQL_BUFFER_RESULT FIELD1, COUNT(*) FROM TABLE1 GROUP BY FIELD1