mysql 索引相關(InnoDB存儲引擎)

索引的常見模型 

索引的出現是爲了提高查詢效率,但是實現索引的方式卻有很多種,所以這裏也就引入了索引模型的概念。可以用於提高讀寫效率的數據結構很多,介紹三種常見、也比較簡單的數據結構,它們分別是哈希表、有序數組和搜索樹。

  • 哈希表 

       哈希表是一種以鍵-值(key-value)存儲數據的結構,我們只要輸入待查找的值即key,就可以找到其對應的值即Value。哈希的思路很簡單,把值放在數組裏,用一個哈希函數把key換算成一個確定的位置,然後把value放在數組的這個位置。不可避免地,多個key值經過哈希函數的換算,會出現同一個值的情況。處理這種情況的一種方法是,拉出一個鏈表。

時間複雜度:查詢/插入/修改/刪除的平均時間複雜度都是O(1)。

哈希表這種結構適用於只有等值查詢的場景, 但是對於排序查詢如,分組:group by、排序:order by 、比較< 、>、等時間複雜度會退化爲O(n)。(InnoDB 並不支持哈希索引,數據庫自身會創建和使用自適應哈希索引)。

  • 有序數組

     按照給定的順序排好序的數組。即索引是一個有序遞增的數組。

    時間複雜度:查詢用二分法就可以快速查詢,時間複雜度是:O(log(N))

    如果僅僅看查詢效率,有序數組就是最好的數據結構了。但是,在需要更新數據的時候就麻煩了,你往中間插入一個記錄就必      須得挪動後面所有的記錄,成本太高。所以,有序數組索引只適用於靜態存儲引擎,比如你要保存的是2017年某個城市的所         有人口信息,這類不會再修改的數據。

  • 搜索樹

       最簡單的是二叉搜索樹,然後有平衡二叉樹、B樹(B-Tree)和B+樹(B+Tree),甚至B樹。這些B樹、B+樹、B樹都是一棵自平衡的搜索樹,它類似普通的平衡二叉樹,不同的一點是B-樹允許每個節點有更多的子節點。B-樹是專門爲外部存儲器設計的,如磁盤,它對於讀取和寫入大塊數據有良好的性能,所以一般被用在文件系統及數據庫中。

  時間複雜度:平衡二叉搜索樹,查詢/插入/修改/刪除的平均時間複雜度都是O(lg(n));

 數據庫的索引最常用B+樹,主要有如下優點:

  • 很適合磁盤存儲,能夠充分利用局部性原理,磁盤預讀,數據預讀的思路是:磁盤讀寫並不是按需讀取,而是按頁預讀,一次會讀一頁的數據,每次加載更多的數據,以便未來減少磁盤IO。
  • 很低的樹高度,能夠存儲大量數據;
  • 索引本身佔用的內存很小;
  • 能夠很好的支持單點查詢,範圍查詢,有序性查詢,B+樹葉子節點之間增加了鏈表。

InnoDB 的索引模型

InnoDB使用了B+樹索引模型,所以數據都是存儲在B+樹中的。每一個索引在InnoDB裏面對應一棵B+樹。

數據庫中的B+書搜索模型可以分爲主鍵索引(又稱聚集索引)和 非主鍵索引(又稱輔助索引、非聚集索引、二級索引)

在B+樹種主鍵索引的葉子節點存儲的是整行的數據,非主鍵索引的葉子節點存儲的內容是主鍵的值。

InnoDB的主鍵索引與行記錄是存儲在一起的,故叫做聚集索引(Clustered Index):

  • 沒有單獨區域存儲行記錄

  • 主鍵索引的葉子節點,存儲主鍵,與對應行記錄(而不是指針)

因爲這個特性,InnoDB的表必須要有聚集索引:

(1)如果表定義了PK,則PK就是聚集索引;

(2)如果表沒有定義PK,則第一個非空unique列是聚集索引;

(3)否則,InnoDB會創建一個隱藏的row-id作爲聚集索引;

聚集索引,也只能夠有一個,因爲數據行在物理磁盤上只能有一份聚集存儲。

當使用非聚集索引查詢時,會先搜索非聚集索引樹查到主鍵的值,然後再通過聚集索引查詢主鍵值對應的行數據。回到主鍵索引樹搜索的過程,我們稱爲回表。也就是說,基於非主鍵索引的查詢需要多掃描一棵索引樹。因此,我們在應用中應該儘量使用主鍵查詢。

兩個索引性質:

1.索引字段要儘量的小:通過上面的分析,我們知道IO次數取決於b+數的高度h,假設當前數據表的數據爲N,每個磁盤塊的數據項的數量是m,則有h=㏒(m+1)N,當數據量N一定的情況下,m越大,h越小;而m = 磁盤塊的大小 / 數據項的大小,磁盤塊的大小也就是一個數據頁的大小,是固定的,如果數據項佔的空間越小,數據項的數量越多,樹的高度越低。這就是爲什麼每個數據項,即索引字段要儘量的小,比如int佔4字節,要比bigint8字節少一半。這也是爲什麼b+樹要求把真實的數據放到葉子節點而不是內層節點,一旦放到內層節點,磁盤塊的數據項會大幅度下降,導致樹增高。當數據項等於1時將會退化成線性表。
2.索引的最左匹配特性:當b+樹的數據項是複合的數據結構,比如(name,age,sex)的時候,b+數是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最後得到檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪個節點,因爲建立搜索樹的時候name就是第一個比較因子,必須要先根據name來搜索才能知道下一步去哪裏查詢。比如當(張三,F)這樣的數據來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失,所以只能把名字等於張三的數據都找到,然後再匹配性別是F的數據了, 這個是非常重要的性質,即索引的最左匹配特性

   聯合索引

聯合索引是指對錶上的多個列進行索引,聯合索引的創建方法與單個索引的創建方法一樣,不同之處僅在於有多個索引列。

mysql> create table t(
    -> a int,
    -> b int,
    -> primary key(a),
    -> key idx_a_b(a,b)
    -> );
Query OK, 0 rows affected (0.11 sec)

那麼何時需要使用聯合索引呢?在討論這個問題之前,先來看一下聯合索引內部的結果。從本質上來說,聯合索引就是一棵B+樹,不同的是聯合索引的鍵值得數量不是1,而是>=2。接着來討論兩個整型列組成的聯合索引,假定兩個鍵值得名稱分別爲a、b如圖

可以看到這與我們之前看到的單個鍵的B+樹並沒有什麼不同,鍵值都是排序的,通過葉子結點可以邏輯上順序地讀出所有數據,就上面的例子來說,即(1,1),(1,2),(2,1),(2,4),(3,1),(3,2),數據按(a,b)的順序進行了存放。

因此,對於查詢select * from table where a=xxx and b=xxx, 顯然是可以使用(a,b) 這個聯合索引的,對於單個列a的查詢select * from table where a=xxx,也是可以使用(a,b)這個索引的。

但對於b列的查詢select * from table where b=xxx,則不可以使用(a,b) 索引,其實你不難發現原因,葉子節點上b的值爲1、2、1、4、1、2顯然不是排序的,因此對於b列的查詢使用不到(a,b) 索引

聯合索引的第二個好處是在第一個鍵相同的情況下,已經對第二個鍵進行了排序處理。

覆蓋索引

 InnoDB存儲引擎支持覆蓋索引(covering index,或稱索引覆蓋),即從輔助索引中就可以得到查詢記錄,而不需要查詢聚集索引中的記錄。即可以避免回表的過程。

使用覆蓋索引的一個好處是:輔助索引不包含整行記錄的所有信息,故其大小要遠小於聚集索引,因此可以減少大量的IO操作。

 

補充:以下內容摘自https://www.cnblogs.com/Eva-J/articles/10126413.html#_label8

MySQL常用的索引

普通索引INDEX:加速查找

唯一索引:
    -主鍵索引PRIMARY KEY:加速查找+約束(不爲空、不能重複)
    -唯一索引UNIQUE:加速查找+約束(不能重複)

聯合索引:
    -PRIMARY KEY(id,name):聯合主鍵索引
    -UNIQUE(id,name):聯合唯一索引
    -INDEX(id,name):聯合普通索引

各個索引的應用場景

舉個例子來說,比如你在爲某商場做一個會員卡的系統。

這個系統有一個會員表
有下列字段:
會員編號 INT
會員姓名 VARCHAR(10)
會員身份證號碼 VARCHAR(18)
會員電話 VARCHAR(10)
會員住址 VARCHAR(50)
會員備註信息 TEXT

那麼這個 會員編號,作爲主鍵,使用 PRIMARY
會員姓名 如果要建索引的話,那麼就是普通的 INDEX
會員身份證號碼 如果要建索引的話,那麼可以選擇 UNIQUE (唯一的,不允許重複)

#除此之外還有全文索引,即FULLTEXT
會員備註信息 , 如果需要建索引的話,可以選擇全文搜索。
用於搜索很長一篇文章的時候,效果最好。
用在比較短的文本,如果就一兩行字的,普通的 INDEX 也可以。
但其實對於全文搜索,我們並不會使用MySQL自帶的該索引,而是會選擇第三方軟件如Sphinx,專門來做全文搜索。

#其他的如空間索引SPATIAL,瞭解即可,幾乎不用

創建/刪除索引的語法

#方法一:創建表時
      CREATE TABLE 表名 (
                字段名1  數據類型 [完整性約束條件…],
                字段名2  數據類型 [完整性約束條件…],
                [UNIQUE | FULLTEXT | SPATIAL ]   INDEX | KEY
                [索引名]  (字段名[(長度)]  [ASC |DESC]) 
                );


#方法二:CREATE在已存在的表上創建索引
        CREATE  [UNIQUE | FULLTEXT | SPATIAL ]  INDEX  索引名 
                     ON 表名 (字段名[(長度)]  [ASC |DESC]) ;


#方法三:ALTER TABLE在已存在的表上創建索引
        ALTER TABLE 表名 ADD  [UNIQUE | FULLTEXT | SPATIAL ] INDEX
                             索引名 (字段名[(長度)]  [ASC |DESC]) ;
                             
#刪除索引:DROP INDEX 索引名 ON 表名字;


#方式一
create table t1(
    id int,
    name char,
    age int,
    sex enum('male','female'),
    unique key uni_id(id),
    index ix_name(name) #index沒有key
);
create table t1(
    id int,
    name char,
    age int,
    sex enum('male','female'),
    unique key uni_id(id),
    index(name) #index沒有key
);


#方式二
create index ix_age on t1(age);


#方式三
alter table t1 add index ix_sex(sex);
alter table t1 add index(sex);

#查看
mysql> show create table t1;
| t1    | CREATE TABLE `t1` (
  `id` int(11) DEFAULT NULL,
  `name` char(1) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sex` enum('male','female') DEFAULT NULL,
  UNIQUE KEY `uni_id` (`id`),
  KEY `ix_name` (`name`),
  KEY `ix_age` (`age`),
  KEY `ix_sex` (`sex`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

 

 

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