MySQL索引優化分析

爲何你寫的sql查詢慢?爲何你建的索引常失效?經過本章內容,你將學會MySQL性能降低的緣由,索引的簡介,索引建立的原則,explain命令的使用,以及explain輸出字段的意義。助你瞭解索引,分析索引,使用索引,從而寫出更高性能的sql語句。還在等啥子?捲起袖子就是幹!html

 

 

咱們先簡單瞭解一下非關係型數據庫關係型數據庫的區別。java

MongoDB是NoSQL中的一種。NoSQL的全稱是Not only SQL,非關係型數據庫。它的特色是性能高擴張性強模式靈活,在高併發場景表現得尤其突出。但目前它還只是關係型數據庫的補充,它在數據的一致性,數據的安全性,查詢的複雜性問題上和關係型數據庫還存在必定差距。mysql

MySQL是關係性數據庫中的一種,查詢功能強數據一致性高數據安全性高支持二級索引。但性能方面稍遜與MongoDB,特別是百萬級別以上的數據,很容易出現查詢慢的現象。這時候須要分析查詢慢的緣由,通常狀況下是程序員sql寫的爛,或者是沒有鍵索引,或者是索引失效等緣由致使的。ios

公司ERP系統數據庫主要是MongoDB(最接近關係型數據的NoSQL),其次是Redis,MySQL只佔不多的部分。如今又從新使用MySQL,歸功於阿里巴巴的奇門系統和聚石塔系統。考慮到訂單數量已是百萬級以上,對MySQL的性能分析也就顯得格外重要。git

 

咱們先經過兩個簡單的例子來入門。後面會詳細介紹各個參數的做用和意義。程序員

說明:須要用到的sql已經放在了github上了,喜歡的同窗能夠點一下star,哈哈。https://github.com/ITDragonBlog/daydayup/tree/master/MySQL/github

 

業務邏輯:訂單導入時,爲了不重複導單,通常會經過交易號去數據庫中查詢,判斷該訂單是否已經存在。算法

 

最基礎的sql語句

 

 

 

查詢的自己沒有任何問題,在線下的測試環境也沒有任何問題。但是,功能一旦上線,查詢慢的問題就迎面而來。幾百上千萬的訂單,用全表掃描?啊?哼!sql

怎麼知道該sql是全表掃描呢?經過explain命令能夠清楚MySQL是如何處理sql語句的。打印的內容分別表示:數據庫

由於數據庫中只有三條數據,因此rows和filtered的信息做用不大。這裏須要重點了解的是type爲ALL,全表掃描的性能是最差的,假設數據庫中有幾百萬條數據,在沒有索引的幫助下會異常卡頓。

 

初步優化:爲transaction_id建立索引

 

 

 

這裏建立的索引是惟一索引,而非普通索引。
惟一索引打印的type值是const。表示經過索引一次就能夠找到。即找到值就結束掃描返回查詢結果。
普通索引打印的type值是ref。表示非惟一性索引掃描。找到值還要繼續掃描,直到將索引文件掃描完爲止。(這裏沒有貼出代碼)
顯而易見,const的性能要遠高於ref。而且根據業務邏輯來判斷,建立惟一索引是合情合理的。

 

再次優化:覆蓋索引

 

 

 

這裏將改成了後
Extra 顯示 Using index,表示該查詢使用了覆蓋索引,這是一個很是好的消息,說明該sql語句的性能很好。若提示的是Using filesort(使用內部排序)和Using temporary(使用臨時表)則代表該sql須要當即優化了。
根據業務邏輯來的,查詢結構返回transaction_id 是能夠知足業務邏輯要求的。

 

業務邏輯:優先處理訂單級別高,錄入時間長的訂單。
既然是排序,首先想到的應該是order by, 還有一個可怕的 Using filesort 等着你。

 

最基礎的sql語句

 

 

 

首先,採用全表掃描就不合理,還使用了文件排序Using filesort,更加拖慢了性能。
MySQL在4.1版本以前文件排序是採用雙路排序的算法,因爲兩次掃描磁盤,I/O耗時太長。後優化成單路排序算法。其本質就是用空間換時間,但若是數據量太大,buffer的空間不足,會致使屢次I/O的狀況。其效果反而更差。與其找運維同事修改MySQL配置,還不如本身乖乖地建索引。

 

初步優化:爲order_level,input_date 建立複合索引

 

 

 

建立複合索引後你會驚奇的發現,和沒建立索引同樣???都是全表掃描,都用到了文件排序。是索引失效?仍是索引建立失敗?咱們試着看看下面打印狀況

 

 

 

將 換成了 後。type從all升級爲index,表示(full index scan)全索引文件掃描,Extra也顯示使用了覆蓋索引。但是不對啊!!!!檢索雖然快了,但返回的內容只有order_level和input_date 兩個字段,讓業務同事怎麼用?難道把每一個字段都建一個複合索引?
MySQL沒有這麼笨,可使用force index 強制指定索引。在原來的sql語句上修改 便可。

 

 

 

再次優化:訂單級別真的要排序麼?

 

