postgresql mysql索引分類及類型

  索引是指按表中某些關鍵屬性或表達式建立元組的邏輯順序,它是由一系列表元組的標識號組成的一個列表。

在關係數據庫中,索引是一種與表有關的數據庫結構,它可以使對應於表的SQL語句執行得更快。索引的作用相當於圖書的目錄,可以根據目錄中的頁碼快速找到所需的內容。

索引是一個單獨的、物理的數據庫結構,它是某個表中一列或若干列值的集合和相應的指向表中物理標識這些值的數據頁的邏輯指針清單。

索引的分類

按存儲結構區分:“聚集索引(又稱聚類索引,簇集索引)”,“分聚集索引(非聚類索引,非簇集索引)”

按數據唯一性區分:“唯一索引”,“非唯一索引”

按鍵列個數區分:“單列索引”,“多列索引”。

 

爲什麼要創建索引呢?這是因爲,創建索引可以大大提高系統的性能。

第一,通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。

第二,可以大大加快 數據的檢索速度,這也是創建索引的最主要的原因。

第三,可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。

第四,在使用分組和排序 子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。

第五,通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。

 

也許會有人要問:增加索引有如此多的優點,爲什麼不對錶中的每一個列創建一個索引呢?這種想法固然有其合理性,然而也有其片面性。雖然,索引有許多優點, 但是,爲表中的每一個列都增加索引,是非常不明智的。這是因爲,增加索引也有許多不利的一個方面。

 

第一,創建索引和維護索引要耗費時間,這種時間隨着數據 量的增加而增加。

第二,索引需要佔物理空間,除了數據表佔數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。

第三,當對錶中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。

 

索引是建立在數據庫表中的某些列的上面。因此,在創建索引的時候,應該仔細考慮在哪些列上可以創建索引,在哪些列上不能創建索引。一般來說,應該在這些列 上創建索引,例如:

 

在經常需要搜索的列上,可以加快搜索的速度;

在作爲主鍵的列上,強制該列的唯一性和組織表中數據的排列結構;

在經常用在連接的列上,這 些列主要是一些外鍵,可以加快連接的速度;

在經常需要根據範圍進行搜索的列上創建索引,因爲索引已經排序,其指定的範圍是連續的;

在經常需要排序的列上創 建索引,因爲索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;

在經常使用在WHERE子句中的列上面創建索引,加快條件的判斷速度。

 

PostgreSQL中五種索引方式:唯一,主鍵,多屬性,部分,表達式

            四種索引類型:B-Tree,Hash,Gist,GIN

CREATE INDEX用於創建索引,默認創建一個B-Tree

索引相關係統表:

每種索引類型在系統表pg_am中用一個元組記錄,共4個元組對應類型,其中記錄了可被調用的接口函數和相關屬性,共有13種函數類型。

每一個創建的索引,會在pg_class系統表中添加一個元組,同時在pg_index中添加一個元組。Pg_index用於記錄與索引有關的信息。

每一種索引類型並不直接設定其所操作的數據類型,而是由操作符系統表pg_opclass管理,而每一個操作符類引用了pg_opfamily一個元組

Pg_amop中存每個索引操作符集合(pg_opfamily)與具體操作符(pg_operator)關聯信息

Pg_amproc則存儲集合與其相關聯的支持過程或函數(pg_proc)關聯信息

例:Pg_proc中一個函數屬於pg_opfamily一個集合,則pg_amproc中就有一個元組記錄這個對應關係。

B-Tree

   M爲樹的階數,B-樹或爲空樹,否則滿足下列條件:

1.定義任意非葉子結點最多隻有M個兒子;且M>2;

2.根結點的兒子數爲[2, M];

3.除根結點以外的非葉子結點的兒子數爲[M/2, M];

4.每個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字,根節點至少一個關鍵字);

5.非葉子結點的關鍵字個數=指向兒子的指針個數-1;

6.非葉子結點的關鍵字:K[1], K[2], …, K[m-1],m<M+1;且K[i]< K[i+1] ;

