MySQL數據庫索引

1、數據庫索引介紹

索引是一種特殊的文件(MySql數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏全部記錄的引用指針,直接在索引中查找符合條件的選項,加快數據庫的查詢速度,而不是一行一行去遍歷數據後才選擇出符合條件的。若是沒有索引,執行查詢時MySQL必須從第一個記錄開始掃描整個表的全部記錄,直至找到符合要求的記錄。表裏面的記錄數量越多,這個操做的代價就越高。若是做爲搜索條件的列上已經建立了索引,MySQL無需掃描任何記錄便可迅速獲得目標記錄所在的位置。node

索引的本質是什麼?索引有什麼優勢,缺點是什麼?

索引是幫助MySQL高效獲取數據的數據結構。所以,索引的本質是一種數據結構。
在數據以外,數據庫系統還能夠維護知足特定查找算法的數據結構,這些數據結構以某種方式指向真實數據,這樣就能夠在這些數據結構上實現高級查找算法,這種數據結構就是索引。 mysql

優勢:算法

一、提升數據檢索效率,下降數據庫的IO成本;
二、經過索引對數據進行排序,下降了數據排序的成本,下降了CPU的利用率; sql

缺點:數據庫

一、索引實際上也是一張表,索引會佔用必定的存儲空間;
二、更新數據表的數據時,須要同時維護索引表,所以,會下降insert、update、delete的速度;數組

2、MySQL索引類型包括哪些?

(1)普通索引

這是最基本的索引,它沒有任何限制。它有如下幾種建立方式:緩存

◆ 建立索引數據結構

CREATE INDEX indexName ON mytable(username(length));

若是是CHAR,VARCHAR類型,length能夠小於字段實際長度;若是是BLOB和TEXT類型,必須指定 length,下同。架構

◆ 修改表結構函數

ALTER mytable ADD INDEX [indexName] ON (username(length))

◆ 建立表的時候直接指定

CREATE TABLE mytable(  
ID INT NOT NULL,   
username VARCHAR(16) NOT NULL,  
INDEX [indexName] (username(length))  
);

刪除索引的語法:

DROP INDEX [indexName] ON mytable;

(2)惟一索引

它與前面的普通索引相似,不一樣的就是:索引列的值必須惟一,但容許有空值。若是是組合索引,則列值的組合必須惟一。它有如下幾種建立方式:

◆建立索引

CREATE UNIQUE INDEX indexName ON mytable(username(length))

◆修改表結構

ALTER mytable ADD UNIQUE [indexName] ON (username(length))

◆建立表的時候直接指定

CREATE TABLE mytable(  
ID INT NOT NULL,   
username VARCHAR(16) NOT NULL,  
UNIQUE [indexName] (username(length))  
);

(3)主鍵索引

它是一種特殊的惟一索引,不容許有空值。通常是在建表的時候同時建立主鍵索引:

CREATE TABLE mytable(  
ID INT NOT NULL,   
username VARCHAR(16) NOT NULL,  
PRIMARY KEY(ID)  
);

固然也能夠用 ALTER 命令。記住:一個表只能有一個主鍵。

(4)組合索引

爲了形象地對比單列索引和組合索引,爲表添加多個字段:

CREATE TABLE mytable(  
ID INT NOT NULL,   
username VARCHAR(16) NOT NULL,  
city VARCHAR(50) NOT NULL,  
age INT NOT NULL 
);

爲了進一步榨取MySQL的效率,就要考慮創建組合索引。就是將 name, city, age建到一個索引裏:

ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age);

建表時,usernname長度爲 16,這裏用 10。這是由於通常狀況下名字的長度不會超過10,這樣會加速索引查詢速度,還會減小索引文件的大小,提升INSERT的更新速度。

若是分別在 usernname,city,age上創建單列索引,讓該表有3個單列索引,查詢時和上述的組合索引效率也會大不同,遠遠低於咱們的組合索引。雖然此時有了三個索引,但MySQL只能用到其中的那個它認爲彷佛是最有效率的單列索引。

創建這樣的組合索引,實際上是至關於分別創建了下面三組組合索引:

usernname,city,age  
usernname,city  
usernname

爲何沒有 city,age這樣的組合索引呢?這是由於MySQL組合索引「最左前綴」的結果。簡單的理解就是隻從最左面的開始組合。並非只要包含這三列的查詢都會用到該組合索引,下面的幾個SQL就會用到這個組合索引:

SELECT * FROM mytable WHREE username="admin" AND city="鄭州" 
SELECT * FROM mytable WHREE username="admin"

