資料庫索引原理及優化

来源:https://www.cnblogs.com/caiyt/archive/2023/02/12/17113975.html
-Advertisement-
Play Games

摘要: 本文內容主要來源於互聯網上主流文章,只是按照個人理解稍作整合,後面附有參考鏈接。 本文內容主要來源於互聯網上主流文章,只是按照個人理解稍作整合,後面附有參考鏈接。 一、摘要 本文以MySQL資料庫為研究對象,討論與資料庫索引相關的一些話題。特別需要說明的是,MySQL支持諸多存儲引擎,而各種 ...


摘要: 本文內容主要來源於互聯網上主流文章,只是按照個人理解稍作整合,後面附有參考鏈接。

本文內容主要來源於互聯網上主流文章,只是按照個人理解稍作整合,後面附有參考鏈接。

一、摘要

本文以MySQL資料庫為研究對象,討論與資料庫索引相關的一些話題。特別需要說明的是,MySQL支持諸多存儲引擎,而各種存儲引擎對索引的支持也各不相同,因此MySQL資料庫支持多種索引類型,如BTree索引,哈希索引,全文索引等等。為了避免混亂,本文將只關註於BTree索引,因為這是平常使用MySQL時主要打交道的索引,至於哈希索引和全文索引本文暫不討論。

二、常見的查詢演算法及數據結構

為什麼這裡要講查詢演算法和數據結構呢?因為之所以要建立索引,其實就是為了構建一種數據結構,可以在上面應用一種高效的查詢演算法,最終提高數據的查詢速度。

2.1 索引的本質

MySQL官方對索引的定義為:索引(Index)是幫助MySQL高效獲取數據的數據結構。提取句子主幹,就可以得到索引的本質:索引是數據結構。

2.2 常見的查詢演算法

我們知道,資料庫查詢是資料庫的最主要功能之一。我們都希望查詢數據的速度能儘可能的快,因此資料庫系統的設計者會從查詢演算法的角度進行優化。那麼有哪些查詢演算法可以使查詢速度變得更快呢?

2.2.1 順序查找(linear search )

最基本的查詢演算法當然是順序查找(linear search),也就是對比每個元素的方法,不過這種演算法在數據量很大時效率是極低的。 
數據結構:有序或無序隊列 
複雜度:O(n) 
實例代碼:

//順序查找
int SequenceSearch(int a[], int value, int n)
{
    int i;
    for(i=0; i<n; i++)
        if(a[i]==value)
            return i;
    return -1;
}

2.2.2 二分查找(binary search)

比順序查找更快的查詢方法應該就是二分查找了,二分查找的原理是查找過程從數組的中間元素開始,如果中間元素正好是要查找的元素,則搜素過程結束;如果某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,而且跟開始一樣從中間元素開始比較。如果在某一步驟數組為空,則代表找不到。 
數據結構:有序數組 
複雜度:O(logn) 
實例代碼:

//二分查找,遞歸版本
int BinarySearch2(int a[], int value, int low, int high)
{
    int mid = low+(high-low)/2;
    if(a[mid]==value)
        return mid;
    if(a[mid]>value)
        return BinarySearch2(a, value, low, mid-1);
    if(a[mid]<value)
        return BinarySearch2(a, value, mid+1, high);
}

2.2.3 二叉排序樹查找

二叉排序樹的特點是:

  1. 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
  2. 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
  3. 它的左、右子樹也分別為二叉排序樹。

搜索的原理:

  1. 若b是空樹,則搜索失敗,否則:
  2. 若x等於b的根節點的數據域之值,則查找成功;否則:
  3. 若x小於b的根節點的數據域之值,則搜索左子樹;否則:
  4. 查找右子樹。

數據結構:二叉排序樹 
時間複雜度: O(log2N)

2.2.4 哈希散列法(哈希表)

其原理是首先根據key值和哈希函數創建一個哈希表(散列表),燃耗根據鍵值,通過散列函數,定位數據元素位置。

數據結構:哈希表 
時間複雜度:幾乎是O(1),取決於產生衝突的多少。

2.2.5 分塊查找

