索引

索引優化分析

簡單介紹

爲何要優化mysql

mysql 性能降低SQL慢、執行時間長、等待時間長
緣由:
1.查詢語句寫的爛
2.索引失效
  單值
  複合
3.關聯查詢太多join(設計缺陷或不得已的需求)
4.服務器調優及各個參數設置java

從幾個方面優化

  成本由低到高,效果卻由高到低:SQL及索引 –> 數據庫表結構 –> 系統配置 –> 硬件mysql

如何優化mysql

  1.觀察,至少跑1天,看看生產的慢SQL狀況
  2.開啓慢查詢日誌,設置閥值,好比超過5秒鐘的就是慢SQL,並將它抓取出來。
  3.explain+慢SQL分析
  4.show profile
  5.運維經理 Or DBA, 進行SQL數據庫服務器的參數調優。
總結
  1.慢查詢的開啓並捕獲
  2.explain+慢SQL分析
  3.show profile查詢SQL在MySQL服務器裏面的執行細節和生命週期狀況
  4.SQL數據庫服務器的參數調優。算法


常見通用的Join查詢

  常見通用的Join查詢詳細分析sql


索引簡介

是什麼

  MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構
  能夠獲得索引的本質:索引是數據結構
  可簡單理解爲「排好序的快速查找數據結構數據庫

數據自己以外,數據庫還維護着一個知足特定查找算法的數據結構,這些數據結構以某種方式指向數據,這樣就能夠在這些數據結構的基礎上實現高級查找算法,這種數據結構就是索引。性能優化

  通常來講索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲在磁盤上
  咱們日常所說的索引,若是沒有特別指明,都是B樹(多路搜索樹,並不必定是二叉的)結構組織的索引。其中彙集索引、次要索引、覆蓋索引、複合索引、前綴索引、惟一索引默認都是使用B+樹索引,統稱索引。固然,除了B+樹以外,還有哈希索引。服務器

優劣勢

優點:
  相似大學圖書館建書目索引,提升數據檢索的效率,下降數據庫的IO成本
  經過索引列對數據進行排序,下降數據排序的成本,下降了CPU的消耗數據結構

劣勢:
  實際上索引也是一張表,該表保存了主鍵與索引字段,並指向實體表的記錄,因此索引列也是要佔用空間的。
  雖然索引大大提升了查詢速度,同時卻會下降更新表的速度,如對錶進行insert,update和delete。由於更新表時,MySQL不只要保存數據,還要保存一下索引文件每次更新添加索引列的字段,都會調整由於更新所帶來的鍵值變化後的索引信息。
  索引只是提升效率的一個因素,若是你的MySQL有大數據量的表,就須要花時間 研究創建最優秀的索引,或優化查詢語句。併發

mysql索引結構

  主要有BTree索引、Hash索引、full-text全文索引、R-Tree索引。下面主要分析BTree索引
  備註:先說下,在MySQL文檔裏,其實是把B+樹索引寫成了BTREE,
  B+樹是一個平衡的多叉樹,從根節點到每一個葉子節點的高度差值不超過1,並且同層級的節點間有指針相互連接。
  在B+樹上的常規檢索,從根節點到葉子節點的搜索效率基本至關,不會出現大幅波動,並且基於索引的順序掃描時,也能夠利用雙向指針快速左右移動,效率很是高。

【初始化介紹】
  一顆b+樹,淺藍色的塊稱之爲一個磁盤塊,能夠看到每一個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示),如磁盤塊1包含數據項17和35,包含指針P1, P2, P3, P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。
  真實的數據存在於葉子節點即3,5,9,19…
  非葉子節點只不存儲真實的數據,只存儲指引搜索方向的數據項,如17,35並不真實存在於數據表中。運維

【查找過程】
  若是要查找數據項29,那麼首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,在內存中用二分查找肯定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間由於很是短(相比於磁盤的IO)能夠忽略不計。
  經過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO, 29在26和30以前,鎖定磁盤塊3的P2指針。
  經過指針加載磁盤塊8到內存,發生第三次IO,同時內存中作二分查找找到29,結束查詢,總計三次IO。

使用索引時機