而下面幾個則不會用到:

SELECT * FROM mytable WHREE age=20 AND city="鄭州" 
SELECT * FROM mytable WHREE city="鄭州"

3、 InnoDB存儲索引

在數據庫中,若是索引太多,應用程序的性能可能會受到影響;若是索引太少,又會對查詢性能產生影響。因此,咱們要追求二者的一個平衡點,足夠多的索引帶來查詢性能提升,又不由於索引過多致使修改數據等操做時負載太高。

InnoDB支持3種常見索引:

 ● 哈希索引

 ● B+ 樹索引

 ● 全文索引

咱們接下來要詳細講解的就是B+ 樹索引和全文索引。

哈希索引

學習哈希索引以前,咱們先了解一些基礎的知識:哈希算法。哈希算法是一種經常使用的算法,時間複雜度爲O(1)。它不只應用在索引上,各個數據庫應用中也都會使用。

哈希表

哈希表(Hash Table)也稱散列表,由直接尋址表改進而來。

1.jpg

在該表中U表示關鍵字全集,K表示實際存在的關鍵字,右邊的數組(哈希表)表示在內存中能夠直接尋址的連續空間,哈希表中每一個插槽關聯的單向鏈表中存儲實際數據的真實地址。

若是右邊的數組直接使用直接尋址表,那麼對於每個關鍵字K都會存在一個h[K]且不重複,這樣存在一些問題,若是U數據量過大,那麼對於計算機的可用容量來講有點不實際。而若是集合K佔比U的比例太小,則分配的大部分空間都要浪費。

所以咱們使用哈希表,咱們經過一些函數h(k)來肯定映射關係,這樣讓離散的數據儘量均勻分佈的利用數組中的插槽,但會有一個問題,多個關鍵字映射到同一個插槽中,這種狀況稱爲碰撞(collision),數據庫中採用最簡單的解決方案:連接法(chaining)。也就是每一個插槽存儲一個單項鍊表,全部碰撞的元素會依次造成鏈表中的一個結點,若是不存在,則鏈表指向爲NULL。

而使用的函數h(k)成爲哈希函數,它必須可以很好的進行散列。最好可以避免碰撞或者達到最小碰撞。通常爲了更好的處理哈希的關鍵字,咱們會將其轉換爲天然數,而後經過除法散列、乘法散列或者全域散列來實現。數據庫通常使用除法散列,即當有m個插槽時,咱們對每一個關鍵字k進行對m的取模:h(k) = k % m。

InnoDB存儲引擎中的哈希算法

InnoDB存儲引擎使用哈希算法來查找字典,衝突機制採用鏈表,哈希函數採用除法散列。對於緩衝池的哈希表,在緩存池中的每頁都有一個chain指針,指向相同哈希值的頁。對於除法散列,m的值爲略大於2倍緩衝池頁數量的質數。如當前innodb_buffer_pool_size大小爲10M,則共有640個16KB的頁,須要分配1280個插槽,而略大於的質數爲1399,所以會分配1399個槽的哈希表,用來哈希查詢緩衝池中的頁。

而對於將每一個頁轉換爲天然數,每一個表空間都有一個space_id,用戶要查詢的是空間中某個連續的16KB的頁,即偏移量(offset),InnoDB將space_id左移20位,再加上space_id和offset,即K=space_id<<20+space_id+offset,而後使用除法散列到各個槽中。

自適應哈希索引

自適應哈希索引採用上面的哈希表實現,屬於數據庫內部機制,DBA不能干預。它只對字典類型的查找很是快速,而對範圍查找等卻無能爲力,如:

select * from t where f='100';

咱們能夠查看自適應哈希索引的使用狀況:

mysql> show engine innodb status\G;
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
2019-05-13 23:32:21 7f4875947700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 32 seconds
...
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 1226, seg size 1228, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 276671, node heap has 1288 buffer(s)
0.16 hash searches/s, 16.97 non-hash searches/s

咱們能夠看到自適應哈希的使用狀況,能夠經過最後一行的hash searches/non-hash searches來判斷使用哈希索引的效率。

咱們可使用innodb_adaptive_hash_index參數來禁用或啓用此特性,默認開啓。

B+ 樹索引

B+ 樹索引是目前關係型數據庫系統中查找最爲經常使用和有效的索引,其構造相似於二叉樹,根據鍵值對快速找到數據。B+ 樹(balance+ tree)由B樹(banlance tree 平衡二叉樹)和索引順序訪問方法(ISAM: Index Sequence Access Method)演化而來,這幾個都是經典的數據結構。而MyISAM引擎最初也是參考ISAM數據結構設計的。

基礎數據結構

想要了解B+ 樹數據結構,咱們先了解一些基礎的知識。

(1)二分查找法

又稱爲折半查找法,指的是將數據順序排列,經過每次和中間值比較,跳躍式查找,每次縮減一半的範圍,快速找到目標的算法。其算法複雜度爲log2(n),比順序查找要快上一些。

如圖所示,從有序列表中查找48,只須要3步:

2.jpg

詳細的算法能夠參考二分查找算法。

(2)二叉查找樹

二叉查找樹的定義是在一個二叉樹中,左子樹的值老是小於根鍵值,根鍵值老是小於右子樹的值。在咱們查找時,每次都從根開始查找,根據比較的結果來判斷繼續查找左子樹仍是右子樹。其查找的方法很是相似於二分查找法。

3.jpg

(3)平衡二叉樹

二叉查找樹的定義很是寬泛,能夠任意構造,可是在極端狀況下查詢的效率和順序查找同樣,如只有左子樹的二叉查找樹。

4.jpg

若想構造一個性能最大的二叉查找樹,就須要該樹是平衡的,即平衡二叉樹(因爲其發明者爲G. M. Adelson-Velsky 和 Evgenii Landis,又被稱爲AVL樹)。其定義爲必須知足任何節點的兩個子樹的高度最大差爲1的二叉查找樹。平衡二叉樹相對結構較優,而最好的性能須要創建一個最優二叉樹,但因爲維護該樹代價高,所以通常平衡二叉樹便可。

平衡二叉樹查詢速度很快,但在樹發生變動時,須要經過一次或屢次左旋和右旋來達到樹新的平衡。這裏不發散講。

B+ 樹

瞭解了基礎的數據結構後,咱們來看下B+ 樹的實現,其定義十分複雜,簡單來講就是在B樹上增長規定:

一、葉子結點存數據,非葉子結點存指針

二、全部葉子結點從左到右用雙向鏈表記錄

目標是爲磁盤或其餘直接存取輔助設備設計的一種平衡查找樹。在該樹中,全部的記錄都按鍵值的大小放在同一層的葉子節點上,各葉子節點之間有指針進行鏈接(非連續存儲),造成一個雙向鏈表。索引節點按照平衡樹的方式構造,並存在指針指向具體的葉子節點,進行快速查找。

下面的B+ 樹爲數據較少時,此時高度爲2,每頁固定存放4條記錄,扇出固定爲5(圖上灰色部分)。葉子節點存放多條數據,是爲了下降樹的高度,進行快速查找。

5.jpg

當咱們插入2八、70、95 3條數據後,B+ 樹因爲數據滿了,須要進行頁的拆分。此時高度變爲3,每頁依然是4條記錄,雙向鏈表未畫出可是依然是存在的,如今能夠看出來是一個平衡二叉樹的雛形了。

6.jpg

InnoDB的B+ 樹索引

InnoDB的B+ 樹索引的特色是高扇出性,所以通常樹的高度爲2~4層,這樣咱們在查找一條記錄時只用I/O 2~4次。當前機械硬盤每秒至少100次I/O/s,所以查詢時間只需0.02~0.04s。

數據庫中的B+ 樹索引分爲彙集索引(clustered index)和輔助索引(secondary index)。它們的區別是葉子節點存放的是否爲一整行的完整數據。

彙集索引

彙集索引就是按照每張表的主鍵(惟一)構造一棵B+ 樹,同時葉子節點存放整行的完整數據,所以將葉子節點稱爲數據頁。因爲定義了數據的邏輯順序,彙集索引也能快速的進行範圍類型的查詢。

彙集索引的葉子節點按照邏輯順序連續存儲,葉子節點內部物理上連續存儲,做爲最小單元,葉子節點間經過雙向指針鏈接,物理存儲上不連續,邏輯存儲上連續。

彙集索引可以針對主鍵進行快速的排序查找和範圍查找,因爲是雙向鏈表,所以在逆序查找時也很是快。

咱們能夠經過explain命令來分析MySQL數據庫的執行計劃:

# 查看錶的定義,能夠看到id爲主鍵,name爲普通列
mysql> show create table dimensionsConf;
| Table          | Create Table    
| dimensionsConf | CREATE TABLE `dimensionsConf` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `remark` varchar(1024) NOT NULL,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `fullindex_remark` (`remark`)
) ENGINE=InnoDB AUTO_INCREMENT=178 DEFAULT CHARSET=utf8 |
1 row in set (0.00 sec)
 
# 先測試一個非主鍵的name屬性排序並查找,能夠看到沒有使用到任何索引,且須要filesort(文件排序),這裏的rows爲輸出行數的預估值
mysql> explain select * from dimensionsConf order by name limit 10\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: dimensionsConf
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 57
        Extra: Using filesort
1 row in set (0.00 sec)
 
# 再測試主鍵id的排序並查找,此時使用主鍵索引,在執行計劃中沒有了filesort操做,這就是彙集索引帶來的優化
mysql> explain select * from dimensionsConf order by id limit 10\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: dimensionsConf
         type: index
possible_keys: NULL
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 10
        Extra: NULL
1 row in set (0.00 sec)
 
# 再查找根據主鍵id的範圍查找,此時直接根據葉子節點的上層節點就能夠快速獲得範圍,而後讀取數據
mysql> explain select * from dimensionsConf where id>10 and id<10000\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: dimensionsConf
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: NULL
         rows: 56
        Extra: Using where
1 row in set (0.00 sec)

輔助索引

輔助索引又稱非彙集索引,其葉子節點不包含行記錄的所有數據,而是包含一個書籤(bookmark),該書籤指向對應行數據的彙集索引,告訴InnoDB存儲引擎去哪裏查找具體的行數據。輔助索引與彙集索引的關係就是結構類似、獨立存在,但輔助索引查找非索引數據須要依賴於彙集索引來查找。

7.jpg

全文索引

咱們經過B+ 樹索引能夠進行前綴查找,如:

select * from blog where content like 'xxx%';

只要爲content列添加了B+ 樹索引(彙集索引或輔助索引),就可快速查詢。但在更多狀況下,咱們在博客或搜索引擎中須要查詢的是某個單詞,而不是某個單詞開頭,如:

select * from blog where content like '%xxx%';

此時若是使用B+ 樹索引依然是全表掃描,而全文檢索(Full-Text Search)就是將整本書或文章內任意內容檢索出來的技術。

倒排索引

全文索引一般使用倒排索引(inverted index)來實現,倒排索引和B+ 樹索引都是一種索引結構,它須要將分詞(word)存儲在一個輔助表(Auxiliary Table)中,爲了提升全文檢索的並行性能,共有6張輔助表。輔助表中存儲了單詞和單詞在各行記錄中位置的映射關係。它分爲兩種:

inverted file index(倒排文件索引),表現爲{單詞,單詞所在文檔ID}
full inverted index(詳細倒排索引),表現爲{單詞,(單詞所在文檔ID, 文檔中的位置)}
對於這樣的一個數據表:

8.jpg

倒排文件索引類型的輔助表存儲爲:

9.jpg

詳細倒排索引類型的輔助表存儲爲,佔用更多空間,也更好的定位數據,比提供更多的搜索特性:

10.jpg

全文檢索索引緩存

輔助表是存在與磁盤上的持久化的表,因爲磁盤I/O比較慢,所以提供FTS Index Cache(全文檢索索引緩存)來提升性能。FTS Index Cache是一個紅黑樹結構,根據(word, list)排序,在有數據插入時,索引先更新到緩存中,然後InnoDB存儲引擎會批量進行更新到輔助表中。

當數據庫宕機時,還沒有落盤的索引緩存數據會自動讀取並存儲,配置參數innodb_ft_cache_size控制緩存的大小,默認爲32M,提升該值,能夠提升全文檢索的性能,但在故障時,須要更久的時間恢復。

在刪除數據時,InnoDB不會刪除索引數據,而是保存在DELETED輔助表中,所以一段時間後,索引會變得很是大,能夠經過optimize table命令手動刪除無效索引記錄。若是須要刪除的內容很是多,會影響應用程序的可用性,參數innodb_ft_num_word_optimize控制每次刪除的分詞數量,默認爲2000,用戶能夠調整該參數來控制刪除幅度。

全文檢索限制

全文檢索存在一個黑名單列表(stopword list),該列表中的詞不須要進行索引分詞,默認共有36個,如the單詞。你能夠自行調整:

mysql> select * from information_schema.INNODB_FT_DEFAULT_STOPWORD;
+-------+
| value |
+-------+
| a     |
| about |
| an    |
| are   |
| as    |
| at    |
| be    |
| by    |
| com   |
| de    |
| en    |
| for   |
| from  |
| how   |
| i     |
| in    |
| is    |
| it    |
| la    |
| of    |
| on    |
| or    |
| that  |
| the   |
| this  |
| to    |
| was   |
| what  |
| when  |
| where |
| who   |
| will  |
| with  |
| und   |
| the   |
| www   |
+-------+
36 rows in set (0.00 sec)

其餘限制還有:

 ● 每張表只能有一個全文檢索索引

 ● 多列組合的全文檢索索引必須使用相同的字符集和字符序,不瞭解的能夠參考MySQL亂碼的緣由和設置UTF8數據格式

 ● 不支持沒有單詞界定符(delimiter)的語言,如中文、日語、韓語等

全文檢索

咱們建立一個全文索引:

mysql> create fulltext index fullindex_remark on dimensionsConf(remark);
Query OK, 0 rows affected, 1 warning (0.39 sec)
Records: 0  Duplicates: 0  Warnings: 1
 
mysql> show warnings;
+---------+------+--------------------------------------------------+
| Level   | Code | Message                                          |
+---------+------+--------------------------------------------------+
| Warning |  124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+
1 row in set (0.00 sec)

全文檢索有兩種方法:

 ● 天然語言(Natural Language),默認方法,可省略:(IN NATURAL LANGUAE MODE)

 ● 布爾模式(Boolean Mode):(IN BOOLEAN MODE)

天然語言還支持一種擴展模式,後面加上:(WITH QUERY EXPANSION)。

其語法爲MATCH()...AGAINST(),MATCH指定被查詢的列,AGAINST指定何種方法查詢。

天然語言檢索

mysql> select remark from dimensionsConf where remark like '%baby%';
+-------------------+
| remark            |
+-------------------+
| a baby like panda |
| a baby like panda |
+-------------------+
2 rows in set (0.00 sec)
 
mysql> select remark from dimensionsConf where match(remark) against('baby' IN NATURAL LANGUAGE MODE);
+-------------------+
| remark            |
+-------------------+
| a baby like panda |
| a baby like panda |
+-------------------+
2 rows in set (0.00 sec)
 
# 查看下執行計劃,使用了全文索引排序
mysql> explain select * from dimensionsConf where match(remark) against('baby');
+----+-------------+----------------+----------+------------------+------------------+---------+------+------+-------------+
| id | select_type | table          | type     | possible_keys    | key              | key_len | ref  | rows | Extra       |
+----+-------------+----------------+----------+------------------+------------------+---------+------+------+-------------+
|  1 | SIMPLE      | dimensionsConf | fulltext | fullindex_remark | fullindex_remark | 0       | NULL |    1 | Using where |
+----+-------------+----------------+----------+------------------+------------------+---------+------+------+-------------+
1 row in set (0.00 sec)

咱們也能夠查看各行數據的相關性,是一個非負的浮點數,0表明沒有相關性:

mysql> select id,remark,match(remark) against('baby') as relevance from dimensionsConf;
+-----+-----------------------+--------------------+
| id  | remark                | relevance          |
+-----+-----------------------+--------------------+
| 106 | c                     |                  0 |
| 111 | 運營商             |                  0 |
| 115 | a baby like panda     | 2.1165735721588135 |
| 116 | a baby like panda     | 2.1165735721588135 |
+-----+-----------------------+--------------------+
4 rows in set (0.01 sec)

布爾模式檢索

MySQL也容許用修飾符來進行全文檢索,其中特殊字符會有特殊含義:

● +: 該word必須存在
● -: 該word必須排除
● (no operator): 該word可選,若是出現,相關性更高
● @distance: 查詢的多個單詞必須在指定範圍以內
● >: 出現該單詞時增長相關性
● <: 出現該單詞時下降相關性
● ~: 出現該單詞時相關性爲負
● *: 以該單詞開頭的單詞
● ": 表示短語

# 表明必須有a baby短語,不能有man,能夠有lik開頭的單詞,能夠有panda,
select remark from dimensionsConf where match(remark) against('+"a baby" -man lik* panda' IN BOOLEAN MODE);

擴展查詢

當查詢的關鍵字過短或不夠清晰時,須要用隱含知識來進行檢索,如database關聯的MySQL/DB2等。但這個我並沒太明白怎麼使用,後續補充吧。

相似的使用是:

select * from articles where match(title,body) against('database' with query expansion);

若是任何問題或者建議,歡迎留言交流。

更多學習內容請訪問從碼農成爲架構師的修煉之路

相關文章
相關標籤/搜索