MySQL性能【索引優化分析】

SQL 性能下降

SQL 性能下降的幾種原因:

  • SQL 寫的爛
  • 使用了索引,但是索引失效了
  • 關聯查詢太多 join(設計缺陷或不得已的需求)
  • 服務器調優及各個參數設置的問題(比如緩衝區、線程數大小設置問題等)

Join 查詢

MySQL 的 SQL 語句執行順序
在這裏插入圖片描述

總結
在這裏插入圖片描述

七種 Join 查詢

// 內連接兩表共有內容
select * from emp a inner join dept b on a.deptId=b.id
// 左連接左表全有 && 兩表共有內容
select * from emp a left join dept b on a.deptId=b.id
// 右連接右表全有 && 兩表共有內容
select * from emp a right join dept b on a.deptId=b.id
// 左連接 左表獨有內容
select * from emp a left join dept b on a.deptId=b.id where b.id is null
// 右連接 右表獨有內容
select * from emp a right join dept b on a.deptId=b.id where a.deptId is null
// 全連接 兩表全部內容【union 去除重複數據】
select * from emp a left join dept b on a.deptId=b.id
union
select * from emp a right join dept b on a.deptId=b.id
// 全連接 兩表獨有內容
select * from emp a left join dept b on a.deptId=b.id where b.id is null
union
select * from emp a right join dept b on a.deptId=b.id where a.deptId is null

索引簡介

官方定義: 索引 Index 是幫助 mysql 高效獲取數據的數據結構。所以我們可以得出一個結論:**索引的本質就是數據結構。**

什麼是索引?

排好序的快速查找數據結構

數據本身之外,數據庫還維護着一個滿足特定查找算法的數據結構,這些數據結構以某種方式指向數據,這樣就可以在這些數據結構的基礎上實現高級查找算法,這種數據結構就是索引。

一般來說索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲到磁盤上。

我們平常所說的索引,如果沒有特別指明,都是指 B 樹(多路搜索樹,並不一定是二叉的)結構組織的索引。其中聚集索引、次要索引、覆蓋索引、複合索引、前綴索引、唯一索引默認都是使用 B+ 樹索引,統稱索引。除了B+樹這種類型的索引之外,還有哈希索引(hash,index)等。

索引的優勢

類似於大學圖書館建立書目索引,提高數據檢索的效率,降低了數據庫的 IO 成本
通過索引列對數據進行排序,降低了數據排序的成本,降低了CPU的使用消耗

索引的劣勢

實際上索引也是一張表,該表保存了主鍵與索引字段,並指向實體表的記錄,所以索引列也是要佔用空間的。

雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對錶進行 insert、update、delete。因爲更新表時,MySQL 不僅要保存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整因爲更新所帶來的鍵值變化後的索引信息。

索引只是提高效率的一個因素,如果你的 MySQL 有大數據量的表,就需要花時間研究建立最優秀的索引,或優化查詢。

mysql 索引分類

  • 單值索引

即一個索引只包含單個列,一個表可以有多個單列索引。

  • 唯一索引

索引的值必須是唯一的,但允許有空值。

  • 複合索引

即一個索引包含多個列

  • 基本語法
// 單獨創建索引
create [unique] index indexName ON mytable(columnName(length));
// 創建表的同時創建索引
alter mytable add [unique] index [indexName] ON (columnName(lenght));
// 刪除索引
drop index [indexName] ON mytable;
// 查看索引
show index from table_name;

四種方式來添加數據表的索引:

// 該語句添加一個主鍵,這意味着索引值必須是唯一的,且不能爲 NULL
ALTER table table_name ADD PRIMARY KEY(column_list);
// 該語句創建索引的值必須是唯一的(除了NULL外,NULL可能會出現多次)。
ALTER table table_name ADD UNIQUE index_name(column_list);
// 添加普通索引,索引值可出現多次。
ALTER table table_name ADD INDEX index_name(column_list);
// 該語句指定了索引爲 FULLTEXT,用於全文索引
ALTER table table_name ADD FULLTEXT index_name(column_list);

mysql 索引結構

BTree 索引

Hash 索引

full-text 全文索引

R-Tree 索引

哪些情況需要創建索引

  1. 主鍵自動建立唯一索引
  2. 頻繁作爲查詢條件的字段應該創建索引
  3. 查詢中與其他表關聯的字段,外鍵關係建立索引
  4. 單鍵/組合索引選擇問題(高併發下傾向於創建組合索引)
  5. 查詢中排序的字段,排序字段若通過索引去訪問將大大提高排序速度。
  6. 查詢中統計或者分組字段。

哪些情況不要創建索引

  1. 頻繁更新的字段不適合創建索引
  2. Where 條件裏用不到的字段不創建索引
  3. 表字段太少
  4. 數據重複且分佈平均的表字段,因此應該只爲最經常查詢和最經常排序的數據建立索引。如果某個數據列包含許多重複的內容,爲它建立索引就沒有太大的實際效果。

性能分析

MySql Query Optimizer

  1. MySQL 中有專門負責優化 SELECT 語句的優化器模塊,主要功能:通過計算分析系統中收集到統計信息,爲客戶端請求的 Query 提供它認爲最優的執行計劃,這部分最耗時間。
  2. 當客戶端向 MySQL 請求一條 Query,命令解析器模塊完成請求分類,區別出是 SELECT 並轉發給 MySQL Query Optimizer 時,MySQL Query Optimizer 首先會對整條 Query 進行優化,處理掉一些常量表達式的預算,直接換算成常量值。並對 Query 中的查詢條件進行簡化和轉換,如去掉一些無用或顯而易見的條件、結構調整等。然後分析 Query 中的 Hint 信息(如果有),看顯示 Hint 信息是否可以完全確定該 Query 的執行計劃。如果沒有 Hint 或 Hint 信息還不足以完全確定執行計劃,則會讀取所涉及對象的統計信息,根據 Query 進行寫相應的計算分析,然後再得出最後的執行計劃。

MySQL 常見瓶頸

  1. CPU:CPU在飽和的時候一般發生在數據裝入內存或從磁盤上讀取數據的時候。
  2. IO:磁盤 I/O 瓶頸發生在裝入數據遠大於內存容量的時候。
  3. 服務器硬件的性能瓶頸:top,free,iostat和vmstat來查看系統的性能狀態

Explain

Explain 又稱爲 【查看執行計劃】

使用 Explain 關鍵字可以模擬優化器執行的 SQL 查詢語句,從而知道 MySQL 是如何處理你的 SQL 語句的。分析你的查詢語句或是表結構的性能瓶頸。

如何使用 Explain

explain + SQL 語句

在這裏插入圖片描述
包含的信息:

id select_type table type possible_keys key key_len ref rows Extra

Explain 的作用

  1. 查看錶的讀取順序
  2. 查看數據讀取操作的操作類型
  3. 查看哪些索引可以使用
  4. 查看哪些索引被實際使用
  5. 查看錶之間的引用關係
  6. 查看每張表有多少行被優化器查詢

Explain 的 id 字段作用

select 查詢的序列號,包含一組數字,表示查詢中執行 select 字句或操作表的順序

id 字段有三種情況:

  1. id 相同,執行順序由上至下
  2. id 不同,如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行
  3. id相同不同,同時存在

id 如果相同,可以認爲是一組,從上往下順序執行;
在所有組中,id值越大,優先級越高,越先執行;
衍生 = DERIVED

Explain 的 select_type 和 table 字段作用

select_type 常見的值:

id select_type
1 SIMPLE
2 PRIMARY
3 SUBQUERY
4 DERIVED
5 UNION
6 UNION RESULT

查詢的類型,主要是用於區別。
普通查詢、聯合查詢、子查詢等的複雜查詢。

  1. SIMPLE :簡單的 select 查詢,查詢中不包含子查詢或者 union
  2. PRIMARY:查詢中若包含任何複雜的子部分,最外層查詢則被標記爲 primary
  3. SUBQUERY:在 select 或 where 列表中包含了子查詢
  4. DERIVED:在 from 列表中包含的子查詢被標記爲 DERIVED衍生 ,MySQL 會遞歸執行這些子查詢,把結果放在臨時表裏。
  5. UNION:若第二個 select 出現在 union 之後,則被標記爲 union;若 union 包含在 from 字句得子查詢中,外層 select 將被標記爲:DERIVED
  6. UNION RESULT:從 union 表獲取結果得 select

table:顯示這一行的數據是關於哪張表的。

Explain 的 type 字段作用

type 表示訪問類型排列,type 顯示的是訪問類型,是較爲重要的一個指標,結果值從最好到最壞依次是:

system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>ALL

system>conft>eq_ref>ref>range>index>ALL

一般來說,得保證查詢至少達到 range 級別,最好能達到 ref。

type 常見的值:

ALL index range ref eq_ref const,system NULL

顯示查詢使用了何種類型,從最好到最差依次是:
system>const>en_ref>ref>range>index>ALL

  1. system:表只有一行記錄(等於系統表),這是 const 類型得特列,平時不會出現,這個也可以忽略不計
  2. const:表示通過索引一次就找到了,const 用於比較 primary key 或者 unique 索引。因爲只匹配一行數據,所以很快;如將主鍵置於 where 列表中, MySQL 就能將該查詢轉換爲一個常量。
  3. ep_ref:唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描。
  4. ref:非唯一性索引掃描,返回匹配某個單獨值的所有行,本質上也是一種索引訪問,它返回所有匹配某個單獨值的行,然而,他可能會找到多個符合條件的行,所以他應該屬於查找和掃描得混合體。
  5. range:只檢查給定範圍的行,使用一個索引來選擇行。key 列顯示使用了哪個索引一般就是在你的 where 語句中出現了 between,<,>,in 等的查詢,這種範圍掃描索引比全表掃描要快,因爲它只需要開始於索引的某一點,而結束於另一點,不用掃描全部索引。
  6. index:Full Index Scan,index 與 ALL 區別在於 index 類型只遍歷索引樹。這通常比 ALL 快,因爲索引文件通常比數據文件小。(也就是說雖然 all 和 Index 都是讀全表,但 index 是從索引中讀取的,而 all 是從硬盤中讀的)
  7. all:Full Table Scan,將遍歷全表以找到匹配的行。

Explain 的 possible_keys 和 key 字段作用

  • possible_keys

顯示可能應用到這張表中的索引,一個或多個。
查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定要被查詢實際使用

  • key

實際使用的索引,如果爲 NULL,則沒有使用索引
查詢中若使用了覆蓋索引,則該索引僅出現在 key 列表中

Explain 的 key_len 字段作用

表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好。
key_len 顯示的值爲索引字段的最大可能長度,並非實際使用長度,即 key_len 是根據表定義計算而得,不是通過表內檢索出的。

Explain 的 ref 字段作用

顯示索引的哪一列被使用了,如果可能的話,是一個常數。哪些列或常量被用於查找索引列上的值。

查詢中與其他表關聯的字段,外鍵關係建立索引

Explain 的 rows 字段作用

根據表統計信息及索引選擇情況,大致估算出找到所需的記錄所需要讀取的行數

Explain 的 Extra 字段作用

記錄十分重要的額外信息

常見的值有以下幾種:

  1. Using filesort:說明 Mysql 會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL 中無法利用索引完成的排序操作稱爲“文件排序”。
  2. Using temporary:使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序 order by 和分組查詢 group by。
  3. Using index:表示相應的 select 操作中使用了覆蓋索引 (Covering Index),避免訪問了表的數據行,效率不錯!如果同時出現了 using where,表明索引被用來執行索引鍵值的查找;如果沒有同時出現 using where,表明索引用來讀取數據而非執行查找動作。覆蓋索引(Covering Index)

覆蓋索引:就是 select 的數據列只用從索引中就能取得,不必讀取數據行,MySQL 可以利用索引返回 select 列表中的字段,而不必根據索引再次讀取數據文件,換句話說查詢列要被所創建的索引覆蓋。

注意:如果要使用覆蓋索引,一定要注意 select 列表中只取出需要的列,不可 select * ,因爲如果將所有字段一起做索引會導致索引文件過大,查詢性能降低。

  1. Using where:表示該條 SQL 使用了 where 過濾。
  2. using join buffer:表示該條 SQL 使用了連接緩存。
  3. impossible where:表示該條 SQL 語句的 where 字句的值總是 false,不能用來獲取任何元組。
  4. select tables optimized away:在沒有 group by 的情況下,基於索引優化 min/max 操作或者對於 MyISAM 存儲引擎優化 count( * )操作,不必等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化。
  5. distinct:優化 distinct 操作,再找到第一匹配的元組後即停止找同樣值得動作。

索引優化

索引分析

  • 單表分析:常用來查詢得字段建立索引優化良好。
  • 兩表分析:左連接建索引建議建到右表中,右表則建到左表中,相反的來互相建立索引即可優化。
  • 三表分析:同兩表一致,也是相反的給另外兩表建立索引優化。

索引失效

  1. 全值匹配
  2. 最佳左前綴法則

如果索引包含了多列,要遵守最左前綴法則,指的是查詢從索引的最左前列開始並且不跳過索引中的列。

  1. 不在索引列上做任何操作(計算、函數、自動或手動類型轉換),會導致索引失效而轉向全表掃描
  2. 存儲引擎不能使用索引中範圍條件右邊的列
  3. 儘量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少 select *
  4. mysql 在使用不等於 (!= 或者 <>) 的時候無法使用索引會導致全表掃描
  5. is null,is not null 也無法使用索引
  6. like 通配符以 % 開頭(’%abc…’) mysql 索引失效會變成全表掃描操作。
  7. 字符串不加單引號索引失效。
  8. 少用 or,用它來來連接時會索引失效。

面試問題:如何解決 like ‘%abc%’ 時索引不失效的方法?
我們都知道 like 查詢時要保證索引不失效的前提是 %加在字符串右邊,列如 : ‘abc%’ 這樣寫索引就不會失效,但現在的條件是我們要全模糊查詢,例如 ‘%abc%’ ,這樣寫索引 100% 會失效,如何解決這種問題呢?

我們可以添加覆蓋索引來優化,比如你的查詢條件是 name,我們在該字段上添加一個索引,列如:
select * from table where name like ‘%abc%’;
經過 explain 分析以上sql並沒有用到索引,我們可以這樣改這條 sql
select name from table where name like ‘%abc%’;
再次經過 explain 分析以上 sql ,終於發現確實用到了索引,這就保證了即使用了 like 全模糊查詢,又解決了索引失效的問題。
**這裏要注意的事情:覆蓋索引優化的前提是 select 查詢的字段正好和你的索引字段數量一致的前提下,簡單來說:覆蓋索引字段和你select 的字段一致或小於其字段。**

一般性建議

  1. 對於單鍵索引,儘量選擇針對當前 query 過濾性更好的索引。
  2. 在選擇組合索引的時候,當前 Query 中過濾性最好的字段在索引字段順序中,位置越靠前越好。
  3. 在選擇組合索引的時候,儘量選擇可以能夠包含當前 query 中的 where 字句中更多字段的索引。
  4. 儘可能通過分析統計信息和調整 query 的寫法來達到選擇合適索引的目的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章