2013年6月14日 星期五

MySQL優化全攻略-相關數據庫命令

0

在MySQL我們在使用SELECT做撈取資料的時候,有時候常常會效能低落,撈取資料需要很長的時間,有時候是SQL語法下得不好導致沒有使用到正確的索引去撈資料,我們這個時候就必須要檢查我們下的SQL語法到底有哪些地方需要改善。


▲ EXPLAIN
 EXPLAIN能夠分析SELECT命令的處理過程。這不僅對於決定是否要為表加上索引很有用,而且對於了解MySQL處理複雜連接的過程也很有用。

而 EXPLAIN 後的資料有下面這些欄位

  1. select_type
  2. table:關連到的資料表
  3. type:使用關聯查詢的類型(效率由好至壞排序)
  4. System
  5. const
  6. eq_ref
  7. ref
  8. fulltext
  9. ref_or_null
  10. index_merge
  11. unique_subquery
  12. index_subquery
  13. range
  14. index
  15. ALL



  • possible_keys:可能使用到的索引,從WHERE語法選擇出一個適合的欄位
  • key:實際使用到的索引,如果為NULL,則是沒有使用索引
  • key_len:使用索引的長度,長度越短 準確性越高
  • ref:顯示那一列的索引被使用,一般是一個常數(const)
  • rows:MySQL用來返回資料的筆數,可以簡單的把rows視為執行效能,越少越好
  • Extra:MySQL用來解析額外的查詢訊息
  • Distinct:當MySQL找到相關連的資料時,就不再搜尋。
  • Not exists:MySQL優化 LEFT JOIN,一旦找到符合的LEFT JOIN資料後,就不再搜尋。
  • Range checked for each Record(index map:#):無法找到理想的索引。此為最慢的使用索引。
  • Using filesort:當出現這個值時,表示此SELECT語法需要優化。因為MySQL必須進行額外的步驟來進行查詢。
  • Using index:返回的資料是從索引中資料,而不是從實際的資料中返回,當返回的資料都出現在索引中的資料時就會發生此情況。
  • Using temporary:同Using filesort,表示此SELECT語法需要進行優化。此為MySQL必須建立一個暫時的資料表(Table)來儲存結果,此情況會發生在針對不同的資料進行ORDER BY,而不是GROUP BY。
  • Using where:使用WHERE語法中的欄位來返回結果。
  • System:system資料表,此為const連接類型的特殊情況。
  • Const:資料表中的一個記錄的最大值能夠符合這個查詢。因為只有一行,這個值就是常數,因為MySQL會先讀這個值然後把它當做常數。
  • eq_ref:MySQL在連接查詢時,會從最前面的資料表,對每一個記錄的聯合,從資料表中讀取一個記錄,在查詢時會使用索引為主鍵或唯一鍵的全部。
  • ref:只有在查詢使用了非唯一鍵或主鍵時才會發生。
  • range:使用索引返回一個範圍的結果。例如:使用大於>或小於<查詢時發生。
  • index:此為針對索引中的資料進行查詢。
  • ALL:針對每一筆記錄進行完全掃描,此為最壞的情況,應該盡量避免。






下面這個例子顯示瞭如何用EXPLAIN提供的信息逐步地優化連接查詢。 (本例來自MySQL文檔,見

http://www.mysql.com/doc/E/X/EXPLAIN.html。原文寫到這裡似乎有點潦草了事,特加上此例。)

假定用EXPLAIN分析的SELECT命令如下所示:
EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
        tt.ProjectReference, tt.EstimatedShipDate,
        tt.ActualShipDate, tt.ClientID,
        tt.ServiceCodes, tt.RepetitiveID,
        tt.CurrentProcess, tt.CurrentDPPerson,
        tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
        et_1.COUNTRY, do.CUSTNAME
      FROM tt, et, et AS et_1, do
      WHERE tt.SubmitTime IS NULL
        AND tt.ActualPC = et.EMPLOYID
        AND tt.AssignedPC = et_1.EMPLOYID
        AND tt.ClientID = do.CUSTNMBR;


SELECT命令中出現的表定義如下:

※表定義

表列列類型
tt ActualPC CHAR(10)
 tt AssignedPC CHAR(10)
 tt ClientID CHAR(10)
 et EMPLOYID CHAR(15)
 do CUSTNMBR CHAR(15)
 

※索引

表索引
tt ActualPC
 tt AssignedPC
 tt ClientID
 et EMPLOYID (主鍵)
do CUSTNMBR (主鍵)
※tt.ActualPC值分佈不均勻

在進行任何優化之前,EXPLAIN對SELECT執行分析的結果如下:
table type possible_keys key key_len ref rows Extra
et ALL PRIMARY NULL NULL NULL 74
do ALL PRIMARY NULL NULL NULL 2135
et_1 ALL PRIMARY NULL NULL NULL 74
tt ALL AssignedPC,ClientID,ActualPC NULL NULL NULL 3872 range checked for each record (key map: 35)


每一個表的type都是ALL,它表明MySQL為每一個表進行了完全連接!這個操作是相當耗時的,因為待處理行的數量達到每一個表行數的乘積!

即,這裡的總處理行數為74 * 2135 * 74 * 3872 = 45,268,558,720。

這裡的問題之一在於,如果數據庫列的聲明不同,MySQL(還)不能有效地運用列的索引。在這個問題上,VARCHAR和CHAR是一樣的,除非它們

聲明的長度不同。由於tt.ActualPC聲明為CHAR(10),而et.EMPLOYID聲明為CHAR(15),因此這裡存在列長度不匹配問題。

為了解決這兩個列的長度不匹配問題,用ALTER TABLE命令把ActualPC列從10個字符擴展到15字符,如下所示:
mysql > ALTER TABLE tt MODIFY ActualPC VARCHAR(15);



現在tt.ActualPC和et.EMPLOYID都是VARCHAR(15)了,執行EXPLAIN進行分析得到的結果如下所示:
table type possible_keys key key_len ref rows Extra
tt ALL AssignedPC,ClientID,ActualPC NULL NULL NULL 3872 where used
do ALL PRIMARY NULL NULL NULL 2135 range checked for each record (key map: 1)
et_1 ALL PRIMARY NULL NULL NULL 74 range checked for each record (key map: 1)
et eq_ref PRIMARY PRIMARY 15 tt.ActualPC 1

 �
這還算不上完美,但已經好多了(行數的乘積現在少了一個係數74)。現在這個SQL命令執行大概需要數秒鐘時間。

為了避免tt.AssignedPC = et_1.EMPLOYID以及tt.ClientID = do.CUSTNMBR比較中的列長度不匹配,我們可以進行如下改動:
mysql > ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),
             MODIFY ClientID VARCHAR(15);

 �
現在EXPLAIN顯示的結果如下:
table type possible_keys key key_len ref rows Extra
et ALL PRIMARY NULL NULL NULL 74
tt ref AssignedPC,ClientID,ActualPC ActualPC 15 et.EMPLOYID 52 where used
et_1 eq_ref PRIMARY PRIMARY 15 tt.AssignedPC 1
do eq_ref PRIMARY PRIMARY 15 tt.ClientID 1

這個結果已經比較令人滿意了。
 餘下的問題在於,默認情況下,MySQL假定tt.ActualPC列的值均勻分佈,而事實上tt表的情況並非如此。幸而,我們可以很容易地讓MySQL知

道這一點:
shell > myisamchk –analyze PATH_TO_MYSQL_DATABASE/tt
 shell > mysqladmin refresh

 �
現在這個連接操作已經非常理想,EXPLAIN分析的結果如下:

table type possible_keys key key_len ref rows Extra
tt ALL AssignedPC,ClientID,ActualPC NULL NULL NULL 3872 where used
et eq_ref PRIMARY PRIMARY 15 tt.ActualPC 1
et_1 eq_ref PRIMARY PRIMARY 15 tt.AssignedPC 1
do eq_ref PRIMARY PRIMARY 15 tt.ClientID 1

接下來我們要討論的是數據庫性能優化的另一方面,即運用數據庫服務器內建的工具輔助性能分析和優化。

▲ SHOW

執行下面這個命令可以了解服務器的運行狀態:

mysql >show status;

該命令將顯示出一長列狀態變量及其對應的值,其中包括:被中止訪問的用戶數量,被中止的連接數量,嘗試連接的次數,並發連接數量最大

值,以及其他許多有用的信息。這些信息對於確定係統問題和效率低下的原因是十分有用的。

SHOW命令除了能夠顯示出MySQL服務器整體狀態信息之外,它還能夠顯示出有關日誌文件、指定數據庫、表、索引、進程和許可權限表的寶貴

信息。請訪問http://www.mysql.com/doc/S/H/SHOW.html了解更多信息。


OPTIMIZE

OPTIMIZE能夠恢復和整理磁盤空間以及數據碎片,一旦對包含變長行的表進行了大量的更新或者刪除,進行這個操作就非常有必要了。

OPTIMIZE當前只能用於MyISAM和BDB表。

結束語:從編譯數據庫服務器開始、貫穿整個管理過程,能夠改善MySQL性能的因素實在非常多,本文只涉及了其中很小的一部分。儘管如此


http://blog.kejyun.com/2012/12/Using-EXPLAIN-SQL-To-Analysis-Efficient-On-MySQL.html

http://blog.itcert.org/archives/1156

http://www.devshed.com/Server_Side/MySQL/Optimize/

http://www.chinabyte.com/builder/detail.shtm?buiid=1012&parid=1

0 意見:

張貼留言