7.非葉子結點的指針:P[1], P[2], …, P[m];其中P[1]指向關鍵字小於K[1]的子樹,P[m]指向關鍵字大於K[m-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹;

8.所有葉子結點位於同一層;

如:(M=3)

235341_zyCq_1160724.png

M=3的B-樹

B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,如果命中則結束,否則進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已經是葉子結點。

特性

1.關鍵字集合分佈在整顆樹中;

2.任何一個關鍵字出現且只出現在一個結點中;

3.搜索有可能在非葉子結點結束;

4.其搜索性能等價於在關鍵字全集內做一次二分查找

 

大規模數據存儲中,實現索引查詢這樣一個實際背景下,樹節點存儲的元素數量是有限的(如果元素數量非常多的話,查找就退化成節點內部的線性查找了),這樣導致二叉查找樹結構由於樹的深度過大而造成磁盤I/O讀寫過於頻繁,進而導致查詢效率低下,那麼如何減少樹的深度(當然是不能減少查詢的數據量),一個基本的想法就是:採用多叉樹結構(由於樹節點元素數量是有限的,自然該節點的子樹數量也就是有限的)。

B 樹是爲了磁盤或其它存儲設備而設計的一種多叉平衡查找樹

 

B+樹的定義

B+樹是應文件系統所需而出的一種B-樹的變型樹。一棵m階的B+樹和m階的B-樹的差異在於:

1.有n棵子樹的結點中含有n個關鍵字,每個關鍵字不保存數據,只用來索引,所有數據都保存在葉子節點。

2.所有的葉子結點中包含了全部關鍵字的信息,及指向含這些關鍵字記錄的指針,且葉子結點本身依關鍵字的大小自小而大順序鏈接。

3.所有的非終端結點可以看成是索引部分,結點中僅含其子樹(根結點)中的最大(或最小)關鍵字。

通常在B+樹上有兩個頭指針,一個指向根結點,一個指向關鍵字最小的葉子結點。

235445_OjpK_1160724.png

a) 爲什麼說B+-tree比B 樹更適合實際應用中操作系統的文件索引和數據庫索引?

1) B+-tree的磁盤讀寫代價更低

B+-tree的內部結點並沒有指向關鍵字具體信息的指針。因此其內部結點相對B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的需要查找的關鍵字也就越多。相對來說IO讀寫次數也就降低了。

舉個例子,假設磁盤中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體信息指針2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點需要2個盤快。而B+ 樹內部結點只需要1個盤快。當需要把內部結點讀入內存中的時候,B 樹就比B+ 樹多一次盤塊查找時間(在磁盤中就是盤片旋轉的時間)。

2) B+-tree的查詢效率更加穩定

由於非終結點並不是最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查找必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。

讀者點評

數據庫索引採用B+樹的主要原因是 B樹在提高了磁盤IO性能的同時並沒有解決元素遍歷的效率低下的問題。正是爲了解決這個問題,B+樹應運而生。B+樹只要遍歷葉子節點就可以實現整棵樹的遍歷。而且在數據庫中基於範圍的查詢是非常頻繁的,而B樹不支持這樣的操作(或者說效率太低)。

235511_H0RR_1160724.png

  看完索引的整體邏輯過程(創建,插入,掃描,刪除)感覺整體性很強,都是有一個總流程,之後函數+數據結構實現相關模塊,邏輯性較強,涉及函數較多。

  創建索引: 將待索引的表元組封裝爲索引元組,並進行排序,之後填充入葉子節點,填充滿則申請新的葉子節點,並在父節點中添加指針,重複該過程。

  插入過程中,主要涉及查找及可能進行的葉子節點分裂。其中,爲了減少分裂操作,設置一個百分比填充因子(留下冗餘量),雖然帶來一定空間浪費,但可以避免過於頻繁的分裂。

Hash索引

   Hash相對比較熟悉。在hash表中,有一個函數以查找鍵爲參數,計算出0~b-1(b是桶的數目)

   B的數目是否變化,分爲靜態與動態。PostgreSQL使用動態hash(可擴展/線性)

實現中包含兩種類型hash,一個是用作索引的外存,另一種用於內存查找

235537_IeFK_1160724.png

元頁記錄相關信息,每次對索引讀寫操作都要將元頁讀入內存

Hash表多個桶組成,每個桶含一或多個頁,每個桶第一頁稱爲桶頁,其他頁稱爲溢出頁。桶頁成組分配,每次分配2的冪次。0,1號初始化分配。每次分配的桶頁磁盤上連續存儲。溢出頁分配在每次分配整體後。(例:4~7桶中,4滿了,溢出頁加在7後)溢出頁和桶頁之間雙向鏈表,溢出頁一旦分配,一直存在,不會釋放物理空間(爲了快速計算桶標號)

位圖用於標記每個溢出頁是否可用

下面舉例說明hash表不斷插入時各種頁面分配情況:

①初始化。0號塊元頁,1,2號塊分配給0,1號桶,並在桶後分配位圖頁(3號塊)

235551_hxLy_1160724.png

②增加兩個溢出頁。磁盤號爲45.

