數據庫 - 索引 index


InnoDB支持的:B樹索引、哈希索引、集羣索引、聚簇索引、覆蓋索引;

什麼是索引

1、什麼是索引:
索引是對數據庫表中一列或多列的值進行排序的一種結構,使用索引可以快速訪問數據庫表中的特定的信息;

通俗的講,數據庫索引就是爲了提高表的搜索效率而對某些字段中的值建立的目錄;
它是一個獨立的表,一條索引記錄要包含的基本信息有鍵值和邏輯指針;

  • 鍵值:定義索引時指定的所有字段的值;
  • 邏輯指針:指向數據頁或另一個索引頁;

2、爲什麼要使用索引:
使用索引可以優化查詢,加快訪問速度,減少磁盤IO;但是不能優化DML操作;

不使用索引時,數據庫只能一行一行掃描所有記錄,然後判斷該記錄是否滿足查詢條件;
(1)使用索引可以避免全表掃描;多數查詢可以只掃描少量索引頁及數據頁,而不是遍歷所有數據頁;
(2)對於非聚集索引,有些查詢甚至可以不訪問數據頁,就能獲取到查詢的數據;
如果SQL語句僅訪問被索引的列,那麼數據庫只需從索引中讀取數據,而不用讀取表;
如果該語句同時還要訪問非索引列,那麼Oracle會使用rowid來查找表中的行;
(3)聚集索引可以避免數據插入操作集中於表的最後一個數據頁;
(4)一些情況下,索引還可用於避免排序操作;

3、使用索引的不足:
(1)創建索引和維護索引要耗費時間,這個時間隨着數據量的增加而增加;
(2)索引是一個獨立的數據表,需要佔用物理空間,建立聚簇索引佔用的空間更大;
(3)對錶中的數據進行增加、修改、刪除時,索引也要動態維護;這就導致了數據庫更新數據的性能下降了;

4、什麼時候使用索引:
(1)表經常進行select查詢操作;在經常需要搜索的列上創建索引;
(2)列名經常在where子句或連接條件中出現;
(3)表記錄很多,記錄的內容分佈範圍很廣,離散度高的列,返回的行數小於5%;
(4)更新鍵值代價低,不是頻繁更新的;
(5)使用邏輯AND/OR效率高;


三星系統

1、索引將相關的記錄放到一起;
2、索引中的數據順序和查找中的排列順序一致;
3、索引中的列包含看查詢中需要的全部列;


存儲索引的數據結構

在MySQL中,索引時存儲在引擎層的而不是服務器層實現的,所以不同存儲引擎支持不同的索引,多個存儲引擎支持同一個索引,其底層實現也可能不同;

B-樹索引

B+樹【InnoDB使用的是B+樹;】

B-樹索引 使用B樹數據結構來存儲數據;

B樹的每個節點都是一個索引頁;每個節點上存放m-1個key和m個指向子節點的指針,每個key是一條索引記錄,

B-Tree索引所有的值都是按順序存儲的,並且每一個葉子到根的距離相同;

B樹索引能加快訪問數據的速度,因爲存儲引擎不用再進行全表掃描來獲取需要的數據,而是從索引根節點開始搜索,根節點中存放了索引的數據和指向子節點的指針,若根節點頁中沒有找到需要的索引數據,則根據指針向下層子節點頁中繼續查找;通過比較要查找的值和節點頁的值,可以找到合適的指針進入下層子節點頁;

指針實際上定義了子節點頁中值的上下限;

葉子結點的指針指向被索引的數據,而不是其他節點頁;

在這裏插入圖片描述

在這裏插入圖片描述

create table people(
	last_name  varchar(50)     not null,
	first_name  varchar(50)     not null,
	dbo    	   date            not null,
	gender     enum('m', 'f')  not null,
	key(last_name, first_name, dbo)         // 定義複合索引
);

索引對多個值進行排序的順序是依據創建表時定義索引時列的順序;

例如:在一個節點頁中存儲的兩個key中存儲的姓、名都相同,則根據出生日期進行排序;

B樹索引適用於全鍵值、鍵值範圍、鍵最左前綴查找;

  • 全鍵值匹配:指的是和索引中所有列進行匹配;
    例如:可以指定姓、名、出生日期;
  • 鍵值範圍匹配:可以指定姓在某一範圍之間;
  • 鍵最左前綴匹配:可以只使用索引的第一列進行匹配;
    例如:根據姓來查找索引值;
  • 鍵最左列前綴匹配:只使用索引第一列的一部分進行匹配;
    例如:姓爲xyz時,可以只指定x;
  • 精確匹配第一列,範圍匹配第二列

