Mysql支持哪幾種索引
索引是在MySql的存儲引擎層中實現的,而不是在服務器層
從數據結構角度
1、B+樹索引(O(log(n))):關於B+樹索引,可以參考 MySQL索引背後的數據結構及算法原理
BTREE在MyISAM裏的形式和Innodb稍有不同
在 Innodb裏,有兩種形態:一是primary key形態,其leaf node裏存放的是數據,而且不僅存放了索引鍵的數據,還存放了其他字段的數據。二是secondary index,其leaf node和普通的BTREE差不多,只是還存放了指向主鍵的信息.
而在MyISAM裏,主鍵和其他的並沒有太大區別。不過和Innodb不太一樣的地方是在MyISAM裏,leaf node裏存放的不是主鍵的信息,而是指向數據文件裏的對應數據行的信息.
MyISAM的B+Tree的葉子節點上的data,並不是數據本身,而是數據存放的地址。主索引和輔助索引沒啥區別,只是主索引中的key一定得是唯一的。這裏的索引都是非聚簇索引。
MyISAM還採用壓縮機制存儲索引,比如,第一個索引爲“her”,第二個索引爲“here”,那麼第二個索引會被存儲爲“3,e”,這樣的缺點是同一個節點中的索引只能採用順序查找。
InnoDB的數據文件本身就是索引文件,B+Tree的葉子節點上的data就是數據本身,key爲主鍵,這是聚簇索引。非聚簇索引,葉子節點上的data是主鍵(所以聚簇索引的key,不能過長)。爲什麼存放的主鍵,而不是記錄所在地址呢,理由相當簡單,因爲記錄所在地址並不能保證一定不會變,但主鍵可以保證。
聚簇索引的優缺點:
- 可以把相關數據保存在一起。例如實現電子郵箱時,可以根據用戶ID來聚集數據,這樣只需要從磁盤讀取少數的數據頁就能獲取某個用戶的全部郵件。如果沒有使用聚簇索引,則每封郵件都可能導致一次磁盤I/O.
- 數據訪問更快。聚簇索引將索引和數據保存在同一個B-Tree中,因此從聚簇索引中獲取數據通常比非聚簇索引中查找要快。
- 使用覆蓋索引掃描的查詢可以直接使用頁節點的主鍵值。
- 插入速度嚴重依賴於插入順序。按照主鍵的順序插入是加載數據到InnoDB表中速度最快的方式。(這種情況可以用主鍵auto_increment自增列解決)
- 更新聚簇索引列的代價很高,因爲會強制InnoDB將每個被更新的行移動到新的位置。
- 二級索引(非聚簇索引)可能比想象的要更大,因爲在二級索引的葉子節點包含了引用行的主鍵列。
- 二級索引的訪問需要兩次索引查找,而不是一次。
在InnoDB表中按主鍵順序插入行
- 寫入的目標頁可能已經刷到磁盤上並從緩存中移除,或者是還沒有被加載到緩存中,InnoDB在不得不在插入新行之前先找到並從磁盤讀取到內存中。這將導致大量的隨機I/O。
- 因爲寫入是亂序的,InnoDB不得不頻繁地做頁分裂操作,以便爲新的行分配空間。頁分裂會導致移動大量數據,一次插入最少需要修改三個頁而不是一個頁。
- 由於頻繁的頁分裂,頁會變得稀疏並被不規則地填充,所以最終數據會有碎片。
順序的主鍵什麼時候會造成更壞的結果?
總結
a、關於innoDB中索引的使用
瞭解不同存儲引擎的索引實現方式對於正確使用和優化索引都非常有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲什麼不建議使用過長的字段作爲主鍵,因爲所有輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段作爲主鍵在InnoDB中不是個好主意,因爲InnoDB數據文件本身是一顆B+Tree,非單調的主鍵會造成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段作爲主鍵則是一個很好的選擇。
b、什麼時候選用myisam
myisam的主鍵索引的葉子節點只存放數據在物理磁盤上的指針,其他次索引也是一樣的;
innodb的主鍵索引的葉子節點下面直接存放數據,其他次索引的葉子節點指向主鍵id;
由此可以挖掘出一個問題,就是如果Innodb有大數據列,比如 varchar(300),這種比較多的話,那麼排序的時候用主鍵id排序會比較慢,因爲id主鍵下面放着所有數據列,而Myisam就不需要掃描數據列,要解決這個問題的話可以再建一個和主鍵id一起的聯合索引;
MyISAM表索引在處理文本索引時更具優勢,而INNODB表索引在其它類型上更具效率優勢。比如全文索引一般在CHAR、VARCHAR或TEXT列上創建,MyISAM表支持而INNODB表不支持,常見主要針對文本進行索引。同時MySQL高併發需要事務場景時,只能使用INNODB表。
c、該如何選用兩個存儲引擎呢
此處參考鏈接:MySQL中MyISAM與InnoDB區別及選擇
因爲MyISAM相對簡單所以在效率上要優於InnoDB.如果系統讀多,寫少。對原子性要求低。那麼MyISAM最好的選擇。且MyISAM恢復速度快。可直接用備份覆蓋恢復。
如果系統讀少,寫多的時候,尤其是併發寫入高的時候。InnoDB就是首選了。
兩種類型都有自己優缺點,選擇那個完全要看自己的實際類弄。
2、hash索引:
a 僅僅能滿足"=","IN"和"<=>"查詢,不能使用範圍查詢
b 其檢索效率非常高,索引的檢索可以一次定位,不像B-Tree 索引需要從根節點到枝節點,最後才能訪問到頁節點這樣多次的IO訪問,所以 Hash 索引的查詢效率要遠高於 B-Tree 索引
c 只有Memory存儲引擎顯示支持hash索引
(1)Hash 索引僅僅能滿足"=","IN"和"<=>"查詢,不能使用範圍查詢。
由於 Hash 索引比較的是進行 Hash 運算之後的 Hash 值,所以它只能用於等值的過濾,不能用於基於範圍的過濾,因爲經過相應的 Hash 算法處理之後的 Hash 值的大小關係,並不能保證和Hash運算前完全一樣。
(2)Hash 索引無法被用來避免數據的排序操作。
由於 Hash 索引中存放的是經過 Hash 計算之後的 Hash 值,而且Hash值的大小關係並不一定和 Hash 運算前的鍵值完全一樣,所以數據庫無法利用索引的數據來避免任何排序運算;
(3)Hash 索引不能利用部分索引鍵查詢。
對於組合索引,Hash 索引在計算 Hash 值的時候是組合索引鍵合併後再一起計算 Hash 值,而不是單獨計算 Hash 值,所以通過組合索引的前面一個或幾個索引鍵進行查詢的時候,Hash 索引也無法被利用。
(4)Hash 索引在任何時候都不能避免表掃描。
前面已經知道,Hash 索引是將索引鍵通過 Hash 運算之後,將 Hash運算結果的 Hash 值和所對應的行指針信息存放於一個 Hash 表中,由於不同索引鍵存在相同 Hash 值,所以即使取滿足某個 Hash 鍵值的數據的記錄條數,也無法從 Hash 索引中直接完成查詢,還是要通過訪問表中的實際數據進行相應的比較,並得到相應的結果。
(5)Hash 索引遇到大量Hash值相等的情況後性能並不一定就會比B-Tree索引高。
對於選擇性比較低的索引鍵,如果創建 Hash 索引,那麼將會存在大量記錄指針信息存於同一個 Hash 值相關聯。這樣要定位某一條記錄時就會非常麻煩,會浪費多次表數據的訪問,而造成整體性能低下。
hash值即爲通過特定算法由指定列數據計算出來,磁盤地址即爲所在數據行存儲在硬盤上的地址(也有可能是其他存儲地址,其實MEMORY會將hash表導入內存)。
這樣,當我們進行WHERE age = 18 時,會將18通過相同的算法計算出一個hash值==>在hash表中找到對應的儲存地址==>根據存儲地址取得數據。
所以,每次查詢時都要遍歷hash表,直到找到對應的hash值,如(4),數據量大了之後,hash表也會變得龐大起來,性能下降,遍歷耗時增加,如(5)。
InnoDB存儲引擎有一個特別的功能,叫自適應哈希索引。當InnoDB注意到一些索引被很頻繁的訪問的時候,會在B-Tree索引的頂端爲這些值建立起內存中的索引。這個過程是自動的,既不能控制,也不能配置它。
3、FULLTEXT索引(現在MyISAM和InnoDB引擎都支持了)
主要用來查找文本中的關鍵字,而不是直接與索引中的值相比較。fulltext索引跟其它索引大不相同,它更像是一個搜索引擎,而不是簡單的where語句的參數匹配。fulltext索引配合match against操作使用,而不是一般的where語句加like。
- --創建article表
- CREATE TABLE article (
- id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
- title VARCHAR(200),
- content TEXT,
- FULLTEXT (title, content) --在title和content列上創建全文索引
- );
例如,我們想要在article
表的title
和content
列中全文檢索指定的查詢字符串,可以如下編寫SQL語句:
SELECT * FROM article WHERE MATCH(title, content) AGAINST('查詢字符串')
它可以在create table,alter table ,create index使用,不過目前只有char、varchar,text 列上可以創建全文索引。值得一提的是,在數據量較大時候,先將數據放入一個沒有全局索引的表中,然後再用CREATE index創建fulltext索引,要比先爲一張表建立fulltext然後再將數據寫入的速度快很多。如果可能,請儘量先創建表並插入所有數據後再創建全文索引,而不要在創建表時就直接創建全文索引,因爲前者比後者的全文索引效率要高。全文索引並不是和MyISAM一起誕生的,它的出現是爲了解決WHERE name LIKE “%word%"這類針對文本的模糊查詢效率較低的問題。在沒有全文索引之前,這樣一個查詢語句是要進行遍歷數據表操作的,可見,在數據量較大時是極其的耗時的,如果沒有異步IO處理,進程將被挾持,很浪費時間。
(1)創建表的適合添加全文索引
CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER NOT NULL , `content` text CHARACTER NULL , `time` int(10) NULL DEFAULT NULL , PRIMARY KEY (`id`), FULLTEXT (content) );
(2)修改表結構添加全文索引
ALTER TABLE article ADD FULLTEXT index_content(content)
(3)直接創建索引
CREATE FULLTEXT INDEX index_content ON article(content)
4、R-Tree索引(空間索引)(用於對GIS數據類型創建SPATIAL索引)
索引 | MyISAM引擎 | InnoDB引擎 | Memory引擎 |
B-Tree索引 | 支持 | 支持 | 支持 |
HASH索引 | 不支持 | 不支持 | 支持 |
R-Tree索引 | 支持 | 不支持 | 不支持 |
Full-text索引 | 支持 | 暫不支持(現在支持) | 不支持 |
從物理存儲角度
1、聚集索引(clustered index)
2、非聚集索引(non-clustered index)
聚集索引:
一種索引,該索引中鍵值的邏輯順序決定了表中相應行的物理順序。即:只要索引是相鄰的,那麼對應的數據一定也是相鄰地存放在磁盤上的。
聚集索引確定表中數據的物理順序。聚集索引類似於電話簿,後者按姓氏排列數據。由於聚集索引規定數據在表中的物理存儲順序,因此一個表只能包含一個聚集索引。但該索引可以包含多個列(組合索引),就像電話簿按姓氏和名字進行組織一樣。
聚集索引對於那些經常要搜索範圍值的列特別有效。使用聚集索引找到包含第一個值的行後,便可以確保包含後續索引值的行在物理相鄰。例如,如果應用程序執行的一個查詢經常檢索某一日期範圍內的記錄,則使用聚集索引可以迅速找到包含開始日期的行,然後檢索表中所有相鄰的行,直到到達結束日期。這樣有助於提高此 類查詢的性能。同樣,如果對從表中檢索的數據進行排序時經常要用到某一列,則可以將該表在該列上聚集(物理排序),避免每次查詢該列時都進行排序,從而節 省成本。
當索引值唯一時,使用聚集索引查找特定的行也很有效率。例如,使用唯一僱員 ID 列 emp_id 查找特定僱員的最快速的方法,是在 emp_id 列上創建聚集索引或 PRIMARY KEY 約束。
如果涉及到大數據量的排序、全表掃描、count之類的操作的話,還是MyISAM佔優勢些,因爲索引所佔空間小,這些操作是需要在內存中完成的。
非聚集索引:
非聚集索引,必須先查到目錄中查到每一項數據對應的頁碼,然後再根據頁碼查到具體內容,該索引中索引的邏輯順序與磁盤上行的物理存儲順序不同。記錄的物理順序與邏輯順序沒有必然的聯繫 索引是通過B-Tree的數據結構來描述的,我們可以這麼理解聚簇索引:索引的葉節點就是數據節點。而非聚簇索引的葉節點仍然是索引節點,只不過有一個指針指向對應的數據塊。
備註:每個表只能有一個聚簇索引,因爲一個表中的記錄只能以一種物理順序存放。但是,一個表可以有不止一個非聚簇索引。聚集索引一張表只能創建一個,非聚集索引一張表可以創建多個,在mysql中InnoDB引擎是唯一支持聚集索引的存儲引擎。InnoDB按照主鍵(Primary Key)進行聚集,如果沒有定義主鍵,InnoDB會試着使用唯一的非空索引來代替。如果沒有這種索引,InnoDB就會定義隱藏的主鍵然後在上面進行聚集。
非聚簇索引需要大量的硬盤空間和內存。另外,雖然非聚簇索引可以提高從表中取數據的速度,它也會降低向表中插入和更新數據的速度。每當你改變了一個建立了非聚簇索引的表中的數據時,必須同時更新索引。如果你預計一個表需要頻繁地更新數據,那麼不要對它建立太多非聚簇索引。另外,如果硬盤和內存空間有限,也應該限制使用非聚簇索引的數量。從邏輯角度
1、普通索引或者單列索引
是最基本的索引,它沒有任何限制。它有以下幾種創建方式:
(1)直接創建索引
CREATE INDEX index_name ON table(column(length))
(2)修改表結構的方式添加索引
ALTER TABLE table_name ADD INDEX index_name ON (column(length))
(3)創建表的時候同時創建索引
CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER NOT NULL , `content` text CHARACTER NULL , `time` int(10) NULL DEFAULT NULL , PRIMARY KEY (`id`), INDEX index_name (title(length)) )
(4)刪除索引
DROP INDEX index_name ON table
2、唯一索引或者非唯一索引
與前面的普通索引類似,不同的就是:索引列的值必須唯一,但允許有空值。如果是組合索引,則列值的組合必須唯一。它有以下幾種創建方式:
(1)創建唯一索引
CREATE UNIQUE INDEX indexName ON table(column(length))
(2)修改表結構
ALTER TABLE table_name ADD UNIQUE indexName ON (column(length))
(3)創建表的時候直接指定
CREATE TABLE `table` ( `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER NOT NULL , `content` text CHARACTER NULL , `time` int(10) NULL DEFAULT NULL , UNIQUE indexName (title(length)) );
3、主鍵索引:主鍵索引是一種特殊的唯一索引,一個表只能有一個主鍵,不允許有空值,一般是在建表的時候同時創建主鍵索引:
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) NOT NULL ,
PRIMARY KEY (`id`)
);
4、多列索引(組合索引):
指多個字段上創建的索引,只有在查詢條件中使用了創建索引時的第一個字段,索引纔會被使用。使用組合索引時遵循最左前綴集合
ALTER TABLE `table` ADD INDEX name_city_age (name,city,age);
5、空間索引:空間索引是對空間數據類型的字段建立的索引,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]
1、unique|fulltext|spatial爲可選參數,分別表示唯一索引、全文索引和空間索引;
2、index和key爲同義詞,兩者作用相同,用來指定創建索引
3、col_name爲需要創建索引的字段列,該列必須從數據表中該定義的多個列中選擇;
4、index_name指定索引的名稱,爲可選參數,如果不指定,MYSQL默認col_name爲索引值;
5、length爲可選參數,表示索引的長度,只有字符串類型的字段才能指定索引長度;
6、asc或desc指定升序或降序的索引值存儲
缺點
1.雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對錶進行insert、update和delete。因爲更新表時,不僅要保存數據,還要保存一下索引文件。
2.建立索引會佔用磁盤空間的索引文件。一般情況這個問題不太嚴重,但如果你在一個大表上創建了多種組合索引,索引文件的會增長很快。
索引只是提高效率的一個因素,如果有大數據量的表,就需要花時間研究建立最優秀的索引,或優化查詢語句。
注意事項
使用索引時,有以下一些技巧和注意事項:
1.索引不會包含有null值的列
只要列中包含有null值都將不會被包含在索引中,複合索引中只要有一列含有null值,那麼這一列對於此複合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值爲null。
2.使用短索引
對串列進行索引,如果可能應該指定一個前綴長度。例如,如果有一個char(255)的列,如果在前10個或20個字符內,多數值是惟一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
3.索引列排序
查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作;儘量不要包含多個列的排序,如果需要最好給這些列創建複合索引。
4.like語句操作
一般情況下不推薦使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。
5.不要在列上進行運算
這將導致索引失效而進行全表掃描,例如
SELECT * FROM table_name WHERE YEAR(column_name)<2017;
6.不使用not in和<>操作