分塊查找又稱索引順序查找,它是順序查找的一種改進方法。其演算法思想是將n個數據元素”按塊有序”劃分為m塊(m ≤ n)。每一塊中的結點不必有序,但塊與塊之間必須”按塊有序”;即第1塊中任一元素的關鍵字都必須小於第2塊中任一元素的關鍵字;而第2塊中任一元素又都必須小於第3塊中的任一元素,依次類推。 
   
演算法流程:

  1. 先選取各塊中的最大關鍵字構成一個索引表;
  2. 查找分兩個部分:先對索引表進行二分查找或順序查找,以確定待查記錄在哪一塊中;然後,在已確定的塊中用順序法進行查找。

這種搜索演算法每一次比較都使搜索範圍縮小一半。它們的查詢速度就有了很大的提升,複雜度為。如果稍微分析一下會發現,每種查找演算法都只能應用於特定的數據結構之上,例如二分查找要求被檢索數據有序,而二叉樹查找只能應用於二叉查找樹上,但是數據本身的組織結構不可能完全滿足各種數據結構(例如,理論上不可能同時將兩列都按順序進行組織),所以,在數據之外,資料庫系統還維護著滿足特定查找演算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找演算法。這種數據結構,就是索引。

2.3 平衡多路搜索樹B樹(B-tree)

上面講到了二叉樹,它的搜索時間複雜度為O(log2N),所以它的搜索效率和樹的深度有關,如果要提高查詢速度,那麼就要降低樹的深度。要降低樹的深度,很自然的方法就是採用多叉樹,再結合平衡二叉樹的思想,我們可以構建一個平衡多叉樹結構,然後就可以在上面構建平衡多路查找演算法,提高大數據量下的搜索效率。

2.3.1 B Tree

B樹(Balance Tree)又叫做B- 樹(其實B-是由B-tree翻譯過來,所以B-樹和B樹是一個概念) ,它就是一種平衡多路查找樹。下圖就是一個典型的B樹: 
這裡寫圖片描述

從上圖中我們可以大致看到B樹的一些特點,為了更好的描述B樹,我們定義記錄為一個二元組[key, data],key為記錄的鍵值,data表示其它數據(上圖中只有key,沒有畫出data數據 )。下麵是對B樹的一個詳細定義:

1. 有一個根節點,根節點只有一個記錄和兩個孩子或者根節點為空;
2. 每個節點記錄中的key和指針相互間隔,指針指向孩子節點;
3. d是表示樹的寬度,除葉子節點之外,其它每個節點有[d/2,d-1]條記錄,並且些記錄中的key都是從左到右按大小排列的,有[d/2+1,d]個孩子;
4. 在一個節點中,第n個子樹中的所有key,小於這個節點中第n個key,大於第n-1個key,比如上圖中B節點的第2個子節點E中的所有key都小於B中的第2個key 9,大於第1個key 3;
5. 所有的葉子節點必須在同一層次,也就是它們具有相同的深度;

由於B-Tree的特性,在B-Tree中按key檢索數據的演算法非常直觀:首先從根節點進行二分查找,如果找到則返回對應節點的data,否則對相應區間的指針指向的節點遞歸進行查找,直到找到節點或找到null指針,前者查找成功,後者查找失敗。B-Tree上查找演算法的偽代碼如下:

BTree_Search(node, key) {
     if(node == null) return null;
     foreach(node.key){
          if(node.key[i] == key) return node.data[i];
          if(node.key[i] > key) return BTree_Search(point[i]->node);
      }
     return BTree_Search(point[i+1]->node);
  }
data = BTree_Search(root, my_key);

關於B-Tree有一系列有趣的性質,例如一個度為d的B-Tree,設其索引N個key,則其樹高h的上限為logd((N+1)/2),檢索一個key,其查找節點個數的漸進複雜度為O(logdN)。從這點可以看出,B-Tree是一個非常有效率的索引數據結構。

另外,由於插入刪除新的數據記錄會破壞B-Tree的性質,因此在插入刪除時,需要對樹進行一個分裂、合併、轉移等操作以保持B-Tree性質,本文不打算完整討論B-Tree這些內容,因為已經有許多資料詳細說明瞭B-Tree的數學性質及插入刪除演算法,有興趣的朋友可以查閱其它文獻進行詳細研究。

2.3.2 B+Tree