1.哪些狀況須要建立索引
  1).主鍵自動創建惟一索引
  2).頻繁做爲查詢查詢條件的字段應該建立索引
  3).查詢中與其它表關聯的字段,外鍵關係創建索引
  4).頻繁更新的字段不適合建立索引
  5).where條件裏用不到的字段不建立索引
  6).單鍵/組合索引的選擇問題(在高併發下傾向建立組合索引)
  7).查詢中排序的字段,排序字段若經過索引去訪問將大大提升排序速度
  8).查詢中統計或者分組字段

2.哪些狀況不要建立索引
  1).表記錄太少
  2).常常增刪改的表(由於不只要保存數據,還要保存一下索引文件)
  3).數據重複且分佈平均的表字段,所以應該只爲最常常查詢和最常常排序的數據列創建索引。
  注意:若是某個數據列包含許多重複的內容,爲它創建索引就沒有太大的實際效果。


性能分析explain

  explain詳細分析


索引優化

Join語句的優化

二張表
  性能優化:left join是由左邊決定的,左邊必定都有,因此右邊是咱們的關鍵點,創建索引要建右邊邊。固然若是索引在左邊,能夠用右鏈接。

1
2
select * from atable
left join btable on atable.aid=btable.bid; // 最好在bid上建索引

 

結論:
  儘量減小Join語句中的NestedLoop的循環次數:「永遠用小結果集驅動大的結果集」

避免索引失效

  2.最佳左前綴法則:若是索引了多列,要尊守最左前綴法則,指的是查詢從索引的最左前列開始而且不跳過索引中的列。
  3.不在索引列上作任何操做(計算、函數、(自動or手動)類型轉換),會致使索引失效而轉向全表掃描。
  4.存儲引擎不能使用索引中範圍條件右邊的列。
  如這樣的sql: select * from user where username='123' and age>20 and phone='1390012345',其中username, age, phone都有索引,只有username和age會生效,phone的索引沒有用到。
  5.儘可能使用覆蓋索引(只訪問索引的查詢(索引列和查詢列致)),如select age from user減小select *
  6.mysql在使用不等於(!= 或者 <>)的時候沒法使用索引會致使全表掃描。
  7.is null, is not null 也沒法使用索引。
  8.like 以通配符開頭(‘%abc..’)mysql索引失效會變成全表掃描的操做。
  因此最好用右邊like 'abc%'。若是兩邊都要用,能夠用select age from user where username like '%abc%',其中age是索引列
  假如index(a,b,c), where a=3 and b like 'abc%' and c=4,a能用,b能用,c不能用
  9.字符串不加單引號索引失效
  10.少用or,用它來鏈接時會索引失效
  11.儘可能避免子查詢,而用join

通常性建議

  對於單鍵索引,儘可能選擇針對當前query過濾性更好的索引
  在選擇組合索引的時候,當前Query中過濾性最好的字段在索引字段順序中,位置越靠前越好
  在選擇組合索引的時候,儘可能選擇能夠可以包含當前query中的where子句中更多字段的索引
  儘量經過分析統計信息和調整query的寫法來達到選擇合適索引的目的。
left/right join注意
1).on與 where的執行順序
  ON 條件(「A LEFT JOIN B ON 條件表達式」中的ON)用來決定如何從 B 表中檢索數據行。若是 B 表中沒有任何一行數據匹配 ON 的條件,將會額外生成一行全部列爲 NULL 的數據,在匹配階段 WHERE 子句的條件都不會被使用。僅在匹配階段完成之後,WHERE 子句條件纔會被使用。它將從匹配階段產生的數據中檢索過濾。
  因此咱們要注意:在使用Left (right) join的時候,必定要在先給出儘量多的匹配知足條件,減小Where的執行。如:

2).注意ON 子句和 WHERE 子句的不一樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id)
AND product_details.id=2;
+----+--------+------+--------+-------+
| id | amount | id | weight | exist |
+----+--------+------+--------+-------+
| 1 | 100 | NULL | NULL | NULL |
| 2 | 200 | 2 | 22 | 0 |
| 3 | 300 | NULL | NULL | NULL |
| 4 | 400 | NULL | NULL | NULL |
+----+--------+------+--------+-------+
4 rows in set (0.00 sec)
 