B-樹索引限制:
1、如果不是按照索引最左列開始查找,則無法使用索引:無法查找指定名的的值;

2、不能跳過索引中的列:只指定姓、出生日期,不指定中間的名列,那麼就使用指定的索引的第一列;

3、若查詢中第一列使用範圍查找,第二列使用全值查找,則只能使用第一列;

SO,定義索引時指定的列的順序很重要!優化性能時,可以使用相同的列但順序不同的索引來滿足不同類型的需求;

哈希索引

哈希索引是基於哈希表實現的,只有精確匹配索引的所有列的查詢纔有效;對索引表的每一行數據,存儲引擎都會對所有的索引列計算一個哈希值,不同鍵值的行計算出來的哈希值不一樣,但是也會有衝突存在;哈希索引會將所有的哈希值存儲在索引中,同時在哈希表中保存指向每個數據行的指針;

有的存儲引擎支持非唯一哈希索引:如果有多列的哈希值相同,索引會以鏈表的方式存放多個記錄指針到同一個哈希條目中;

在這裏插入圖片描述
哈希索引的數據結構:每個節點存儲索引行的哈希值和指向數據表中行記錄的指針;哈希值是順序存儲的,但是行記錄指針不是有序的;

使用哈希索引存儲時,只支持精確匹配索引的所有列,查找記錄時,根據指定的索引列的值計算哈希值,然後根據哈希值查找對應的記錄指針,最後根據指針指向的行記錄的值,比較是否匹配查詢條件,以確保就是要查找的行;

哈希索引的限制:
1、哈希索引只包含哈希值和行指針,而不存儲字段值,所以不能使用索引中的值來避免讀取行;

2、哈希索引數據不是按照索引值順序存儲的,所以無法用於排序;

3、哈希索引不支持部分索引列匹配查找,因爲哈希索引存儲的哈希值是使用索引列的全部內容計算出來的;

4、哈希索引只支持的等值比較查詢:=in()<=>,不支持範圍查詢:where age > 20;

5、訪問哈希索引的數據的速度快,除非哈希衝突多,因爲出現哈希衝突時,存儲引擎需要遍歷鏈表中存儲的所有行指針,逐一對比行記錄的值是否符合查詢條件;

6、若哈希衝突很多的話,維護索引的代價就大;比如刪除一行時,需要遍歷對應哈希值的鏈表中的每個行指針;

在使用哈希索引進行查詢時,必須在where子句中包含常量值:select id from url where url_crc=CRC32("http://www.mysql.com") and url="http://www.mysql.com";
根據url_crc哈希值查找行指針,若出現哈希衝突,就根據url的值,與鏈表中存儲的所有行指針指向的行記錄值進行對比,找出對應的行記錄;

位圖索引 (bitmap) (oracle)

空間數據索引 (R-Tree)

全文索引


索引分類

聚集索引與非聚集索引

一般索引(oracle)

索引鍵值可重複;

唯一索引

唯一索引:不允許任意兩行具有相同索引值的索引;唯一索引的值不重複,但是可以有一個null;

注意:

  • 若爲已經存在的表的某列創建唯一索引,如果該列中有重複的值,那麼不能在該列上創建索引;
  • 若創建表時指定索引列,那麼在向表中添加記錄時,索引列的值不能重複;也就是說若將學生表的name列設置爲索引列,那麼添加記錄時,添加已經存在的name的記錄會失敗;
  • 創建表時,若爲某字段設置了唯一性約束,那麼數據庫會隱式的爲該列創建唯一索引,索引的名稱就是列的名稱;
  • 可以爲某一字段設置兩個唯一索引,只要索引名字不同即可;
    create unique index name1 on student(name);
    create unique index name2 on student(name);

主鍵索引

主鍵索引:數據庫爲表定義主鍵時將會自動創建主鍵索引,主鍵索引是唯一索引的特定類型;該索引要求主鍵中每個值都唯一,但是不能爲空;在查詢中使用主鍵索引時,還允許對數據的快速訪問;

複合索引

包含多個字段的索引,稱爲複合索引;

索引最多可以包含31個字段,索引記錄最大長度爲600B;

聚集索引 (Clustered)