其實B-Tree有許多變種,其中最常見的是B+Tree,比如MySQL就普遍使用B+Tree實現其索引結構。B-Tree相比,B+Tree有以下不同點:

  • 每個節點的指針上限為2d而不是2d+1;
  • 內節點不存儲data,只存儲key;
  • 葉子節點不存儲指針;

下麵是一個簡單的B+Tree示意。 
這裡寫圖片描述

由於並不是所有節點都具有相同的域,因此B+Tree中葉節點和內節點一般大小不同。這點與B-Tree不同,雖然B-Tree中不同節點存放的key和指針可能數量不一致,但是每個節點的域和上限是一致的,所以在實現中B-Tree往往對每個節點申請同等大小的空間。一般來說,B+Tree比B-Tree更適合實現外存儲索引結構,具體原因與外存儲器原理及電腦存取原理有關,將在下麵討論。

2.3.3 帶有順序訪問指針的B+Tree

一般在資料庫系統或文件系統中使用的B+Tree結構都在經典B+Tree的基礎上進行了優化,增加了順序訪問指針。 
這裡寫圖片描述

如圖所示,在B+Tree的每個葉子節點增加一個指向相鄰葉子節點的指針,就形成了帶有順序訪問指針的B+Tree。做這個優化的目的是為了提高區間訪問的性能,例如圖4中如果要查詢key為從18到49的所有數據記錄,當找到18後,只需順著節點和指針順序遍歷就可以一次性訪問到所有數據節點,極大提到了區間查詢效率。

這一節對B-Tree和B+Tree進行了一個簡單的介紹,下一節結合存儲器存取原理介紹為什麼目前B+Tree是資料庫系統實現索引的首選數據結構。

三、索引數據結構設相關的電腦原理

上文說過,二叉樹、紅黑樹等數據結構也可以用來實現索引,但是文件系統及資料庫系統普遍採用B-/+Tree作為索引結構,這一節將結合電腦組成原理相關知識討論B-/+Tree作為索引的理論基礎。

3.1 兩種類型的存儲

在電腦系統中一般包含兩種類型的存儲,電腦主存(RAM)和外部存儲器(如硬碟、CD、SSD等)。在設計索引演算法和存儲結構時,我們必須要考慮到這兩種類型的存儲特點。主存的讀取速度快,相對於主存,外部磁碟的數據讀取速率要比主從慢好幾個數量級,具體它們之間的差別後面會詳細介紹。 上面講的所有查詢演算法都是假設數據存儲在電腦主存中的,電腦主存一般比較小,實際資料庫中數據都是存儲到外部存儲器的。

一般來說,索引本身也很大,不可能全部存儲在記憶體中,因此索引往往以索引文件的形式存儲的磁碟上。這樣的話,索引查找過程中就要產生磁碟I/O消耗,相對於記憶體存取,I/O存取的消耗要高幾個數量級,所以評價一個數據結構作為索引的優劣最重要的指標就是在查找過程中磁碟I/O操作次數的漸進複雜度。換句話說,索引的結構組織要儘量減少查找過程中磁碟I/O的存取次數。下麵詳細介紹記憶體和磁碟存取原理,然後再結合這些原理分析B-/+Tree作為索引的效率。

3.2 主存存取原理

目前電腦使用的主存基本都是隨機讀寫存儲器(RAM),現代RAM的結構和存取原理比較複雜,這裡本文拋卻具體差別,抽象出一個十分簡單的存取模型來說明RAM的工作原理。 
這裡寫圖片描述

從抽象角度看,主存是一系列的存儲單元組成的矩陣,每個存儲單元存儲固定大小的數據。每個存儲單元有唯一的地址,現代主存的編址規則比較複雜,這裡將其簡化成一個二維地址:通過一個行地址和一個列地址可以唯一定位到一個存儲單元。上圖展示了一個4 x 4的主存模型。

主存的存取過程如下:

當系統需要讀取主存時,則將地址信號放到地址匯流排上傳給主存,主存讀到地址信號後,解析信號並定位到指定存儲單元,然後將此存儲單元數據放到數據匯流排上,供其它部件讀取。寫主存的過程類似,系統將要寫入單元地址和數據分別放在地址匯流排和數據匯流排上,主存讀取兩個匯流排的內容,做相應的寫操作。