mysql> SELECT * FROM product LEFT JOIN product_details
ON (product.id = product_details.id)
WHERE product_details.id=2;
+----+--------+----+--------+-------+
| id | amount | id | weight | exist |
+----+--------+----+--------+-------+
| 2 | 200 | 2 | 22 | 0 |
+----+--------+----+--------+-------+
1 row in set (0.01 sec)

 

  從上可知,第一條查詢使用 ON 條件決定了從 LEFT JOIN的 product_details表中檢索符合的全部數據行。第二條查詢作了簡單的LEFT JOIN,而後使用 WHERE 子句從 LEFT JOIN的數據中過濾掉不符合條件的數據行。


查詢截取分析

查詢優化

1.永遠小表驅動大表

  在java程序裏,兩個for循環,循環次數無論誰先誰後都是二者次數相乘。
  但在mysql的語句中,必定要小表驅動大表,由於小表跟Mysql鏈接和釋放數量少
如in與exists

1
2
3
4
5
6
7
8
9
10
11
12
13
select * from A where id in (select id form B)
等價於
for select id from B
for select * from A where A.id=B.id
結論:當B表的數據集必須小於A表的數據集時,用in優於exists。
 
select * from A where exists (select 1 from B where B.id=A.id) // 這裏的1用任何常量都行
等價於
for select * from A
for select * from B where B.id=A.id
結論:當A表的數據集必須小於B表的數據集時,用in優於exists。
 
注意:A表與B表的ID字段應創建索引

 

  exists通用語法select ... from table where exists (subquery)
  該語法能夠理解爲:將主查詢的數據,放到子查詢中作條件驗證,根據驗證結果(true或false)來決定主查詢的數據結果是否得以保留。
提示:
  1).exists(subquery)只返回true或false, 所以子查詢中select *也能夠是select 1select 'X', 官方說法是實際執行會忽略select 清單,所以沒有區別。
  2).exists 子查詢的實際執行過程可能通過了優化而不是咱們理解上的逐條對比,若是擔心效率問題,可進行實際檢驗。
  3).exists 子查詢每每也能夠用條件表達式、其餘子查詢或者JOIN來替代,何種最優須要具體問題具體分析。

2.order by 關鍵字優化

  1).order by子句,儘可能使用index方式排序,避免使用FileSort方式排序
  MySQL支持二種方式的排序,FileSort和Index,Index效率高,它指MySQL掃描索引自己完成排序。FileSort方式效率較低。(用explain能夠在extra字段裏看到Using index/filesort)
  Order By知足兩種狀況,會使用Index方式排序
   a.Order by語句使用索引最左前列
   b.使用where子句與Order by子句條件組合知足索引最左前列。
  
  2).儘量在索引列上完成排序操做,遵守索引建的最佳左前綴
  3).若是不在索引列上,fileSort有兩種算法:mysql就要啓動雙路排序和單路排序
  雙路排序:MySQL4.1以前是使用雙路排序,字面意思就是 兩次掃描磁盤,最終獲得數據,讀取行指針和orderby列,對他們進行排序,而後掃描已經排序好的列表,按照列表中的值從新從列表中讀取對應的數據輸出。從磁盤取排序字段,在buffer進行排序,再從磁盤取其餘字段。取一批數據,要對磁盤進行了兩次掃描,衆所周知,I/O是很耗時的,因此在mysql4.1後,出現了改進算法,就是單路排序
  單路排序:從磁盤讀取查詢須要的全部列,按照order by列在buffer對它們進行排序,而後掃描排序後的列表進行輸出,它的效率更快一些,避免了第二次讀取數據。而且把隨機IO變成了順序IO,可是它會使用更多的空間,由於它把每一行都保存在內存中了。
  結論及引伸出的問題:因爲單路是後出的,整體而言好過雙路,可是單路也有問題。

單路的問題
  在sort_buffer中,方法B比方法A要多佔用不少空間,由於方法B是把全部字段都取出,因此有可能取出的數據的總大小超出了sort_buffer的容量,致使每次只能取sort_buffer容量大小的數據,進行排序(建立tmp文件,多路合併),排完再取sort_buffer容量大小,再排…..從而屢次IO.
  原本想省一次IO操做,反而致使了大量的I/O操做,反而得不償失。

  4).參數調優
  增大sort_buffer_size參數的設置
  增大 max_length_for_sort_data 參數的設置

