mySql索引詳解

mySql中索引類型

普通索引(key),唯一索引(unique key),主鍵索引(primary key),全文索引(fulltext key

三種索引的索引方式是一樣的,只不過對索引的關鍵字有不同的限制:

  • 普通索引:對關鍵字沒有限制
  • 唯一索引:要求記錄提供的關鍵字不能重複
  • 主鍵索引:要求關鍵字唯一且不爲null

主鍵索引和普通索引的查詢有什麼區別?
如果語句是select * from T where ID=500,即主鍵查詢方式,則只需要搜索ID這棵B+樹;
如果語句是select * from T where k=5,即普通索引查詢方式,則需要先搜索k索引樹,得到ID的值爲500,再到ID索引樹搜索一次。這個過程稱爲回表。
也就是說,基於非主鍵索引的查詢需要多掃描一棵索引樹。因此,我們在應用中應該儘量使用主鍵查詢。
回到主鍵索引樹搜索的過程,我們稱爲回表

主鍵長度越小,普通索引的葉子節點就越小,普通索引佔用的空間也就越小

Mysql索引管理語法

查看索引:

show create table `table_name`   ---查看錶創建結構 

在這裏插入圖片描述
desc 表名
在這裏插入圖片描述
表創建後添加索引

create TABLE user_index(
	id int auto_increment primary key,
	first_name varchar(16),
	last_name VARCHAR(16),
	id_card VARCHAR(18),
	information text
);

-- 更改表結構
alter table user_index
-- 創建一個first_name和last_name的複合索引,並命名爲name
add key name (first_name,last_name),
-- 創建一個id_card的唯一索引,默認以字段名作爲索引名
add UNIQUE KEY (id_card),
-- 雞肋,全文索引不支持中文
add FULLTEXT KEY (information);

1.添加PRIMARY KEY(主鍵索引)

ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) 

2.添加UNIQUE(唯一索引)

ALTER TABLE `table_name` ADD UNIQUE ( `column` ) 

3.添加INDEX(普通索引)

ALTER TABLE `table_name` ADD INDEX index_name ( `column` )

4.添加FULLTEXT(全文索引)

ALTER TABLE `table_name` ADD FULLTEXT ( `column`) 

5.添加多列索引

ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )

創建表時指定索引

CREATE TABLE user_index2 (
	id INT auto_increment PRIMARY KEY, 
	first_name VARCHAR (16),
	last_name VARCHAR (16),
	id_card VARCHAR (18),
	information text,
	KEY name (first_name, last_name),
	FULLTEXT KEY (information),
	UNIQUE KEY (id_card)
);

刪除索引
根據索引名刪除普通索引、唯一索引、全文索引:alter table 表名 drop KEY 索引名

alter table user_index drop KEY name; 
alter table user_index drop KEY id_card;
alter table user_index drop KEY information;

刪除主鍵索引:

alter table user_index
-- 重新定義字段(ps: 如果主鍵索引是自增,需要取消自增長再行刪除)
MODIFY id int,
drop PRIMARY KEY

重建索引:

重建索引能將索引中的空洞去除、讓索引更緊縮、佔用空間變小。當索引文件過大的時候可以試試重建索引

alter table T add index(k); -- 重建普通索引:
alter table T add primary key(id); -- 重建主鍵索引: 

mysql索引類型(按存儲結構劃分)

說明:索引是存儲引擎層次的東西、不同的存儲引擎支持的索引類型也不同。

BTree索引

1、MySQL的BTree索引使用的是B樹中的B+Tree,是大多數 MySQL 存儲引擎的默認索引類型
ps:但對於主要的兩種存儲引擎(MyISAM和InnoDB)的實現方式是不同的。

2、因爲不再需要進行全表掃描,只需要對樹進行搜索即可,所以查找速度快很多。

3、除了用於查找,還可以用於排序分組

4、可以指定多個列作爲索引列,多個索引列共同組成鍵。

5、適用於全鍵值、鍵值範圍和鍵前綴查找,其中鍵前綴查找只適用於最左前綴查找。如果不是按照索引列的順序進行查找,則無法使用索引

BTree和紅黑樹比較

紅黑樹等平衡樹也可以用來實現索引,但是文件系統及數據庫系統普遍採用 B+ Tree 作爲索引結構,主要有以下兩個原因

(一)更少的查找次數

平衡樹查找操作的時間複雜度和樹高 h 相關,O(h)=O(logdN),其中 d 爲每個節點的出度。
紅黑樹的出度爲 2,而 B+ Tree 的出度一般都非常大,所以紅黑樹的樹高 h 很明顯比 B+ Tree 大非常多,查找的次數也就更多。

(二)利用磁盤預讀特性

爲了減少磁盤 I/O 操作,磁盤往往不是嚴格按需讀取,而是每次都會預讀。預讀過程中,磁盤進行順序讀取,順序讀取不需要進行磁盤尋道,並且只需要很短的旋轉時間,速度會非常快。

操作系統一般將內存和磁盤分割成固定大小的塊,每一塊稱爲一頁,內存與磁盤以頁爲單位交換數據。數據庫系統將索引的一個節點的大小設置爲頁的大小,使得一次 I/O 就能完全載入一個節點。並且可以利用預讀特性,相鄰的節點也能夠被預先載入。

BTree索引在(MyISAM和InnoDB)的不同實現方式

MyISAM實現
B+Tree葉節點的data域存放的是數據記錄的地址。在索引檢索的時候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其 data(葉子節點) 域的值,然後以 data 域的值爲地址讀取相應的數據記錄。這被稱爲非聚簇索引

InnoDB實現

其數據文件本身就是索引文件。相比MyISAM,索引文件和數據文件是分離的,其表數據文件本身就是按B+Tree組織的一個索引結構,樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引。這被稱聚簇索引(或聚集索引)。而其餘的索引都作爲輔助索引,輔助索引的data域存儲相應記錄主鍵的值而不是地址,這也是和MyISAM不同的地方。在根據主索引搜索時,直接找到key所在的節點即可取出數據;在根據輔助索引查找時,則需要先取出主鍵的值,再走一遍主索引。 因此,在設計表的時候,不建議使用過長的字段作爲主鍵,也不建議使用非單調的字段作爲主鍵,這樣會造成主索引頻繁分裂。

哈希索引

哈希索引能以 O(1) 時間進行查找,但是失去了有序性:

無法用於排序與分組;
只支持精確查找,無法用於部分查找和範圍查找。
InnoDB 存儲引擎有一個特殊的功能叫“自適應哈希索引”,當某個索引值被使用的非常頻繁時,會在 B+Tree 索引之上再創建一個哈希索引,這樣就讓 B+Tree 索引具有哈希索引的一些優點,比如快速的哈希查找。

在絕大多數需求爲單條記錄查詢的時候,可以選擇哈希索引,查詢性能最快。其餘大部分場景,建議選擇BTree索引。

全文索引

MyISAM 存儲引擎支持全文索引,用於查找文本中的關鍵詞,而不是直接比較是否相等。
查找條件使用 MATCH AGAINST,而不是普通的 WHERE。
全文索引使用倒排索引實現,它記錄着關鍵詞到其所在文檔的映射。

ps:InnoDB 存儲引擎在 MySQL 5.6.4 版本中也開始支持全文索引。

空間數據索引

MyISAM 存儲引擎支持空間數據索引(R-Tree),可以用於地理數據存儲。空間數據索引會從所有維度來索引數據,可以有效地使用任意維度來進行組合查詢。

必須使用 GIS 相關的函數來維護數據。

分析索引使用情況 explain

可以通過explain selelct來分析SQL語句執行前的執行計劃:
在這裏插入圖片描述

什麼樣的列段適合建立索引

where 條件後面使用:給where條件中的字段建立索引能提高新能
order by:給參與order by 的字段建立索引提高性能,因爲 建立索引後此字段就有序了。能 極大提高新能。如果不建立索引那麼全部數據都要使用外部排序效率非常低
join on:對join語句匹配關係(on)涉及的字段建立索引能夠提高效率
索引覆蓋:當select 查詢的字段 包含在索引中那麼就不需要回表。比如給 name,age 這個兩個列段建立索引 然後 select name,age from user where name = ‘zs’.就能利用到覆蓋索引。
or條件:一但有一邊無索引可用就會導致整個SQL語句的全表掃描

注意:索引不是越多越好,索引是能提高查詢新能,但是會降低增刪改的新能。並且會佔用跟多的磁盤空間。

語法細節(要點)

在滿足索引使用的場景下(where/order by/join on或索引覆蓋),索引也不一定被使用

字段要獨立出現

比如下面兩條SQL語句在語義上相同,但是第一條會使用主鍵索引而第二條、第三條不會使用到索引。

select * from user where id = 20-1;
select * from user where id+1 = 20;
select * from user where xxx(id) = 20;   --- xxx是一個函數 

多列索引

在需要使用多個列作爲條件進行查詢時,使用多列索引比使用多個單列索引性能更好。例如下面的語句中,最好把 actor_id 和 film_id 設置爲多列索引。

SELECT film_id, actor_ id FROM sakila.film_actor WHERE
 actor_id = 1 AND film_id = 1;

like查詢,不能以通配符開頭

title字段建立索引,但是當我們like查不注意的時候會導致索引失效

select * from article where title like '%mysql%'; --無法使用索引全表掃描
select * from article where title like 'mysql%'; -- 能使用到索引

索引列的順序

讓選擇性最強的索引列放在前面。
索引的選擇性是指:不重複的索引值和記錄總數的比值。最大值爲 1,此時每個記錄都有唯一的索引與其對應。選擇性越高,查詢效率也越高。

例如下面顯示的結果中 customer_id 的選擇性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。

SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
COUNT(*)
FROM payment;
 staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
               COUNT(*): 16049

前綴索引