235601_NAXB_1160724.png

 

③增加一個桶。增加2號桶需要分裂,其實增加了2,3號桶,但3號先不投入使用。

235613_zmsa_1160724.png

④爲2號桶增加溢出頁

235621_p74D_1160724.png

 

Hash的實現:構建,元組插入,溢出頁管理,hash表擴展

構建:初始化,之後掃描要索引的表,生成索引元組,最後插入到hash

溢出頁的分配回收需要考慮共享問題,設置排他鎖

Hash表擴展,每次插入,計算當前記錄r/桶數n,比例太大就要進行擴展和壓縮。

 

GiST

通用搜索樹,是一種平衡的樹狀結構訪問方法,相當於一個基礎模板,可以用它實現任意索引模式。它可以建立一種可擴展的索引結構,包括數據類型和查詢謂詞擴展。Gist接口提供了高層抽象,只要求實現基本語義,而不用理解數據庫內部工作機制。

可以根據相關領域設定數據或謂詞,多用於圖像處理

 

GIN

通用倒排索引,是一個存儲(key,posting list)集合的索引結構,key是鍵值,posting list是出現過key的位置,位置由“元組ID:位置”表示 如(”hello”,”14:17,…”)表示hello關鍵字在14號元組被索引屬性第17個位置出現。

通過這種結構可快速查找包含關鍵字的元組,特別適合支持全文搜索。

GIN包含四部分:

Entry:一個詞位/鍵值

Entry tree:在entry上構建的b樹

Posting tree:在一個entry出現的物理位置上構建的b樹

Postling list:entry物理位置列表

235634_6ySn_1160724.png

 

GIN索引適合靜態的數據因爲查找是迅速的. 對於動態數據, GiST 可以更快的更新. 具體來說, GiST索引在動態數據上是好用的並且如果單獨的字(詞位)在100,000以下也是快速的,然而GIN 索引在處理100,000詞位以上時是更好的但是更新就要慢點了.

 

TSearch2 全文搜索

全文檢索是指計算機索引程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程序就根據事先建立的索引進行查找,並將查找的結果反饋給用戶的檢索方式。這個過程類似於通過字典中的檢索字表查字的過程。提供了可檢索出滿足某條件自然語言文檔的能力,並可根據相關性排序

步驟:1)對文本預處理,得到結果,創建索引

      2)解析查詢條件,使用索引查詢

      3)得到結果,處理返回

預處理:文本解析,語義分析,詞位存儲

235728_JkqP_1160724.png

 

擴展:

(一)B-Tree索引和Hash索引的區別 

Hash 索引結構的特殊性,其檢索效率非常高,索引的檢索可以一次定位,不像B-Tree 索引需要從根節點到枝節點,最後才能訪問到頁節點這樣多次的IO訪問,所以 Hash 索引的查詢效率要遠高於 B-Tree 索引。

 

可能很多人又有疑問了,既然 Hash 索引的效率要比 B-Tree 高很多,爲什麼大家不都用 Hash 索引而還要使用 B-Tree 索引呢?任何事物都是有兩面性的,Hash 索引也一樣,雖然 Hash 索引效率高,但是 Hash 索引本身由於其特殊性也帶來了很多限制和弊端,主要有以下這些。

 

(1)Hash 索引僅僅能滿足"=","IN"和"<=>"查詢,不能使用範圍查詢。

 

由於 Hash 索引比較的是進行 Hash 運算之後的 Hash 值,所以它只能用於等值的過濾,不能用於基於範圍的過濾,因爲經過相應的 Hash 算法處理之後的 Hash 值的大小關係,並不能保證和Hash運算前完全一樣。

 

(2)Hash 索引無法被用來避免數據的排序操作。

 

由於 Hash 索引中存放的是經過 Hash 計算之後的 Hash 值,而且Hash值的大小關係並不一定和 Hash 運算前的鍵值完全一樣,所以數據庫無法利用索引的數據來避免任何排序運算;

 

(3)Hash 索引不能利用部分索引鍵查詢。

 

對於組合索引,Hash 索引在計算 Hash 值的時候是組合索引鍵合併後再一起計算 Hash 值,而不是單獨計算 Hash 值,所以通過組合索引的前面一個或幾個索引鍵進行查詢的時候,Hash 索引也無法被利用。

 

(4)Hash 索引在任何時候都不能避免表掃描。

 