這裡可以看出,主存存取的時間僅與存取次數呈線性關係,因為不存在機械操作,兩次存取的數據的“距離”不會對時間有任何影響,例如,先取A0再取A1和先取A0再取D3的時間消耗是一樣的。

3.3 磁碟存取原理

上文說過,索引一般以文件形式存儲在磁碟上,索引檢索需要磁碟I/O操作。與主存不同,磁碟I/O存在機械運動耗費,因此磁碟I/O的時間消耗是巨大的。

磁碟讀取數據靠的是機械運動,當需要從磁碟讀取數據時,系統會將數據邏輯地址傳給磁碟,磁碟的控制電路按照定址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數據在哪個磁軌,哪個扇區。為了讀取這個扇區的數據,需要將磁頭放到這個扇區上方,為了實現這一點,磁頭需要移動對準相應磁軌,這個過程叫做尋道,所耗費時間叫做尋道時間,然後磁碟旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間,最後便是對讀取數據的傳輸。 所以每次讀取數據花費的時間可以分為尋道時間、旋轉延遲、傳輸時間三個部分。其中:

  • 尋道時間是磁臂移動到指定磁軌所需要的時間,主流磁碟一般在5ms以下。
  • 旋轉延遲就是我們經常聽說的磁碟轉速,比如一個磁碟7200轉,表示每分鐘能轉7200次,也就是說1秒鐘能轉120次,旋轉延遲就是1/120/2 = 4.17ms。
  • 傳輸時間指的是從磁碟讀出或將數據寫入磁碟的時間,一般在零點幾毫秒,相對於前兩個時間可以忽略不計。

那麼訪問一次磁碟的時間,即一次磁碟IO的時間約等於5+4.17 = 9ms左右,聽起來還挺不錯的,但要知道一臺500 -MIPS的機器每秒可以執行5億條指令,因為指令依靠的是電的性質,換句話說執行一次IO的時間可以執行40萬條指令,資料庫動輒十萬百萬乃至千萬級數據,每次9毫秒的時間,顯然是個災難。

3.4 局部性原理與磁碟預讀

由於存儲介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,磁碟的存取速度往往是主存的幾百分分之一,因此為了提高效率,要儘量減少磁碟I/O。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的數據放入記憶體。這樣做的理論依據是電腦科學中著名的局部性原理:當一個數據被用到時,其附近的數據也通常會馬上被使用。程式運行期間所需要的數據通常比較集中。

由於磁碟順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有局部性的程式來說,預讀可以提高I/O效率。預讀的長度一般為頁(page)的整倍數。頁是電腦管理存儲器的邏輯塊,硬體及操作系統往往將主存和磁碟存儲區分割為連續的大小相等的塊,每個存儲塊稱為一頁(在許多操作系統中,頁得大小通常為4k),主存和磁碟以頁為單位交換數據。當程式要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤信號,磁碟會找到數據的起始位置並向後連續讀取一頁或幾頁載入記憶體中,然後異常返回,程式繼續運行。

四、資料庫索引所採用的數據結構B-/+Tree及其性能分析

到這裡終於可以分析為何資料庫索引採用B-/+Tree存儲結構了。上文說過資料庫索引是存儲到磁碟的而我們又一般以使用磁碟I/O次數來評價索引結構的優劣。先從B-Tree分析,根據B-Tree的定義,可知檢索一次最多需要訪問h-1個節點(根節點常駐記憶體)。資料庫系統的設計者巧妙利用了磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。為了達到這個目的,在實際實現B-Tree還需要使用如下技巧:每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁里,加之電腦存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。

B-Tree中一次檢索最多需要h-1次I/O(根節點常駐記憶體),漸進複雜度為O(h)=O(logdN)。一般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)。

綜上所述,如果我們採用B-Tree存儲結構,搜索時I/O次數一般不會超過3次,所以用B-Tree作為索引結構效率是非常高的。

4.1 B+樹性能分析

從上面介紹我們知道,B樹的搜索複雜度為O(h)=O(logdN),所以樹的出度d越大,深度h就越小,I/O的次數就越少。B+Tree恰恰可以增加出度d的寬度,因為每個節點大小為一個頁大小,所以出度的上限取決於節點內key和data的大小:

dmax=floor(pagesize/(keysize+datasize+pointsize))//floor表示向下取整

  由於B+Tree內節點去掉了data域,因此可以擁有更大的出度,從而擁有更好的性能。