參數設置,提升order by 的速度
  1.order by 時select * 是一個大忌,最好只Query須要,這點很是重要。在這裏影響的是:
    a).當Query 的字段大小總和小於 max_length_for_sort_data ,並且排序字段不是text|blob類型時,會用改進後的算法 – 單路排序;不然用老算法–多路排序
    b).兩種算法算法的數據都有可能超出 sort_buffer 的容量,超出以後,會建立tmp文件進行合併排序,致使屢次I/O, 可是用單路排序算法的風險會更大一些,因此要提升 sort_buffer_size
  2.嘗試提升 sort_buffer_size
    無論用哪一種算法,提升這個參數都會提升效率,固然,要根據系統的能力去提升,由於這個參數是針對每一個進程的。
  3.嘗試提升 max_length_for_sort_data
    提升這個參數,會增長用改進算法的機率。但若是設的過高,數據總容量超出 sort_buffer_size 的機率增大,明顯症狀是高的磁盤I/O活動和低的處理器使用率。

  5).總結
  MySql兩種排序方式:文件排序(filesort)或掃描有序索引排序(index)
  MySql能爲排序與查詢使用相同的索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
KEY a_b_c(a, b, c)
 
order by 能使用索引最左前綴
- order by a
- order by a, b
- order by a desc, c desc
 
若是where使用索引的最左前綴定義爲常量,則order by 能使用索引
- where a = const order by b, c
- where a = const and b = const order by c
- where a = const order by b,c
- where a = const and b > const order by b, c
 
不能使用索引進行排序
- order by a asc, b desc, c desc /* 排序不一致 */
- where g = const order by b, c /* 丟失a索引 */
- where a = const order by c /* 丟失b索引 */
- where a = const order by a, d /* d不是索引的一部分 */
- where a in (..) order by b, c /* 對於排序來講,多個相等條件也是範圍查詢(in 也是範圍查詢)!! */

 

3.group by 關鍵字優化

基本與 order by 優化一致
  1).group by 實質是先排序後分組,遵守索引建的最佳左前綴
  2).當沒法使用索引列,增大 max_length_for_sort_data 參數的設置 + 增大sort_buffer_size參數的設置
  3).where高於having,能寫在where限定的條件就不要去having限定了。

慢查詢日誌

1.是什麼

  MySql的慢查詢日誌是MySQL提供的一種日誌記錄,它用來記錄在MySQL中響應時間超過閥值的語句,具體指運行時間超過long_query_time值的SQL,則會被記錄到慢查詢日誌中
  long_query_time 的默認值爲10,意思是運行10秒以上的語句。
  由它來查看哪些SQL超出了咱們的最大忍耐時間值,好比一條sql執行超過5秒種,咱們就算慢SQL,但願能收集超過5秒的sql,結合以前explain進行全面分析。   

2.怎麼用

1.說明
  默認狀況下,MySQL數據庫沒有開啓慢查詢日誌,須要咱們手動來設置這個參數。
  固然,若是不是調優須要的話,通常不建議啓動該參數,由於開啓慢查詢日誌會或多或少帶來必定的性能影響。慢查詢日誌支持將日誌記錄寫入文件。

2.查看是否開啓及如何開啓
  默認:show variables like '%slow_query_log%';
  開啓:set global show_query_log=1;,這個 只對當前數據庫生效,若是MySQL重啓後則會失效。若是要永久生效,必須修改配置文件my.cnf(其餘系統變量也是如此)

3.開啓慢查詢後,什麼樣的SQL纔會記錄到慢查詢日誌裏面呢?
  這個是由參數long_query_time控制,默認狀況下long_query_time的值爲10秒,
  命令:``show variables like ‘long_query_time%;’。可使用命令修改,也能夠在my.cnf參數裏面修改。
  假如運行時間正好等於 long_query_time 的狀況,並不會被記錄。也就是說,在mysql源碼裏是 判斷>long_query_time,而非>=.