聚集索引也是一種數據存儲方式,InnoDB的聚集索引是在在同一個結構中保存了B-樹索引和數據行;

聚集索引:表中數據按照索引的順序來存儲的;對於聚集索引,葉子結點也就是數據結點;葉子節點存儲了真是的數據行,不再有另外的數據頁;非葉子結點只存儲索引列的值;葉子結點存儲所有列的值;

  • 一張表中只能創建一個聚集索引,因爲真實數據的物理順序只能有一種;如果存在聚集索引,就不能再指定CLUSTERED 關鍵字;
  • 如果一張表沒有聚集索引,那麼它就被稱爲“堆集”(heap),所有的新行都被添加到表的末尾位置;
  • InnoDB將主鍵作爲聚集索引,如果沒有顯示定義主鍵,那麼就會選擇一個非空的唯一索引代替,如果沒有這樣的索引,InnoDB會隱式的定義一個主鍵來作爲聚集索引;

在這裏插入圖片描述

(1)聚集索引查詢操作:
如上圖,在名字字段建立聚集索引,當需要根據此字段查找特定記錄時,會從此索引的根結點開始查找,根結點沒有,就根據指針查找下一個子節點,知道找到或返回查找失敗;

例如:查找“green”,在索引頁1001中沒有,但是它介於[Bennet,Karsen],據此找到了索引頁1007,在該頁中,Green介於[Greane, Hunter]間,據此找到葉子結點1133,在此頁中找到了目標數據行,該行存儲了所有字段的值,不只是索引字段的值;

此次查詢的IO操作包括3個索引頁的插敘(最後一個其實是在數據頁中查詢),這裏的查詢可能是從磁盤讀取,或只從緩存中讀取的,如果此表訪問頻率比較高,那麼索引樹中較高層的索引可能是從緩存中讀取的,所以真正的IO可能小於上面的情況;

(2)聚集索引插入操作:
最簡單的情況下,插入操作根據索引找到對應的數據頁,然後通過挪動已有記錄爲新數據騰出空間,最後插入數據;

若數據頁已經滿了,則需要拆分數據頁;(頁拆分比較耗費資源,所以數據庫系統中會有相應的機制儘量減少頁拆分的次數,通常是爲每頁預留空間)

(3)聚集索引刪除操作:
刪除操作將導致下方的數據行向上移動以填充空白記錄造成的空白;如果刪除的行是數據頁的最後一行,那麼該頁將會被回收,相應的索引頁中的記錄將被刪除;

數據的刪除操作,可能會導致索引頁中僅有一條記錄,這時,該記錄可能會被移到臨近的索引頁中,原索引頁會被回收,這就是“索引合併”;

非聚集索引 (Non-clustered)

非聚集索引:表數據的存儲順序與索引的順序無關;對於非聚集索引,葉子結點存儲的是索引字段的值和指向數據頁數據行的指針,而不是存儲所有字段的值;該層緊鄰數據頁,其行數與數據錶行數一致;

  • 一個表中只能有一個聚集索引,但是表中的每一列都可以有自己的非聚集索引;

(1)非聚集索引查詢操作:
先查找索引頁,找打葉子結點之後,根據葉子結點中記錄存儲的指針查找對應的數據頁,在數據頁中找到需要的數據行;

(2)非聚集索引插入操作:
若一張表中包含非聚集索引,但是沒有聚集索引,那麼新數據將被插入到最後一個數據頁中,然後更新非聚集索引;

若包含聚集索引,那麼就會根據聚集索引查找數據的插入位置,然後更新聚集索引和非聚集索引;

(3)非聚集索引刪除操作:
如果在刪除命令的Where子句中包含的列上,建有非聚集索引,那麼該非聚集索引將被用於查找數據行的位置,數據刪除之後,位於索引葉子上的對應記錄也將被刪除。如果該表上有其它非聚集索引,則它們葉子結點上的相應數據也要刪除。

如果刪除的數據是該數所頁中的唯一一條,則該頁也被回收,同時需要更新各個索引樹上的指針。

由於沒有自動的合併功能,如果應用程序中有頻繁的隨機刪除操作,最後可能導致表包含多個數據頁,但每個頁中只有少量數據;

覆蓋索引

覆蓋索引:非聚集索引的葉子節點包含所有數據行中的索引列值,使用這些節點就可以返回查詢所需要的真正的數據,而不需要訪問數據頁,這種情況就稱爲索引覆蓋;