4.2 B+樹查找過程

這裡寫圖片描述
B-樹和B+樹查找過程基本一致。如上圖所示,如果要查找數據項29,那麼首先會把磁碟塊1由磁碟載入到記憶體,此時發生一次IO,在記憶體中用二分查找確定29在17和35之間,鎖定磁碟塊1的P2指針,記憶體時間因為非常短(相比磁碟的IO)可以忽略不計,通過磁碟塊1的P2指針的磁碟地址把磁碟塊3由磁碟載入到記憶體,發生第二次IO,29在26和30之間,鎖定磁碟塊3的P2指針,通過指針載入磁碟塊8到記憶體,發生第三次IO,同時記憶體中做二分查找找到29,結束查詢,總計三次IO。真實的情況是,3層的b+樹可以表示上百萬的數據,如果上百萬的數據查找只需要三次IO,性能提高將是巨大的,如果沒有索引,每個數據項都要發生一次IO,那麼總共需要百萬次的IO,顯然成本非常非常高。

這一章從理論角度討論了與索引相關的數據結構與演算法問題,下一章將討論B+Tree是如何具體實現為MySQL中索引,同時將結合MyISAM和InnDB存儲引擎介紹非聚集索引和聚集索引兩種不同的索引實現形式。

五、MySQL索引實現

在MySQL中,索引屬於存儲引擎級別的概念,不同存儲引擎對索引的實現方式是不同的,本文主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式。

5.1 MyISAM索引實現

MyISAM引擎使用B+Tree作為索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖: 
這裡寫圖片描述

這裡設表一共有三列,假設我們以Col1為主鍵,則上圖是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重覆。如果我們在Col2上建立一個輔助索引,則此索引的結構如下圖所示: 
這裡寫圖片描述

同樣也是一顆B+Tree,data域保存數據記錄的地址。因此,MyISAM中索引檢索的演算法為首先按照B+Tree搜索演算法搜索索引,如果指定的Key存在,則取出其data域的值,然後以data域的值為地址,讀取相應數據記錄。 
MyISAM的索引方式也叫做“非聚集”的,之所以這麼稱呼是為了與InnoDB的聚集索引區分。

5.2 InnoDB索引實現

雖然InnoDB也使用B+Tree作為索引結構,但具體實現方式卻與MyISAM截然不同。

第一個重大區別是InnoDB的數據文件本身就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。 
這裡寫圖片描述

上圖是InnoDB主索引(同時也是數據文件)的示意圖,可以看到葉節點包含了完整的數據記錄。這種索引叫做聚集索引。因為InnoDB的數據文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識數據記錄的列作為主鍵,如果不存在這種列,則MySQL自動為InnoDB表生成一個隱含欄位作為主鍵,這個欄位長度為6個位元組,類型為長整形。

第二個與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作為data域。例如,下圖為定義在Col3上的一個輔助索引: 
這裡寫圖片描述

這裡以英文字元的ASCII碼作為比較準則。聚集索引這種實現方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。

瞭解不同存儲引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了InnoDB的索引實現後,就很容易明白為什麼不建議使用過長的欄位作為主鍵,因為所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的欄位作為主鍵在InnoDB中不是個好主意,因為InnoDB數據文件本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時數據文件為了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增欄位作為主鍵則是一個很好的選擇。

下一章將具體討論這些與索引有關的優化策略。

六、索引使用策略及優化

MySQL的優化主要分為結構優化(Scheme optimization)和查詢優化(Query optimization)。本章討論的高性能索引策略主要屬於結構優化範疇。本章的內容完全基於上文的理論基礎,實際上一旦理解了索引背後的機制,那麼選擇高性能的策略就變成了純粹的推理,並且可以理解這些策略背後的邏輯。

6.1 聯合索引及最左首碼原理

聯合索引(複合索引)