其實給訂單級別排序意義並不大,給訂單級別添加索引意義也不大。由於order_level的值可能只有,低,中,高,加急,這四種。對於這種重複且分佈平均的字段,排序和加索引的做用不大。
咱們可否先固定 order_level 的值,而後再給 input_date 排序?若是查詢效果明顯,是能夠推薦業務同事使用該查詢方式。

 

 

 

和以前的sql比起來,type從index 升級爲 ref(非惟一性索引掃描)。索引的長度從68變成了5,說明只用了一個索引。ref也是一個常量。Extra 爲Using index condition 表示自動根據臨界值,選擇索引掃描仍是全表掃描。總的來講性能遠勝於以前的sql。

 

上面兩個案例只是快速入門,咱們需嚴記一點:優化是基於業務邏輯來的。絕對不能爲了優化而擅自修改業務邏輯。若是能修改固然是最好的。

 

 

官方定義:索引(Index) 是幫助MySQL高效獲取數據的數據結構。
你們必定很好奇,索引爲何是一種數據結構,它又是怎麼提升查詢的速度?咱們拿最經常使用的二叉樹來分析索引的工做原理。看下面的圖片:
640

建立索引的優點:

1. 提升數據的檢索速度,下降數據庫IO成本:使用索引的意義就是經過縮小表中須要查詢的記錄的數目從而加快搜索的速度。

2. 下降數據排序的成本,下降CPU消耗:索引之因此查的快,是由於先將數據排好序,若該字段正好須要排序,則真好下降了排序的成本。

 

建立索引的劣勢:
1. 佔用存儲空間:索引實際上也是一張表,記錄了主鍵與索引字段,通常以索引文件的形式存儲在磁盤上。
2. 下降更新表的速度:表的數據發生了變化,對應的索引也須要一塊兒變動,從而減低的更新速度。不然索引指向的物理數據可能不對,這也是索引失效的緣由之一。
3. 優質索引建立難:索引的建立並不是一日之功,也並不是一直不變。須要頻繁根據用戶的行爲和具體的業務邏輯去建立最佳的索引。

 

 

咱們常說的索引通常指的是BTree(多路搜索樹)結構組織的索引。其中還有聚合索引,次要索引,複合索引,前綴索引,惟一索引,統稱索引,固然除了B+樹外,還有哈希索引(hash index)等。

 

單值索引:一個索引只包含單個列,一個表能夠有多個單列索引
惟一索引:索引列的值必須惟一,但容許有空值
複合索引:一個索引包含多個列,實際開發中推薦使用

實際開發中推薦使用複合索引,而且單表建立的索引個數建議不要超過五個

 

基本語法:

 

建立:

 

 

刪除:

 

 

查看:

 

 

哪些狀況須要建索引:
1. 主鍵,惟一索引
2. 常常用做查詢條件的字段須要建立索引
3. 常常須要排序、分組和統計的字段須要創建索引
4. 查詢中與其餘表關聯的字段,外鍵關係創建索引

 

哪些狀況不要建索引:
1. 表的記錄太少,百萬級如下的數據不須要建立索引
2. 常常增刪改的表不須要建立索引
3. 數據重複且分佈平均的字段不須要建立索引,如 true,false 之類。
4. 頻發更新的字段不適合建立索引
5. where條件裏用不到的字段不須要建立索引

 

 

 

MySQL自身參見的性能問題有磁盤空間不足,磁盤I/O太大,服務器硬件性能低。
1. CPU:CPU 在飽和的時候通常發生在數據裝入內存或從磁盤上讀取數據時候
2. IO:磁盤I/O 瓶頸發生在裝入數據遠大於內存容量的時候
3. 服務器硬件的性能瓶頸:top,free,iostat 和 vmstat來查看系統的性能狀態

 

使用explain關鍵字能夠模擬優化器執行sql查詢語句,從而得知MySQL 是如何處理sql語句。

 

 

 

id

 

select 查詢的序列號,包含一組能夠重複的數字,表示查詢中執行sql語句的順序。通常有三種狀況:
第一種:id所有相同,sql的執行順序是由上至下;
第二種:id所有不一樣,sql的執行順序是根據id大的優先執行;
第三種:id既存在相同,又存在不一樣的。先根據id大的優先執行,再根據相同id從上至下的執行。

 

select_type

 

select 查詢的類型,主要是用於區別普通查詢,聯合查詢,嵌套的複雜查詢
simple:簡單的select 查詢,查詢中不包含子查詢或者union
primary:查詢中若包含任何複雜的子查詢,最外層查詢則被標記爲primary
subquery:在select或where 列表中包含了子查詢
derived:在from列表中包含的子查詢被標記爲derived(衍生)MySQL會遞歸執行這些子查詢,把結果放在臨時表裏。
union:若第二個select出如今union以後,則被標記爲union,若union包含在from子句的子查詢中,外層select將被標記爲:derived
union result:從union表獲取結果的select

 

partitions

 

表所使用的分區,若是要統計十年公司訂單的金額,能夠把數據分爲十個區,每年表明一個區。這樣能夠大大的提升查詢效率。

 

type

 

這是一個很是重要的參數,鏈接類型,常見的有:all , index , range , ref , eq_ref , const , system , null 八個級別。
性能從最優到最差的排序:system > const > eq_ref > ref > range > index > all
對java程序員來講,若保證查詢至少達到range級別或者最好能達到ref則算是一個優秀而又負責的程序員。

all:(full table scan)全表掃描無疑是最差,如果百萬千萬級數據量,全表掃描會很是慢。

index:(full index scan)全索引文件掃描比all好不少,畢竟從索引樹中找數據,比從全表中找數據要快。
range:只檢索給定範圍的行,使用索引來匹配行。範圍縮小了,固然比全表掃描和全索引文件掃描要快。sql語句中通常會有between,in,>,< 等查詢。
ref:非惟一性索引掃描,本質上也是一種索引訪問,返回全部匹配某個單獨值的行。好比查詢公司全部屬於研發團隊的同事,匹配的結果是多個並不是惟一值。
eq_ref:惟一性索引掃描,對於每一個索引鍵,表中有一條記錄與之匹配。好比查詢公司的CEO,匹配的結果只多是一條記錄,
const:表示經過索引一次就能夠找到,const用於比較primary key 或者unique索引。由於只匹配一行數據,因此很快,若將主鍵至於where列表中,MySQL就能將該查詢轉換爲一個常量。
system:表只有一條記錄(等於系統表),這是const類型的特列,平時不會出現,瞭解便可

 

possible_keys

 

顯示查詢語句可能用到的索引(一個或多個或爲null),不必定被查詢實際使用。僅供參考使用。

 

key

 

顯示查詢語句實際使用的索引。若爲null,則表示沒有使用索引。

 

key_len

 

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

 

ref

 

顯示索引的哪一列或常量被用於查找索引列上的值。

 

rows

 

根據表統計信息及索引選用狀況,大體估算出找到所需的記錄所須要讀取的行數,值越大越很差。

 

extra

 

Using filesort: 說明MySQL會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中沒法利用索引完成的排序操做稱爲「文件排序」 。出現這個就要馬上優化sql。
Using temporary: 使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序 order by 和 分組查詢 group by。 出現這個更要馬上優化sql。
Using index: 表示相應的select 操做中使用了覆蓋索引(Covering index),避免訪問了表的數據行,效果不錯!若是同時出現Using where,代表索引被用來執行索引鍵值的查找。若是沒有同時出現Using where,表示索引用來讀取數據而非執行查找動做。
覆蓋索引(Covering Index) :也叫索引覆蓋,就是select 的數據列只用從索引中就可以取得,沒必要讀取數據行,MySQL能夠利用索引返回select 列表中的字段,而沒必要根據索引再次讀取數據文件。
Using index condition: 在5.6版本後加入的新特性,優化器會在索引存在的狀況下,經過符合RANGE範圍的條數 和 總數的比例來選擇是使用索引仍是進行全表遍歷。
Using where: 代表使用了where 過濾
Using join buffer: 代表使用了鏈接緩存
impossible where: where 語句的值老是false,不可用,不能用來獲取任何元素
distinct: 優化distinct操做,在找到第一匹配的元組後即中止找一樣值的動做。

 

filtered

 

一個百分比的值,和rows 列的值一塊兒使用,能夠估計出查詢執行計劃(QEP)中的前一個表的結果集,信件格式從而肯定join操做的循環次數。小表驅動大表,減輕鏈接的次數。

 

經過explain的參數介紹,咱們能夠得知:
1. 表的讀取順序(id)
2. 數據讀取操做的操做類型(type)
3. 哪些索引被實際使用(key)
4. 表之間的引用(ref)
5. 每張表有多少行被優化器查詢(rows)

 

 

從程序員的角度
1. 查詢語句寫的很差
2. 沒建索引,索引建的不合理或索引失效
3. 關聯查詢有太多的join

從服務器的角度
1. 服務器磁盤空間不足
2. 服務器調優配置參數設置不合理

 

1. 索引是排好序且快速查找的數據結構。其目的是爲了提升查詢的效率。
2. 建立索引後,查詢數據變快,但更新數據變慢。
3. 性能降低的緣由極可能是索引失效致使。
4. 索引建立的原則,常常查詢的字段適合建立索引,頻繁須要更新的數據不適合建立索引。
5. 索引字段頻繁更新,或者表數據物理刪除容易形成索引失效。
6. 擅用 explain 分析sql語句
7. 除了優化sql語句外,還能夠優化表的設計。如儘可能作成單表查詢,減小表之間的關聯。設計歸檔表等。

 

到這裏,MySQL的索引優化分析就結束了,有什麼不對的地方,你們能夠提出來。若是以爲不錯能夠點一下贊。

 

 

 

推薦閱讀:

1,小白專屬mysql入門

2,MySQL的索引是什麼?怎麼優化?

3,第5篇:數據庫系統的實現

4,數據倉庫③-實現與使用(含OLAP重點講解)

640?wx_fmt=png


文章來源:https://blog.csdn.net/rlnLo2pNEfx9c/article/details/80731300

相關文章
相關標籤/搜索