MYSQL系列筆記(三)——優化專題
一、首先mysql得先開啟慢查詢日誌。不會的可以網上查。
1.1 環境準備:
perl環境,因為mysql自帶的mysqldumpslow優化工具以及pt-query-digest工具分析都是perl檔案。
curl環境,因為mysql安裝版沒有帶pt-query-digest工具,所以想要使用該工具得先安裝,安裝可以通過curl命令直接安裝。就得先需要curl環境。
安裝perl環境只需要下載完成之後配置環境變數即可,跟jdk差不多,就不再記錄了。
curl環境安裝下載之後也配置環境變數即可。
1.2 mysqldumpslow因為是mysql完整版自帶的,只需要安裝了perl環境,去mysql目錄bin下面檢視有沒有mysqldumpslow這個檔名的pl檔案。
使用方式:mysqldumpslow /path(慢查詢日誌的路徑,一般在mysql目錄data裡面)。
可以使用mysqldumpslow --help 幫助指令檢視可以使用那些指令
經常使用的引數:
-s,是order的順序
al 平均鎖定時間
ar 平均返回記錄時間
at 平均查詢時間(預設)
c 計數
l 鎖定時間
r 返回記錄
t 查詢時間
-t,是top n的意思,即為返回前面多少條的資料
-g,後邊可以寫一個正則匹配模式,大小寫不敏感的
主要功能是, 統計不同慢sql的出現次數(Count),執行最長時間(Time),累計總耗費時間(Time),等待鎖的時間(Lock),傳送給客戶端的行總數(Rows),掃描的行總數(Rows)
1.3 pt-query-digest這個就需要安裝之後才能使用,pt-query-digest是用於分析mysql慢查詢的一個工具,它可以分析binlog、General log、slowlog,也可以通過SHOWPROCESSLIST或者通過tcpdump抓取的MySQL協議資料來進行分析。可以把分析結果輸出到檔案中,分析過程是先對查詢語句的條件進行引數化,然後對引數化以後的查詢進行分組統計,統計出各查詢的執行時間、次數、佔比等,可以藉助分析結果找出問題進行優化。
shell>pt-query-digest --help 常用引數: --create-review-table :當使用--review引數把分析結果輸出到表中時,如果沒有表就自動建立。 --create-history-table:當使用--history引數把分析結果輸出到表中時,如果沒有表就自動建立。 --filter : 對輸入的慢查詢按指定的字串進行匹配過濾後再進行分析 --limit:限制輸出結果百分比或數量,預設值是20,即將最慢的20條語句輸出,如果是95%則按總響應時間佔比從大到小排序,輸出到總和達到95%位置截止。 --log=s :指定輸出的日誌檔案 --history 將分析結果儲存到表中,分析結果比較詳細,下次再使用--history時,如果存在相同的語句,且查詢所在的時間區間和歷史表 中的不同,則會記錄到資料表中,可以通過查詢同一CHECKSUM來比較某型別查詢的歷史變化。 --review:將分析結果儲存到表中,這個分析只是對查詢條件進行引數化,一個型別的查詢一條記錄,比較簡單。 當下次使用--review時,如果存在相同的語句分析,就不會記錄到資料表中。 --output 分析結果輸出型別,值可以是report(標準分析報告)、slowlog(Mysql slow log)、json、json-anon,一般使用report,以便於閱讀。 --since:從該指定日期開始分析。 --until:截止時間,配合—since可以分析一段時間內的慢查詢。
二、使用explain關鍵字來查詢某條sql語句的執行計劃,當我們要去執行一條sql語句的時候,mysql首先是先分析了這個sql,再去執行,這個關鍵字就是用來查詢這個計劃,也相當於查詢這句sql的執行情況了。
舉例:
EXPLAIN SELECT * FROM customer;
下面這張圖片是這條sql語句執行的結果。
這些列的含義:
table:顯示這一行的資料是關於那張表的。
type:這是比較重要的一列,顯示連線使用了哪種型別,它更確切的說是一種資料庫引擎查詢表的一種方式,在《高效能mysql》一書中作者更是覺得稱呼它為訪問型別更貼切一些。從最好的到最差的:const、eq_reg、ref、range、index、ALL。後面一點會詳情說明
撇開sql的具體應用環境以及其他因素,你應當儘量優化你的sql語句,使它的type儘量靠左,但實際運用中還是要綜合考慮各個方面的。
possible_keys:顯示可能運用在這張表的索引,如果為空,沒有可能的索引。
key:實際使用的索引,如果為空,則是沒有使用索引。
key_len:使用的索引的長度,在不損失精確性的情況下,長度越短越好。
ref:顯示索引的那一列被使用了,如果可能的話,是一個常數。
rows:mysql認為必須檢查的用來返回資料的行數。
Extra:擴充套件列。有兩個值需要注意一下;1.Using filesort:(檔案排序,在order by中比較常見)看到這個的時候,查詢就需要優化了。MYSQL需要額外的步驟來發現如何對返回的行排序。它根據連線型別以及儲存排序鍵值和匹配條件的全部行的行指標來排序全部行。
Using temporary :這裡,MYSQL需要建立一個臨時表來儲存結果,這通常發生在對不同的列進行order by,而不是group by上。
再來試試帶了where條件並且條件是主鍵時候的查詢:
EXPLAIN SELECT * FROM customer WHERE customer_id='574';
可以看出來以上兩條sql語句的區別還是很大的。(如果條件不是主鍵列也可能得到之前的結果),主鍵是帶了索引的。
三、type詳解:
all:
這便是所謂的“全表掃描”,如果是展示一個數據表中的全部資料項,倒是覺得也沒什麼,如果是在一個查詢資料項的sql中出現了all型別,那通常意味著你的sql語句處於一種最原生的狀態,有很大的優化空間。
為什麼這麼說呢?因為all是一種非常暴力和原始的查詢方法,非常的耗時而且低效。用all去查詢資料就好比這樣的一個情形:S學校有倆萬人,我告訴你你給我找到小明,然後你怎麼做呢!你當然是把全校倆萬人挨個找一遍,即使你很幸運第一個人便找到了小明,但是你仍然不能停下,因為你無法確認是否有另外一個小明存在,直到你把倆萬人找完為止。所以,基本所有情況,我們都要避免這樣型別的查詢,除非你不得不這樣做。
index:
這種連線型別只是另外一種形式的全表掃描,只不過它的掃描順序是按照索引的順序。這種掃描根據索引然後回表取資料,和all相比,他們都是取得了全表的資料,而且index要先讀索引而且要回表隨機取資料,因此index不可能會比all快(取同一個表資料),但為什麼官方的手冊將它的效率說的比all好,唯一可能的原因在於,按照索引掃描全表的資料是有序的。這樣一來,結果不同,也就沒法比效率的問題了。
range:
range指的是有範圍的索引掃描,相對於index的全索引掃描,它有範圍限制,因此要優於index。關於range比較容易理解,需要記住的是出現了range,則一定是基於索引的。同時除了顯而易見的between,and以及’>’,’<'外,in和or也是索引範圍掃描。
ref:
出現該連線型別的條件是: 查詢條件列使用了索引而且不為主鍵和unique。其實,意思就是雖然使用了索引,但該索引列的值並不唯一,有重複。這樣即使使用索引快速查詢到了第一條資料,仍然不能停止,要進行目標值附近的小範圍掃描。但它的好處是它並不需要掃全表,因為索引是有序的,即便有重複值,也是在一個非常小的範圍內掃描。下面為了演示這種情形,給employee表中的name列新增一個普通的key(值允許重複)
const:
通常情況下,如果將一個主鍵放置到where後面作為條件查詢,mysql優化器就能把這次查詢優化轉化為一個常量。至於如何轉化以及何時轉化,這個取決於優化器。
type總結:
有很多東西和細節,想要明白清楚,也是沒有那麼簡單的,需要對作業系統以及資料庫的底層查詢和執行原理要有一個清楚的理解。同時type的幾種型別幾乎都是基於索引之上的,因此需要對索引有個深入的瞭解,而且explain的結果可以指導我們什麼時候加索引,什麼時候不加索引,從而讓我們更好的使用索引。
四、常見SQL優化:
以下資料我是在mysql官方demo資料庫sakila中執行的以sql_yong工具執行的。(下載在mysql官網的demo中)。
count()以及max()優化:
看到的出來,這是一個全表掃描的方式,這條sql語句IO消耗特別大,會拖慢伺服器的效率。需要掃描1W多行返回資料。而且也沒有索引。
那我們這種情況一般怎麼優化呢?
我們可以在要查詢的這一行建立索引:
再次執行查詢結果如下:
通過explain,我們可以看出現在這條sql並不需要實際的查詢得出資料,只需要通過索引就可以知道sql的執行結果。(缺點就是建立索引的時候也需要花費時間。)
count()舉例:
在一條SQL中同時查出2006年和2007年電影的數量。
select count(release_year=‘2006’ OR NULL) AS ‘2006年電影數量’,count(release_year=‘2007’ OR NULL) AS ‘2007年電影數量’ FROM film;
值得注意的地方是 count() 和count(列名)是有區別的,count()是要統計值為null的列,count(列名)是不會統計該列值為null的列。
子查詢的優化:
通常情況下,需要把子查詢優化為join查詢,但在優化時要注意關聯表是否存在一對多的關係,這時可能會出現重複資料,我們可以使用去重關鍵字 distinct。
優化limit查詢:
limit常用於分頁處理,時常會伴隨order by從句使用,所以可能會造成大量的IO問題。
優化方案1:使用有索引的列或主鍵進行order by操作。
優化方案2:記錄上次返回的主鍵,在下次查詢時使用主鍵過濾查詢 where id>=50 and id<=60 limit 1,10;
但是方案2有個缺點,主鍵一定得是順序排序的。
當我們主鍵不是順序自增的時候,這時候我們可以用一個小技巧:加一個額外列index_id,設成自增。再給這一列加上索引,查詢時候使用這一列即可。
五、索引的合理使用:
如何選擇合適的列建立索引:
1 在where,group by、order by、on等從句中出現的列。
2 索引欄位越小越好(每一頁欄位越大資料就越多IO就更多。)
3 離散度大的列放在聯合索引的前面(離散度:簡單來講:唯一值更多的)
索引的維護及優化:
重複索引:是指相同的列以相同的順序建立的同類型的索引。
冗餘索引:是指多個索引的字首列相同,或是在聯合索引中包含了主鍵索引。
過多的索引不但會影響我們寫入效率也會影響查詢的效率。
怎麼找出冗餘索引和重複索引呢?
1.可以使用Shlomi Noach的common_schema中的一些試圖來定位,common_schema是一系列可以安裝到伺服器上的常用的儲存和試圖。
2.可以使用Percona Toolkit中的pt_duplicate-key-checker,該工具通過分析表結構來找出冗餘和重複的索引。
pt_duplicate-key-checker跟pt-query-digest使用差不多 這個工具使用需要三個引數pt_duplicate-key-checker\ -u root \ -p 密碼\ -h 資料庫地址
。會得出重複索引的結果還有優化建議。
刪除不用了的索引:
可以通過慢查詢日誌配合pt-index-usage工具來進行索引使用情況的分析,具體可以百度這個工具的使用。 pt-index-usage\ -u root -p 123456 \ lizhi-slow.log
六、資料庫結構優化
選擇合適的資料型別:
1.使用可以存下你的資料的最小的資料型別。
2.使用簡單的資料型別。Int要比varchar型別在mysql處理上簡單。
3.儘可能的使用not null定義欄位。
4.儘量少用text型別,非用不可的時候可以考慮分表。
PS:如果使用int來儲存日期時間,要利用FROM_UNIXTIME()和UNIX_TIMESTAMP()這兩個函式,第一個函式意思是將int型別的時間戳轉換為時間格式,第二個是正常的日期時間格式資料轉換為int進行儲存。(在插入和查詢的時候直接用函式將值包起來)
可以使用bigint來儲存IP地址,利用INET_ATON(),INET_NTOA(),作用跟上面兩個函式差不多也是轉換的作用。
七、表的正規化化和反正規化化
正規化化是指資料庫設計的規範,一般遵循第三設計正規化就可以了,也就是要求資料表中不存在非關鍵欄位對任意候選關鍵欄位的傳遞函式依賴。
有點不好解釋,大概就是一個表有欄位是重複值很多的,修改一條可能會修改這個表大多數資料,優化方式就是拆分。將有些欄位拆分到另一個表,再進行中間表多對多關聯。
反正規化化優化:是指為了查詢效率的考慮把原本符合第三正規化的表適當的增加冗餘,以達到優化查詢效率的目的,就是空間換取時間的操作。也不用覺得資料庫設計不規範,一切設計都是建立在需求上面的。
下面這兩個圖是完全符合第三正規化設計規範的,但是我們查詢一個訂單所需要的資訊需要關聯四個表,但是查詢的欄位卻不是很多。
下面這兩個圖是我們反正規化化優化之後的結果,雖然表的欄位設計重複了,但是也只是新增的時候多增加幾個欄位,可我們在查詢的時候只需要查詢一個表就行了。
表的垂直拆分
垂直拆分,就是把原來一個有很多列的表拆分成多個表,這解決了表的寬度問題。可以按以下原則進行:
1.把不常用的欄位單獨存放到一個表中。
2.把大欄位單獨放到一個表中。
3.把經常一起使用的欄位放到一起。
表的水平拆分
表的水平拆分主要是為了解決單表的資料量過大的問題,水平拆分的表每一個表的結構都是完全一致的。
常用的水平拆分方法:
1.對id進行hash運算,如果要拆分成5個表則使用mod(id,5)取出0-4個值
2.針對不同的hashID把資料存到不同的表中。
隨之而來的也有問題:
1.跨分割槽表進行資料查詢
2.統計及後臺報表操作
我們可以講前後臺的業務分開,前臺查詢使用拆分之後的表,後臺統計就查詢幾個表彙總的表,雖然效率會比較低一點。
八、系統配置優化
資料庫是基於作業系統的,大多數專案MYSQL都是安裝在Linux系統上,所以對於作業系統的引數配置也會影響到MySQL的效能。
常見的系統配置:
MYSQL配置檔案:
MYSQL可以通過啟動時指定配置引數和使用配置檔案兩種方式進行配置,
如果存在多個位置存在配置檔案,則後面載入順序的會覆蓋前面的。
九、伺服器硬體優化。