如果在多個字段上創建了一個符合的非聚集索引,並且查詢中所需要的select字段,以及where、order by、group by、having子句中所涉及到的字段都包含在索引列中,那麼只所搜索引頁就能查到所需數據,而不用再訪問數據頁了;

在索引覆蓋的情況下,包含兩種掃描:匹配索引掃描、非匹配索引掃描;

索引覆蓋要求索引列要包含查詢中設計的所有字段,另外,若where子句中的查詢條件符合“全鍵值”、“鍵前綴查找”等的要求,就可以使用匹配索引掃描,否則只能使用非匹配索引掃描;非匹配索引掃描將掃描索引樹上的所有葉子結點;

聚集索引與非聚集索引的區別

  • 一個表中只能有一個聚集索引,但是可以有 多個非聚集索引;
  • 聚集索引中索引的順序與數據表中行的物理順序相同,非聚集索引中索引順序與表中行記錄順序無關;
  • 聚集索引中節點存儲的是完整的行記錄數據,不再有數據表,非聚集索引中葉子結點中存儲的是索引列的值和執行數據表中對應行的指針;
  • 聚集索引插入數據時,先根據索引找到插入位置再插入,非聚集索引直接在最後一個數據頁中插入,然後更新索引表;
  • 聚集索引數據訪問速度更快;

各種索引特點:

  • 唯一索引字段值不重複,可以有一個爲null;主鍵索引字段值不重複,不能有null;
  • 一個表只能有一個聚集索引,但是可以有多個非聚集索引、唯一索引;
  • 聚集索引一定是唯一索引,唯一索引不一定是聚集索引;
  • 唯一索引有助於定位信息,但是主鍵索引性能更好;

創建 / 修改 / 刪除 索引

MySQL

unique:唯一索引;
clustered:聚集索引;
noclustered:非聚集索引;

索引名字要唯一,一般格式是“表名_字段1名_字段2名

  • 爲已存在的表創建索引:
    create unique/clustered/noclustered index indexName on tableName(列名1 asc, 列名2 desc, ...);
  • 修該表:alter table tableName add unique indexName(列的列表);
  • 創建表時指定索引:create table tableName([...], unique uniqueName (列的列表));

修改索引名字:alter index oldIndexName rename to newIndexName;

刪除索引:drop index indexName;

Oracle

查看索引建在哪表、列:
select * from user_indexes;
select * from user_ind_columns;


索引碎片問題 (oracle)

一張表上線使用很久之後,期間由於對基表做DML操作,導致索引表塊的自動更改操作,尤其是基表的delete操作,會引起index表的index_entries的邏輯刪除,而只有當一個索引塊中的所有index_entry都被刪除了,這個索引塊纔會被刪除;索引對基表的deleteinsert操作都會產生索引碎片問題;時間越長,索引碎片就會越來越大,查詢時間會越來越長、效率越來越低;

在Oracle裏面,可以通過查看index_stats視圖,當發生以下三種情形之一時,就說明索引碎片該整理了:
(1)Height >= 4
(2)pct_used < 50%
(3)del_lf_rows / lf_rows > 0.2

1、使用非管理員賬號創建表t,在表t的id列創建索引,並向表中insert一百萬條記錄:

create table t(id int);
create index ind_1 on t(id);
begin for i in 1..1000000 loop insert into t values(i); if mod(i, 100) = 0 then commit; end if;end loop;end;

2、查看索引建立在哪個列:select * from user_ind_columns;

3、分析索引使用情況:analyze index ind_1 validate structure;
(注意:每次查看index_stats視圖之前都要先分析分析一次,執行分析操作之後,纔會把分析的結果插入到index_stats視圖中,才能查看到最新的索引使用情況)

4、查看索引使用情況:select name, height, pct_used, del_lf_rows/lf_rows from index_stats;

5、整理索引碎片(在線分析整理):alter index ind_1 rebuild [online] [ts_test];
注:以前整理索引碎片,要先停服務,把表鎖住,然後把數據導入到另外一張新表,然後再整理;現在可以在線整理索引,不需要停服務;

6、若出現ORA-00439: feature not enabled: Online Index Build , Time: 0.000000sselect * from v$option where parameter = 'Online Index Build'; false表示沒有開啓在線整理索引碎片的功能,或者Oracle版本不支持;

7、若沒開啓在線整理索引碎片功能,那就不在線:alter index ind_1 rebuild

8、先分析,再查看;

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