首先介紹一下聯合索引。聯合索引其實很簡單,相對於一般索引只有一個欄位,聯合索引可以為多個欄位創建一個索引。它的原理也很簡單,比如,我們在(a,b,c)欄位上創建一個聯合索引,則索引記錄會首先按照A欄位排序,然後再按照B欄位排序然後再是C欄位,因此,聯合索引的特點就是:

  • 第一個欄位一定是有序的
  • 當第一個欄位值相等的時候,第二個欄位又是有序的,比如下表中當A=2時所有B的值是有序排列的,依次類推,當同一個B值得所有C欄位是有序排列的

    | A | B | C | 
    | 1 | 2 | 3 | 
    | 1 | 4 | 2 | 
    | 1 | 1 | 4 | 
    | 2 | 3 | 5 | 
    | 2 | 4 | 4 | 
    | 2 | 4 | 6 | 
    | 2 | 5 | 5 |

其實聯合索引的查找就跟查字典是一樣的,先根據第一個字母查,然後再根據第二個字母查,或者只根據第一個字母查,但是不能跳過第一個字母從第二個字母開始查。這就是所謂的最左首碼原理。

最左首碼原理

我們再來詳細介紹一下聯合索引的查詢。還是上面例子,我們在(a,b,c)欄位上建了一個聯合索引,所以這個索引是先按a 再按b 再按c進行排列的,所以:

以下的查詢方式都可以用到索引

select * from table where a=1;
select * from table where a=1 and b=2;
select * from table where a=1 and b=2 and c=3;

上面三個查詢按照 (a ), (a,b ),(a,b,c )的順序都可以利用到索引,這就是最左首碼匹配。

如果查詢語句是:

select * from table where a=1 and c=3; 那麼只會用到索引a。

如果查詢語句是:

select * from table where b=2 and c=3; 因為沒有用到最左首碼a,所以這個查詢是用不到索引的。

如果用到了最左首碼,但是順序顛倒會用到索引碼?

比如:

select * from table where b=2 and a=1;
select * from table where b=2 and a=1 and c=3;

如果用到了最左首碼而只是顛倒了順序,也是可以用到索引的,因為mysql查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃。但我們還是最好按照索引順序來查詢,這樣查詢優化器就不用重新編譯了。

首碼索引

除了聯合索引之外,對mysql來說其實還有一種首碼索引。首碼索引就是用列的首碼代替整個列作為索引key,當首碼長度合適時,可以做到既使得首碼索引的選擇性接近全列索引,同時因為索引key變短而減少了索引文件的大小和維護開銷。

一般來說以下情況可以使用首碼索引:

  • 字元串列(varchar,char,text等),需要進行全欄位匹配或者前匹配。也就是=‘xxx’ 或者 like ‘xxx%’
  • 字元串本身可能比較長,而且前幾個字元就開始不相同。比如我們對中國人的姓名使用首碼索引就沒啥意義,因為中國人名字都很短,另外對收件地址使用首碼索引也不是很實用,因為一方面收件地址一般都是以XX省開頭,也就是說前幾個字元都是差不多的,而且收件地址進行檢索一般都是like ’%xxx%’,不會用到前匹配。相反對外國人的姓名可以使用首碼索引,因為其字元較長,而且前幾個字元的選擇性比較高。同樣電子郵件也是一個可以使用首碼索引的欄位。
  • 前一半字元的索引選擇性就已經接近於全欄位的索引選擇性。如果整個欄位的長度為20,索引選擇性為0.9,而我們對前10個字元建立首碼索引其選擇性也只有0.5,那麼我們需要繼續加大首碼字元的長度,但是這個時候首碼索引的優勢已經不明顯,沒有太大的建首碼索引的必要了。

一些文章中也提到:

MySQL 首碼索引能有效減小索引文件的大小,提高索引的速度。但是首碼索引也有它的壞處:MySQL 不能在 ORDER BY 或 GROUP BY 中使用首碼索引,也不能把它們用作覆蓋索引(Covering Index)。

