MySQL索引類型及優化


索引是快速搜索的關鍵。MySQL索引的建立對於MySQL的高效運行是很重要的。下面介紹幾種常見的MySQL索引類型。

數據庫表中,對字段建立索引可以大大提高查詢速度。假如我們創建了一個 mytable表:

CREATE TABLE mytable(

ID INT NOT NULL,  

username VARCHAR(16) NOT NULL

);

我們隨機向里面插入了10000條記錄,其中有一條:5555, admin。

在查找username="admin"的記錄 SELECT * FROM mytable WHERE username='admin';時,如果在username上已經建立了索引,MySQL無須任何掃描,即准確可找到該記錄。相反,MySQL會掃描所有記錄,即要查詢10000條記錄。

索引分單列索引和組合索引。單列索引,即一個索引只包含單個列,一個表可以有多個單列索引,但這不是組合索引。組合索引,即一個索包含多個列。

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="鄭州" 
(5)建立索引的時機

到這里我們已經學會了建立索引,那么我們需要在什么情況下建立索引呢?一般來說,在WHERE和JOIN中出現的列需要建立索引,但也不完全如此,因為MySQL只對<,<=,=,>,>=,BETWEEN,IN,以及某些時候的LIKE才會使用索引。例如:

SELECT t.Name

FROM mytable t LEFT JOIN mytable m  

ON t.Name=m.username WHERE m.age=20 AND m.city='鄭州' 
此時就需要對city和age建立索引,由於mytable表的userame也出現在了JOIN子句中,也有對它建立索引的必要。

剛才提到只有某些時候的LIKE才需建立索引。因為在以通配符%和_開頭作查詢時,MySQL不會使用索引。例如下句會使用索引:

SELECT * FROM mytable WHERE username like'admin%' 
而下句就不會使用:

SELECT * FROM mytable WHEREt Name like'%admin' 
因此,在使用LIKE時應注意以上的區別。

(6)索引的不足之處

上面都在說使用索引的好處,但過多的使用索引將會造成濫用。因此索引也會有它的缺點:

◆雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對表進行INSERT、UPDATE和DELETE。因為更新表時,MySQL不僅要保存數據,還要保存一下索引文件。

◆建立索引會占用磁盤空間的索引文件。一般情況這個問題不太嚴重,但如果你在一個大表上創建了多種組合索引,索引文件的會膨脹很快。

索引只是提高效率的一個因素,如果你的MySQL有大數據量的表,就需要花時間研究建立最優秀的索引,或優化查詢語句。

(7)使用索引的注意事項

使用索引時,有以下一些技巧和注意事項:

◆索引不會包含有NULL值的列

只要列中包含有NULL值都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那么這一列對於此復合索引就是無效的。所以我們在數據庫設計時不要讓字段的默認值為NULL。

◆使用短索引

對串列進行索引,如果可能應該指定一個前綴長度。例如,如果有一個CHAR(255)的列,如果在前10個或20個字符內,多數值是惟一的,那么就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。

◆索引列排序

MySQL查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那么order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作;盡量不要包含多個列的排序,如果需要最好給這些列創建復合索引。

◆like語句操作

一般情況下不鼓勵使用like操作,如果非使用不可,如何使用也是一個問題。like “%aaa%” 不會使用索引而like “aaa%”可以使用索引。

◆不要在列上進行運算

select * from users where YEAR(adddate)<2007; 
將在每個行上進行運算,這將導致索引失效而進行全表掃描,因此我們可以改成

select * from users where adddate<‘2007-01-01’; 
◆不使用NOT IN和<>操作

以上,就對其中MySQL索引類型進行了介紹。

索引對查詢的速度有着至關重要的影響,理解索引也是進行數據庫性能調優的起點。考慮如下情況,假設數據庫中一個表有10^6條記 錄,DBMS的頁面大小為4K,並存儲100條記錄。如果沒有索引,查詢將對整個表進行掃描,最壞的情況下,如果所有數據頁都不在內存,需要讀取10^4 個頁面,如果這10^4個頁面在磁盤上隨機分布,需要進行10^4次I/O,假設磁盤每次I/O時間為10ms(忽略數據傳輸時間),則總共需要 100s(但實際上要好很多很多)。如果對之建立B-Tree索引,則只需要進行log100(10^6)=3次頁面讀取,最壞情況下耗時30ms。這就 是索引帶來的效果,很多時候,當你的應用程序進行SQL查詢速度很慢時,應該想想是否可以建索引。進入正題:

第二章、索引與優化

1、選擇索引的數據類型

MySQL支持很多數據類型,選擇合適的數據類型存儲數據對性能有很大的影響。通常來說,可以遵循以下一些指導原則:

(1)越小的數據類型通常更好:越小的數據類型通常在磁盤、內存和CPU緩存中都需要更少的空間,處理起來更快。
(2)簡單的數據類型更好:整型數據比起字符,處理開銷更小,因為字符串的比較更復雜。在MySQL中,應該用內置的日期和時間數據類型,而不是用字符串來存儲時間;以及用整型數據類型存儲IP地址。
(3)盡量避免NULL:應該指定列為NOT NULL,除非你想存儲NULL。在MySQL中,含有空值的列很難進行查詢優化,因為它們使得索引、索引的統計信息以及比較運算更加復雜。你應該用0、一個特殊的值或者一個空串代替空值。

1.1、選擇標識符
選擇合適的標識符是非常重要的。選擇時不僅應該考慮存儲類型,而且應該考慮MySQL是怎樣進行運算和比較的。一旦選定數據類型,應該保證所有相關的表都使用相同的數據類型。
(1)   整型:通常是作為標識符的最好選擇,因為可以更快的處理,而且可以設置為AUTO_INCREMENT。

(2)   字符串:盡量避免使用字符串作為標識符,它們消耗更好的空間,處理起來也較慢。而且,通常來說,字符串都是隨機的,所以它們在索引中的位置也是隨機的,這會導致頁面分裂、隨機訪問磁盤,聚簇索引分裂(對於使用聚簇索引的存儲引擎)。

2、索引入門
對於任何DBMS,索引都是進行優化的最主要的因素。對於少量的數據,沒有合適的索引影響不是很大,但是,當隨着數據量的增加,性能會急劇下降。
如果對多列進行索引(組合索引),列的順序非常重要,MySQL僅能對索引最左邊的前綴進行有效的查找。例如:
假 設存在組合索引it1c1c2(c1,c2),查詢語句select * from t1 where c1=1 and c2=2能夠使用該索引。查詢語句select * from t1 where c1=1也能夠使用該索引。但是,查詢語句select * from t1 where c2=2不能夠使用該索引,因為沒有組合索引的引導列,即,要想使用c2列進行查找,必需出現c1等於某值。

2.1、索引的類型
索引是在存儲引擎中實現的,而不是在服務器層中實現的。所以,每種存儲引擎的索引都不一定完全相同,並不是所有的存儲引擎都支持所有的索引類型。
2.1.1、B-Tree索引
假設有如下一個表:

 

CREATE TABLE People (

   last_name varchar(50)    not null,

   first_name varchar(50)    not null,

   dob        date           not null,

   gender     enum('m', 'f') not null,

   key(last_name, first_name, dob)

);

 

其索引包含表中每一行的last_name、first_name和dob列。其結構大致如下:

索引存儲的值按索引列中的順序排列。可以利用B-Tree索引進行全關鍵字、關鍵字范圍和關鍵字前綴查詢,當然,如果想使用索引,你必須保證按索引的最左邊前綴(leftmost prefix of the index)來進行查詢。
(1)匹配全值(Match the full value):對索引中的所有列都指定具體的值。例如,上圖中索引可以幫助你查找出生於1960-01-01的Cuba Allen。
(2)匹配最左前綴(Match a leftmost prefix):你可以利用索引查找last name為Allen的人,僅僅使用索引中的第1列。
(3)匹配列前綴(Match a column prefix):例如,你可以利用索引查找last name以J開始的人,這僅僅使用索引中的第1列。
(4)匹配值的范圍查詢(Match a range of values):可以利用索引查找last name在Allen和Barrymore之間的人,僅僅使用索引中第1列。
(5)匹配部分精確而其它部分進行范圍匹配(Match one part exactly and match a range on another part):可以利用索引查找last name為Allen,而first name以字母K開始的人。
(6)僅對索引進行查詢(Index-only queries):如果查詢的列都位於索引中,則不需要讀取元組的值。
由於B-樹中的節點都是順序存儲的,所以可以利用索引進行查找(找某些值),也可以對查詢結果進行ORDER BY。當然,使用B-tree索引有以下一些限制:
(1) 查詢必須從索引的最左邊的列開始。關於這點已經提了很多遍了。例如你不能利用索引查找在某一天出生的人。
(2) 不能跳過某一索引列。例如,你不能利用索引查找last name為Smith且出生於某一天的人。
(3) 存儲引擎不能使用索引中范圍條件右邊的列。例如,如果你的查詢語句為WHERE last_name="Smith" AND first_name LIKE 'J%' AND dob='1976-12-23',則該查詢只會使用索引中的前兩列,因為LIKE是范圍查詢。