3.日誌分析工具 mysqldumpslow

  在生產環境中,若是要手工分析日誌,查找、分析SQL,顯然是個體力活,MySQL提供了日誌分析工具 mysqldumpslow
查看 mysqldumpslow 的幫助信息:mysqldumpslow --help
  s: 表示按何種方式排序
  c: 訪問次數
  l: 鎖定時間
  r: 返回記錄
  t: 查詢時間
  al: 平均鎖定時間
  ar: 平均返回記錄數
  at: 平均查詢時間
  t: 返回前面多少條的數據
  g: 後邊搭配一個正則匹配模式,大小寫不敏感的。

1
2
3
4
5
6
7
8
9
10
11
獲得返回記錄集最多的10個SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/show.log
 
獲得訪問次數最多的10個SQL
mysqldumpslow -s c -t 10 /var/lig/mysql/show.log
 
獲得按照時間排序的前10條裏面含有左鏈接的查詢語句
mysqldumpslow -s t -t 10 -g "left join" /var/lig/mysql/show.log
 
另外建議在使用這些命令時結構 | 和more使用,不然有可能出現爆屏狀況
mysqldumpslow -s r -t 10 /var/lig/mysql/show.log | more

 

4.show profile

  這個是sql分析最強大的
  默認狀況下,參數處於關閉狀態,並保存最近15次的運行結果

1.是什麼
  是mysql提供能夠用來分析當前會話中語句執行的資源消耗狀況。能夠用於SQL的調優的測量

2.分析步驟
  1).是否支持,看看當前的mysql版本是否支持
   show variables like 'profiling%'
  2).開啓功能,默認是關閉,使用前須要開啓
   set profiling=on
  3).運行SQL
   select * from emp group by id%10 limit 1500000
   select * from emp group by id%20 order by 5
  4).查看結果,show profiles

1
2
3
4
5
6
mysql> SHOW PROFILES;
+----------+----------+-------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+----------+-------------------------------------------------------+
| 1 | 2.000088 | select * from emp group by id%10 limit 1500000 |
| 2 | 1.000136 | select * from emp group by id%20 order by 5 |

 

  5).診斷SQL,show profile cpu, block io for query 2 後的數字是 show profiles 裏的query_id
參數備註:
  all: 顯示全部的開銷信息
  block io: 顯示塊IO相關開銷
  context switches: 上下文切換相關開銷
  cpu: 顯示CPU相關開銷信息
  ipc: 顯示發送和接收相關開銷信息
  memory: 顯示內存相關開銷信息
  page faults: 顯示頁面錯誤相關開銷信息
  source: 顯示和source_function, source_file, souce_line相關的開銷信息
  swaps: 顯示交換次數相關開銷的信息

  6).平常開發須要注意的結論
  出現下一個狀況,就很危險了。
   converting HEAP to MyISAM 查詢結果太長,內存都不夠用了往磁盤上搬了。
   Creating tmp table 建立臨時表:copy數據到臨時表,用完再刪除
   Copying to tmp table on disk 把內存中臨時表複製到磁盤,很危險!!!
   locaked

5.全局查詢日誌

永遠不要在生產環境上打開,測試時能夠
一、配置啓用

1
2
3
4
5
6
7
8
9
在mysql的my.cnf中,設置以下:
# 開啓
general_log=1
 
# 記錄日誌文件的路徑
general_log_file=/path/logfile
 
# 輸出格式
log_output=FILE

 

  
二、編碼啓用

1
2
3
4
5
6
mysql> set global general_log=1;
mysql> set global log_output='TABLE';
# 此後,你所編寫的sql語句,將會記錄到mysql庫裏的general_log表。
 
# 能夠用下面的命令查看
mysql> select * from mysql.general_log;

 


MySQL鎖機制

  MySQL鎖機制詳細分析


補充知識

數據庫表結構優化

1.選擇合適的數據類型
  數據類型的選擇,重點在於 合適,如何選擇合適的數據類型?  1.使用能夠存下你的數據的最小的數據類型  2.使用簡單的數據類型,int要比varchar類型在mysql處理上簡單  3.儘量的使用not null定義字段  4.儘可能少用text類型,非用不可最好考慮分表。

相關文章
相關標籤/搜索