6.2 索引優化策略

  • 最左首碼匹配原則,上面講到了
  • 主鍵外檢一定要建索引
  • 對 where,on,group by,order by 中出現的列使用索引
  • 儘量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示欄位不重覆的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別欄位可能在大數據面前區分度就是0
  • 對較小的數據列使用索引,這樣會使索引文件更小,同時記憶體中也可以裝載更多的索引鍵
  • 索引列不能參與計算,保持列“乾凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的欄位值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
  • 為較長的字元串使用首碼索引
  • 儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可
  • 不要過多創建索引, 權衡索引個數與DML之間關係,DML也就是插入、刪除數據操作。這裡需要權衡一個問題,建立索引的目的是為了提高查詢效率的,但建立的索引過多,會影響插入、刪除數據的速度,因為我們修改的表數據,索引也需要進行調整重建
  • 對於like查詢,”%”不要放在前面。 
    SELECT * FROMhoudunwangWHEREunameLIKE'後盾%' -- 走索引 
    SELECT * FROMhoudunwangWHEREunameLIKE "%後盾%" -- 不走索引
  • 查詢where條件數據類型不匹配也無法使用索引 
    字元串與數字比較不使用索引; 
    CREATE TABLEa(achar(10)); 
    EXPLAIN SELECT * FROMaWHEREa="1" – 走索引 
    EXPLAIN SELECT * FROM a WHERE a=1 – 不走索引 
    正則表達式不使用索引,這應該很好理解,所以為什麼在SQL中很難看到regexp關鍵字的原因

參考文章:

http://blog.csdn.net/suifeng3051/article/details/49530299?locationNum=1 
http://tech.meituan.com/mysql-index.html 
https://yq.aliyun.com/articles/39841 
http://blog.csdn.net/lovelion/article/details/8462814

 轉載:https://yq.aliyun.com/articles/65126

 

建索引原則:

1.最左首碼匹配原則,非常重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調整。
2.=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優化器會幫你優化成索引可以識別的形式
3.儘量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示欄位不重覆的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別欄位可能在大數據面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不同,這個值也很難確定,一般需要join的欄位我們都要求是0.1以上,即平均1條掃描10條記錄
4.索引列不能參與計算,保持列“乾凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的欄位值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
5.儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 《Terraform 101 從入門到實踐》這本小冊在南瓜慢說官方網站和GitHub兩個地方同步更新,書中的示例代碼也是放在GitHub上,方便大家參考查看。 介紹了Terraform一些比較基礎的概念後,我們可以先瞭解一下Terraform的語法,也就是HCL的語法。 變數Variables 變數 ...
  • chatGPT最近突然又大火起來了,而且這次不是一般的火,帶有濃濃的商業氣息火了。各個互聯網大廠都開始進軍了,感覺要來一場ChatGPT的軍備競賽一樣,看看誰先獲取國內的地盤。 作為吃瓜群眾,我們也能個人使用ChatGPT,現在小捲來教大家更高級的玩法,就是用個人微信接入ChatGPT,個人微信變... ...
  • FASTJSON2項目使用了上面的技巧,其中JDKUtils和UnsafeUtils有上面技巧的實現: JDKUtils:https://github.com/alibaba/fastjson2/blob/fastcode_demo_20221218/core/src/main/java/com/... ...
  • 準備工作 Docker環境 Mongo資料庫 配置Mongo資料庫 ASP.NET6 集成Mongo 安裝MongoDB.Driver { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Wa ...
  • 最近遇到需求需要把控制台程式做成 windows服務 的形式運行。改成 windows服務 後,程式無法訪問共用網路文件,訪問提示“Access to the path '網路路徑' is denied.“,拒絕訪問,原因是當前服務的用戶憑證無許可權訪問,於是需要修改當前服務的用戶憑證,改為可訪問共用 ...
  • 簡單說下什麼是t4模版T4,即4個T開頭的英文字母組合:Text Template Transformation Toolkit。 T4(Text Template Transformation Toolkit)是微軟官方在VisualStudio 2008中開始使用的代碼生成引擎。在 Visual ...
  • 痞子衡嵌入式半月刊: 第 71 期 這裡分享嵌入式領域有用有趣的項目/工具以及一些熱點新聞,農曆年分二十四節氣,希望在每個交節之日準時發佈一期。 本期刊是開源項目(GitHub: JayHeng/pzh-mcu-bi-weekly),歡迎提交 issue,投稿或推薦你知道的嵌入式那些事兒。 上期回顧 ...
  • 日誌 錯誤日誌 錯誤日誌是mysql中最重要的日誌之一,它記錄了當mysqld啟動和停止時,以及伺服器在運行過程中發生任何嚴重錯誤時的相關信息,當資料庫出現任何故障導致無法正常使用時,建議首先查看此日誌。 該日誌是預設開啟的,預設存放目錄:/var/log/,預設的日誌文件名為mysql.log。查 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...