前面已經知道,Hash 索引是將索引鍵通過 Hash 運算之後,將 Hash運算結果的 Hash 值和所對應的行指針信息存放於一個 Hash 表中,由於不同索引鍵存在相同 Hash 值,所以即使取滿足某個 Hash 鍵值的數據的記錄條數,也無法從 Hash 索引中直接完成查詢,還是要通過訪問表中的實際數據進行相應的比較,並得到相應的結果。

 

(5)Hash 索引遇到大量Hash值相等的情況後性能並不一定就會比B-Tree索引高。

 

對於選擇性比較低的索引鍵,如果創建 Hash 索引,那麼將會存在大量記錄指針信息存於同一個 Hash 值相關聯。這樣要定位某一條記錄時就會非常麻煩,會浪費多次表數據的訪問,而造成整體性能低下。

 

hash相當於把key通過hash函數計算,得到key的hash值,再用這個hash值做指針,查找hash表中是否存在key,如果存在就返回 key所對應的value,選定一個好的hash函數很重要,好的hash函數可以使計算出的hash值分佈均勻,降低衝突,只有衝突減小了,纔會降低 hash表的查找時間。

 

b-tree完全基於key的比較,和二叉樹相同的道理,相當於建個排序後的數據集,使用二分法查找算法,實際上也非常快,而且受數據量增長影響非常小。

(二)mysql索引

索引策略決定數據庫快速定位數據的效率,存儲策略決定數據持久化的效率。

MySQL中兩大主要存儲引擎MyISAM和InnoDB採用了不同的索引和存儲策略,本文將分析它們的異同和性能。

 

MySQL主要提供2種方式的索引:B-Tree(包括B+Tree)索引,Hash索引。

B樹索引具有範圍查找和前綴查找的能力,對於N節點的B樹,檢索一條記錄的複雜度爲O(LogN)。

哈希索引只能做等於查找,但是無論多大的Hash表,查找複雜度都是O(1)。

顯然,如果值的差異性大,並且以等於查找爲主,Hash索引是更高效的選擇,它有O(1)的查找複雜度。如果值的差異性相對較差,並且以範圍查找爲主,B樹是更好的選擇,它支持範圍查找。

MyISAM索引實現

 

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

235820_FPgW_1160724.png

這裏設表一共有三列,假設我們以Col1爲主鍵,則圖8是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在Col2上建立一個輔助索引,則此索引的結構同樣也是一顆B+Tree,data域保存數據記錄的地址。因此,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然後以data域的值爲地址,讀取相應數據記錄。

 

MyISAM的索引方式也叫做“非聚集”的,之所以這麼稱呼是爲了與InnoDB的聚集索引區分。

 

InnoDB索引實現

 

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

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

235834_TYyn_1160724.png

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

 

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

235844_3vLZ_1160724.png

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

 

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

 

 

實際上,您可以把索引理解爲一種特殊的目錄。微軟的SQL   SERVER提供了兩種索引:聚集索引(clustered   index,也稱聚類索引、簇集索引)和非聚集索引(nonclustered   index,也稱非聚類索引、非簇集索引)。下面,我們舉例來說明一下聚集索引和非聚集索引的區別:  

   

  其實,我們的漢語字典的正文本身就是一個聚集索引。比如,我們要查“安”字,就會很自然地翻開字典的前幾頁,因爲“安”的拼音是“an”,而按照拼音排序漢字的字典是以英文字母“a”開頭並以“z”結尾的,那麼“安”字就自然地排在字典的前部。如果您翻完了所有以“a”開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字;同樣的,如果查“張”字,那您也會將您的字典翻到最後部分,因爲“張”的拼音是“zhang”。也就是說,字典的正文部分本身就是一個目錄,您不需要再去查其他目錄來找到您需要找的內容。  

   

  我們把這種正文內容本身就是一種按照一定規則排列的目錄稱爲“聚集索引”。  

   

  如果您認識某個字,您可以快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛纔的方法找到您要查的字,而需要去根據“偏旁部首”查到您要找的字,然後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合“部首目錄”和“檢字表”而查到的字的排序並不是真正的正文的排序方法,比如您查“張”字,我們可以看到在查部首之後的檢字表中“張”的頁碼是672頁,檢字表中“張”的上面是“馳”字,但頁碼卻是63頁,“張”的下面是“弩”字,頁面是390頁。很顯然,這些字並不是真正的分別位於“張”字的上下方,現在您看到的連續的“馳、張、弩”三字實際上就是他們在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我們可以通過這種方式來找到您所需要的字,但它需要兩個過程,先找到目錄中的結果,然後再翻到您所需要的頁碼。  

   

  我們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱爲“非聚集索引”。 

                                    </div>
                                        </div>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章