語法:index(field(10)),使用字段值的前10個字符建立索引,默認是使用字段的全部內容建立索引。前綴索引能節省空間,但會增加查詢掃描次數,並且不能使用覆蓋索引;

區分度問題:前綴的區分度要高。比如密碼就適合建立前綴索引,因爲密碼幾乎各不相同。
我們可以利用select count(*)/count(distinct left(password,prefixLen));,通過從調整prefixLen的值(從1自增)查看不同前綴長度的一個平均匹配度,接近1時就可以了(表示一個密碼的前prefixLen個字符幾乎能確定唯一一條記錄)

**倒序存儲,再創建前綴索引:**用於繞過字符串本身前綴的區分度不夠的問題,比如爲我們要給身份證字段建立索引,但是身份證前面幾位都差不多。我們主要是根據後面的幾位來做區分。這時候就可以使用 倒序存儲,再創建前綴索引

覆蓋索引

由於覆蓋索引可以減少樹的搜索次數,顯著提升查詢性能,所以使用覆蓋索引是一個常用的性能優化手段。

索引的複用能力。因爲可以支持最左前綴,所以當已經有了(a,b)這個聯合索引後,一般就不需要單獨在a上建立索引了。因此,第一原則是,如果通過調整順序,可以少維護一個索引,那麼這個順序往往就是需要優先考慮採用的。

最左前綴原則

MySQL中的索引可以以一定順序引用多列,這種索引叫作聯合索引。如User表的name和city加聯合索引就是(name,city)o而最左前綴原則指的是,如果查詢的時候查詢條件精確匹配索引的左邊連續一列或幾列,則此列就可以被用到。如下:

select * from user where name=xx and city=xx ; //可以命中索引
select * from user where name=xx ; // 可以命中索引
select * from user where city=xx; // 無法命中索引            

這裏需要注意的是,查詢的時候如果兩個條件都用上了,但是順序不同,如 city= xx and name =xx,那麼現在的查詢引擎會自動優化爲匹配聯合索引的順序,這樣是能夠命中索引的.

列段重複少的放前面,多的放後面。ORDERBY子句也遵循此規

注意避免冗餘索引

冗餘索引指的是索引的功能相同,能夠命中 就肯定能命中 ,那麼 就是冗餘索引如(name,city )和(name )這兩個索引就是冗餘索引,能夠命中後者的查詢肯定是能夠命中前者的 在大多數情況下,都應該儘量擴展已有的索引而不是創建新索引。

MySQLS.7 版本後,可以通過查詢 sys 庫的 schemal_r dundant_indexes 表來查看冗餘索引

索引下推 (5.6版本開始後)

以在索引遍歷過程中,對索引中包含的字段先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數。

導致索引失效的場景

1.建什麼索引用什麼索引,順序也最好保持一致

2.最佳左前綴索引名稱命名(如字段name,age,city,則索引命名應該是nameAgeCity或者xxx_nameAgeCity,順序很重要)

3.不在索引列上做任何操作(計算,函數,or,類型轉換),會導致索引失效而轉向全表掃描

4.存儲引擎不能使用索引中範圍條件右邊的列(如name=‘lin’ and age>25 and city=‘qingdao’,則age後面的索引會實效)

5.儘量使用覆蓋索引(只訪問索引的查詢(索引列和要查詢的列一致)),減少select *

6.MySQL在使用不等於(!=或者<>)的時候無法使用索引會導致全表掃描
  
7. is null,is not null 也無法使用索引

8.like以通配符在這(’%abc’,’%abc%’)兩種情況會索引實效變成全表掃描,‘abc%‘則不會,若要’%abc’,’%abc%'不失效,建議使用覆蓋索引,且查詢的字段要少於索引或者與索引一致,不使用select *。如爲name,age,city建了索引,請這麼使用:select name或者select age,或者select city或者select name,age,city。如果select name,age,city,email則會全表掃描
  
9.字符串不加引號索引失效,

10.少用or,用他來連接時索引會失效

11.select * from A where exists (select 1 from where b.id=A.id)#當A表的數據系小於B表時,用exists優於in

12.使用join代替子查詢

索引的優點

1、大大減少了服務器需要掃描的數據行數。
2、幫助服務器避免進行排序和分組,以及避免創建臨時表(B+Tree 索引是有序的,可以用於 ORDER BY 和 GROUP BY 操作。臨時表主要是在排序和分組過程中創建,因爲不需要排序和分組,也就不需要創建臨時表)。
3、將隨機 I/O 變爲順序 I/O(B+Tree 索引是有序的,會將相鄰的數據都存儲在一起)。

索引的使用條件

1、對於非常小的表、大部分情況下簡單的全表掃描比建立索引更高效;
2、對於中到大型的表,索引就非常有效;
3、但是對於特大型的表,建立和維護索引的代價將會隨之增長。這種情況下,需要用到一種技術可以直接區分出需要查詢的一組數據,而不是一條記錄一條記錄地匹配,例如可以使用分區技術。

發佈了167 篇原創文章 · 獲贊 59 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章