MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。
數據庫查詢是數據庫的最主要功能之一,咱們都但願查詢數據的速度能儘量的快,所以數據庫系統的設計者會從查詢算法的角度進行優化,這篇文章對索引作一個系統的梳理,但願對你們有幫助。node
索引的分類能夠從多個角度進行,下面分別從數據結構,物理存儲和業務邏輯三個維度進行劃分。mysql
關於B+樹索引,後面會深刻解析算法
如今MyISAM和InnoDB引擎都支持了sql
用於對GIS數據類型建立SPATIAL索引數據庫
索引是經過二叉樹的數據結構來描述的,咱們能夠這麼理解聚簇索引:索引的葉節點就是數據節點。而非聚簇索引的葉節點仍然是索引節點,只不過有一個指針指向對應的數據塊。服務器
舉個例子說明下:數據結構
create table student ( `id` INT UNSIGNED AUTO_INCREMENT, `name` VARCHAR(255), PRIMARY KEY(`id`), KEY(`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
該表中主鍵id是該表的彙集索引、name爲非彙集索引;表中的每行數據都是按照彙集索引id排序存儲的;好比要查找name='Arla'和name='Arle'的兩個同窗,他們在name索引表中位置多是相鄰的,可是實際存儲位置可能差的很遠。name索引表節點按照name排序,檢索的是每一行數據的主鍵。彙集索引表按照主鍵id排序,檢索的是每一行數據的真實內容。數據結構和算法
主鍵索引是一種特殊的惟一索引,不容許有空值ide
複合索引指多個字段上建立的索引,只有在查詢條件中使用了建立索引時的第一個字段,索引纔會被使用。使用複合索引時遵循最左前綴集合性能
空間索引是對空間數據類型的字段創建的索引,MYSQL中的空間數據類型有4種,分別是GEOMETRY、POINT、LINESTRING、POLYGON。
MYSQL使用SPATIAL關鍵字進行擴展,使得可以用於建立正規索引類型的語法建立空間索引。建立空間索引的列,必須將其聲明爲NOT NULL,空間索引只能在存儲引擎爲MYISAM的表中建立.
CREATE TABLE table_name[col_name data type] [unique|fulltext|spatial][index|key][index_name](col_name[length])[asc|desc]
create table table_name( id int(11), name varchar(20), sex boolean, INDEX(id) );
查看錶結構
show create table table_name;
可使 EXPLAIN 語句查看索引是否被使用
explain select * from table_name where id = 1\G
create table index2( id int unique, name varchar(20), unique INDEX index_2(id asc) );
全文索引只能在char,varchar或者text 類型的字段上。並且,只有MyISAM 儲存引擎支持全文索引。
create table idnex3( id int, info varchar(20), FULLTEXT INDEX index3_info(info) )ENGINE=MyISAM;
create table index4( id int, subject varchar(255), index index4_st(subject(10)) );
這裏須要注意的,subject 的長度爲255,可是index4_st索引只有10。這樣作的目的仍是爲了提升查詢速度。對於字符型的數據,能夠不用查詢所有信息,只查詢其前面的若干字符信息。
create table index5( id int, name varchar(20), sex char(4), index index5_ns(name.sex) );
這是咱們能夠看到,name 和sex字段上已經建立了index_ns索引。
在example0() 表中的id 建立名爲index7_id 的索引。
create index index7_id on example0(id);
create UNIQUE index index_name on table_name(name);
create FULLTEXT index index_name on table_name(info);
create INDEX index_name ON table_name(name(10));
create INDEX index_name ON table_name(name,sex);
在name字段上建立名爲indx_name 的索引
alter table table_name ADD INDEX index_name(name(20));
alter table table_name ADD UNIQUE INDEX index_name(id);
alter table table_name ADD FULLTEXT INDEX index_name(info);
alter table table_name ADD INDEX index_name(name(4));
alter tabel table_name ADD INDEX index_name(name.sex);
DROP INDEX index_name ON table_name;
目前大部分數據庫系統及文件系統都採用B-Tree或其變種B+Tree做爲索引結構,那麼索引樹是如何維護的?
查找是數據結構和算法中一個很是重要的概念。
B-Tree是一種多路搜索樹(並非二叉的):
B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,若是命中則結束,不然進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已是葉子結點;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樹中每個內部節點會包含必定數量的鍵值。一般,鍵值的數量被選定在d和2d之間。在實際中,鍵值佔用了節點中大部分的空間。因數2將保證節點能夠被拆分或組合。若是一個內部節點有2d個鍵值,那麼添加一個鍵值給此節點的過程,將會拆分2d鍵值爲2個d鍵值的節點,並把此鍵值添加給父節點。每個拆分的節點須要最小數目的鍵值。類似地,若是一個內部節點和他的鄰居二者都有d個鍵值,那麼將經過它與鄰居的合併來刪除一個鍵值。刪除此鍵值將致使此節點擁有d-1個鍵值;與鄰居的合併則加上d個鍵值,再加上從鄰居節點的父節點移來的一個鍵值。結果爲徹底填充的2d個鍵值。
下面是往B樹中依次插入
6 10 4 14 5 11 15 3 2 12 1 7 8 8 6 3 6 21 5 15 15 6 32 23 45 65 7 8 6 5 4
B-Tree有許多變種,其中最多見的是B+Tree,MySQL就廣泛使用B+Tree實現其索引結構。
與B-Tree相比,B+Tree有如下不一樣點:
B+的搜索與B-樹也基本相同,區別是B+樹只有達到葉子結點才命中(B-樹能夠在非葉子結點命中),其性能也等價於在關鍵字全集作一次二分查找;
下面是往B+樹中依次插入
6 10 4 14 5 11 15 3 2 12 1 7 8 8 6 3 6 21 5 15 15 6 32 23 45 65 7 8 6 5 4
通常來講,索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲的磁盤上。
這樣的話,索引查找過程當中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級,因此評價一個數據結構做爲索引的優劣最重要的指標就是在查找過程當中磁盤I/O操做次數的漸進複雜度。換句話說,索引的結構組織要儘可能減小查找過程當中磁盤I/O的存取次數。
假如每一個盤塊能夠正好存放一個B樹的結點(正好存放2個文件名)。那麼一個BTNODE結點就表明一個盤塊,而子樹指針就是存放另一個盤塊的地址。
下面,我們來模擬下查找文件29的過程:
B+-tree的內部結點並無指向關鍵字具體信息的指針。所以其內部結點相對B 樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。
舉個例子,假設磁盤中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體信息指針2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點須要2個盤快。而B+
樹內部結點只須要1個盤快。當須要把內部結點讀入內存中的時候,B 樹就比B+樹多一次盤塊查找時間(在磁盤中就是盤片旋轉的時間)。
因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。
索引查詢是數據庫中重要的記錄查詢方法,要不要進入索引以及在那些字段上創建索引都要和實際數據庫系統的查詢要求結合來考慮,下面給出實際中的一些通用的原則:
一個單獨的索引掃描只能用於這樣的條件子句:使用被索引字段和索引操做符類中的操做符, 而且這些條件以AND鏈接。
假設在(a, b)上有一個索引, 那麼相似WHERE a = 5 AND b = 6的條件可使用索引,可是像WHERE a = 5 OR b = 6的條件就不能直接使用索引。
一個相似WHERE x =42 OR x = 47 OR x = 53 OR x = 99 這樣的查詢能夠分解成四個在x上的獨立掃描,每一個掃描使用一個條件, 最後將這些掃描的結果OR 在一塊兒,生成最終結果。
另一個例子是,若是咱們在x 和y上有獨立的索引,一個相似WHERE x = 5 AND y = 6 這樣的查詢能夠分解爲幾個使用獨立索引的子句,而後把這幾個結果AND 在一塊兒,生成最終結果。
聯合索引又叫複合索引。兩個或更多個列上的索引被稱做複合索引。
對於複合索引:Mysql從左到右的使用索引中的字段,一個查詢能夠只使用索引中的一部份,但只能是最左側部分。例如索引是key index (a,b,c)。 能夠支持a | a,b| a,b,c 3種組合進行查找,但不支持 b,c進行查找 .當最左側字段是常量引用時,索引就十分有效。
因此說建立複合索引時,應該仔細考慮列的順序。對索引中的全部列執行搜索或僅對前幾列執行搜索時,複合索引很是有用;僅對後面的任意列執行搜索時,複合索引則沒有用處。
這裏記錄兩種方式,分別是
show status like ‘Handler_read%';
你們能夠注意:
+-----------------------+--------------+ | Variable_name | Value | +-----------------------+--------------+ | Handler_read_first | 153 | | Handler_read_key | 364 | | Handler_read_next | 425 | | Handler_read_prev | 598 | | Handler_read_rnd | 605 | | Handler_read_rnd_next | 860571 | +-----------------------+--------------+ 6 rows in set (0.00 sec) ————————————————
分析這幾個值,咱們能夠查看當前索引的使用狀況:
查詢 schema_unused_indexes庫。
root@localhost [sys]>select * from schema_unused_indexes; +-------------------+-------------+------------+ | object_schema | object_name | index_name | +-------------------+-------------+------------+ | sysbench_testdata | sbtest1 | k_1 | | sysbench_testdata | sbtest10 | k_10 | | sysbench_testdata | sbtest3 | k_3 | | sysbench_testdata | sbtest4 | k_4 | | sysbench_testdata | sbtest5 | k_5 | | sysbench_testdata | sbtest6 | k_6 | | sysbench_testdata | sbtest7 | k_7 | | sysbench_testdata | sbtest8 | k_8 | | sysbench_testdata | sbtest9 | k_9 | +-------------------+-------------+------------+ 9 rows in set (0.00 sec)
explain顯示了mysql如何使用索引來處理select語句以及鏈接表。能夠幫助選擇更好的索引和寫出更優化的查詢語句。
新建一張表,
CREATE TABLE IF NOT EXISTS `article` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `author_id` int(10) unsigned NOT NULL, `category_id` int(10) unsigned NOT NULL, `views` int(10) unsigned NOT NULL, `comments` int(10) unsigned NOT NULL, `title` varbinary(255) NOT NULL, `content` text NOT NULL, PRIMARY KEY (`id`) );
執行查詢,
EXPLAIN SELECT author_id FROM `article` WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1
響應數據以下,
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: article type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 3 Extra: Using where; Using filesort 1 row in set (0.00 sec)
type 是 ALL,即最壞的狀況。Extra 裏還出現了 Using filesort,也是最壞的狀況。
MySQL 在表裏找到所需行的方式。包括(由左至右,由最差到最好):
| All | index | range | ref | eq_ref | const,system | null |