2.1.2、Hash索引
MySQL 中,只有Memory存儲引擎顯示支持hash索引,是Memory表的默認索引類型,盡管Memory表也可以使用B-Tree索引。Memory存儲 引擎支持非唯一hash索引,這在數據庫領域是罕見的,如果多個值有相同的hash code,索引把它們的行指針用鏈表保存到同一個hash表項中。
假設創建如下一個表:
CREATE TABLE testhash (
fname VARCHAR(50) NOT NULL,
lname VARCHAR(50) NOT NULL,
KEY USING HASH(fname)
) ENGINE=MEMORY;
包含的數據如下:

假設索引使用hash函數f( ),如下:

 

f('Arjen') = 2323

f('Baron') = 7437

f('Peter') = 8784

f('Vadim') = 2458

 

此時,索引的結構大概如下:

Slots是有序的,但是記錄不是有序的。當你執行
mysql> SELECT lname FROM testhash WHERE fname='Peter';
MySQL會計算’Peter’的hash值,然后通過它來查詢索引的行指針。因為f('Peter') = 8784,MySQL會在索引中查找8784,得到指向記錄3的指針。
因為索引自己僅僅存儲很短的值,所以,索引非常緊湊。Hash值不取決於列的數據類型,一個TINYINT列的索引與一個長字符串列的索引一樣大。

Hash索引有以下一些限制:
(1)由於索引僅包含hash code和記錄指針,所以,MySQL不能通過使用索引避免讀取記錄。但是訪問內存中的記錄是非常迅速的,不會對性造成太大的影響。
(2)不能使用hash索引排序。
(3)Hash索引不支持鍵的部分匹配,因為是通過整個索引值來計算hash值的。
(4)Hash索引只支持等值比較,例如使用=,IN( )和<=>。對於WHERE price>100並不能加速查詢。
2.1.3、空間(R-Tree)索引
MyISAM支持空間索引,主要用於地理空間數據類型,例如GEOMETRY。
2.1.4、全文(Full-text)索引
全文索引是MyISAM的一個特殊索引類型,主要用於全文檢索。

3、高性能的索引策略
3.1、聚簇索引(Clustered Indexes)
聚 簇索引保證關鍵字的值相近的元組存儲的物理位置也相同(所以字符串類型不宜建立聚簇索引,特別是隨機字符串,會使得系統進行大量的移動操作),且一個表只 能有一個聚簇索引。因為由存儲引擎實現索引,所以,並不是所有的引擎都支持聚簇索引。目前,只有solidDB和InnoDB支持。
聚簇索引的結構大致如下:

注: 葉子頁面包含完整的元組,而內節點頁面僅包含索引的列(索引的列為整型)。一些DBMS允許用戶指定聚簇索引,但是MySQL的存儲引擎到目前為止都不支 持。InnoDB對主鍵建立聚簇索引。如果你不指定主鍵,InnoDB會用一個具有唯一且非空值的索引來代替。如果不存在這樣的索引,InnoDB會定義 一個隱藏的主鍵,然后對其建立聚簇索引。一般來說,DBMS都會以聚簇索引的形式來存儲實際的數據,它是其它二級索引的基礎。

3.1.1、InnoDB和MyISAM的數據布局的比較
為了更加理解聚簇索引和非聚簇索引,或者primary索引和second索引(MyISAM不支持聚簇索引),來比較一下InnoDB和MyISAM的數據布局,對於如下表:

 

 

CREATE TABLE layout_test (

   col1 int NOT NULL,

   col2 int NOT NULL,

   PRIMARY KEY(col1),

   KEY(col2)

);

 

假設主鍵的值位於1---10,000之間,且按隨機順序插入,然后用OPTIMIZE TABLE進行優化。col2隨機賦予1---100之間的值,所以會存在許多重復的值。
(1)   MyISAM的數據布局
其布局十分簡單,MyISAM按照插入的順序在磁盤上存儲數據,如下:

注:左邊為行號(row number),從0開始。因為元組的大小固定,所以MyISAM可以很容易的從表的開始位置找到某一字節的位置。
據些建立的primary key的索引結構大致如下:

注:MyISAM不支持聚簇索引,索引中每一個葉子節點僅僅包含行號(row number),且葉子節點按照col1的順序存儲。
來看看col2的索引結構:

實際上,在MyISAM中,primary key和其它索引沒有什么區別。Primary key僅僅只是一個叫做PRIMARY的唯一,非空的索引而已。

(2)   InnoDB的數據布局
InnoDB按聚簇索引的形式存儲數據,所以它的數據布局有着很大的不同。它存儲表的結構大致如下:

注:聚簇索引中的每個葉子節點包含primary key的值,事務ID和回滾指針(rollback pointer)——用於事務和MVCC,和余下的列(如col2)。

相 對於MyISAM,二級索引與聚簇索引有很大的不同。InnoDB的二級索引的葉子包含primary key的值,而不是行指針(row pointers),這減小了移動數據或者數據頁面分裂時維護二級索引的開銷,因為InnoDB不需要更新索引的行指針。其結構大致如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM