??索引對查詢的速度有著至關(guān)重要的影響朝抖,理解索引也是進行數(shù)據(jù)庫性能調(diào)優(yōu)的起點糊闽∈崦担考慮如下情況,假設(shè)數(shù)據(jù)庫中一個表有106條記錄右犹,DBMS的頁面大小為4K提澎,并存儲100條記錄。如果沒有索引傀履,查詢將對整個表進行掃描虱朵,最壞的情況下,如果所有數(shù)據(jù)頁都不在內(nèi)存钓账,需要讀取104個頁面碴犬,如果這104個頁面在磁盤上隨機分布,需要進行104次I/O梆暮,假設(shè)磁盤每次I/O時間為10ms(忽略數(shù)據(jù)傳輸時間)服协,則總共需要100s(但實際上要好很多很多)。如果對之建立B-Tree索引啦粹,則只需要進行l(wèi)og100(10^6)=3次頁面讀取偿荷,最壞情況下耗時30ms。這就是索引帶來的效果唠椭,很多時候跳纳,當(dāng)你的應(yīng)用程序進行SQL查詢速度很慢時,應(yīng)該想想是否可以建索引贪嫂。
索引與優(yōu)化
選擇索引的數(shù)據(jù)類型
MySQL支持很多數(shù)據(jù)類型寺庄,選擇合適的數(shù)據(jù)類型存儲數(shù)據(jù)對性能有很大的影響。通常來說,可以遵循以下一些指導(dǎo)原則:
(1)越小的數(shù)據(jù)類型通常更好:越小的數(shù)據(jù)類型通常在磁盤斗塘、內(nèi)存和CPU緩存中都需要更少的空間赢织,處理起來更快。
(2)簡單的數(shù)據(jù)類型更好:整型數(shù)據(jù)比起字符馍盟,處理開銷更小于置,因為字符串的比較更復(fù)雜。在MySQL中贞岭,應(yīng)該用內(nèi)置的日期和時間數(shù)據(jù)類型八毯,而不是用字符串來存儲時間;以及用整型數(shù)據(jù)類型存儲IP地址瞄桨。
(3)盡量避免NULL:應(yīng)該指定列為NOT NULL宪彩,除非你想存儲NULL。在MySQL中讲婚,含有空值的列很難進行查詢優(yōu)化,因為它們使得索引俊柔、索引的統(tǒng)計信息以及比較運算更加復(fù)雜筹麸。你應(yīng)該用0、一個特殊的值或者一個空串代替空值雏婶。
選擇標識符
選擇合適的標識符是非常重要的物赶。選擇時不僅應(yīng)該考慮存儲類型,而且應(yīng)該考慮MySQL是怎樣進行運算和比較的留晚。一旦選定數(shù)據(jù)類型酵紫,應(yīng)該保證所有相關(guān)的表都使用相同的數(shù)據(jù)類型。
(1) 整型:通常是作為標識符的最好選擇错维,因為可以更快的處理奖地,而且可以設(shè)置為AUTO_INCREMENT。
(2) 字符串:盡量避免使用字符串作為標識符赋焕,它們消耗更好的空間参歹,處理起來也較慢。而且隆判,通常來說犬庇,字符串都是隨機的,所以它們在索引中的位置也是隨機的侨嘀,這會導(dǎo)致頁面分裂臭挽、隨機訪問磁盤,聚簇索引分裂(對于使用聚簇索引的存儲引擎)咬腕。
索引入門
對于任何DBMS欢峰,索引都是進行優(yōu)化的最主要的因素。對于少量的數(shù)據(jù),沒有合適的索引影響不是很大赤赊,但是闯狱,當(dāng)隨著數(shù)據(jù)量的增加,性能會急劇下降抛计。
如果對多列進行索引(組合索引)哄孤,列的順序非常重要,MySQL僅能對索引最左邊的前綴進行有效的查找吹截。例如:
假設(shè)存在組合索引it1c1c2(c1,c2)瘦陈,查詢語句select * from t1 where c1=1 and c2=2能夠使用該索引。查詢語句select * from t1 where c1=1也能夠使用該索引波俄。但是晨逝,查詢語句select * from t1 where c2=2不能夠使用該索引,因為沒有組合索引的引導(dǎo)列懦铺,即捉貌,要想使用c2列進行查找,必需出現(xiàn)c1等于某值冬念。
索引的類型
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)
);
索引存儲的值按索引列中的順序排列趁窃。可以利用B-Tree索引進行全關(guān)鍵字急前、關(guān)鍵字范圍和關(guān)鍵字前綴查詢醒陆,當(dāng)然,如果想使用索引裆针,你必須保證按索引的最左邊前綴(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-樹中的節(jié)點都是順序存儲的胜蛉,所以可以利用索引進行查找(找某些值)挠进,也可以對查詢結(jié)果進行ORDER BY。當(dāng)然誊册,使用B-tree索引有以下一些限制:
(1) 查詢必須從索引的最左邊的列開始领突。關(guān)于這點已經(jīng)提了很多遍了。例如你不能利用索引查找在某一天出生的人案怯。
(2) 不能跳過某一索引列君旦。例如,你不能利用索引查找last name為Smith且出生于某一天的人嘲碱。
(3) 存儲引擎不能使用索引中范圍條件右邊的列金砍。例如,如果你的查詢語句為WHERE last_name="Smith" AND first_name LIKE 'J%' AND dob='1976-12-23'麦锯,則該查詢只會使用索引中的前兩列恕稠,因為LIKE是范圍查詢。
Hash索引
MySQL中扶欣,只有Memory存儲引擎顯示支持hash索引鹅巍,是Memory表的默認索引類型,盡管Memory表也可以使用B-Tree索引料祠。Memory存儲引擎支持非唯一hash索引昆著,這在數(shù)據(jù)庫領(lǐng)域是罕見的,如果多個值有相同的hash code术陶,索引把它們的行指針用鏈表保存到同一個hash表項中。
假設(shè)創(chuàng)建如下一個表:
CREATE TABLE testhash (
fname VARCHAR(50) NOT NULL,
lname VARCHAR(50) NOT NULL,
KEY USING HASH(fname)
) ENGINE=MEMORY;
假設(shè)索引使用hash函數(shù)f( )煤痕,如下:
f('Arjen') = 2323
f('Baron') = 7437
f('Peter') = 8784
f('Vadim') = 2458
此時,索引結(jié)果如下
2323 -> row 1
2458 -> row 4
7437 -> row 2
8784 -> row 3
Slots是有序的梧宫,但是記錄不是有序的。當(dāng)你執(zhí)行
mysql> SELECT lname FROM testhash WHERE fname='Peter';
MySQL會計算’Peter’的hash值摆碉,然后通過它來查詢索引的行指針塘匣。因為f('Peter') = 8784,MySQL會在索引中查找8784巷帝,得到指向記錄3的指針忌卤。
因為索引自己僅僅存儲很短的值,所以楞泼,索引非常緊湊驰徊。Hash值不取決于列的數(shù)據(jù)類型,一個TINYINT列的索引與一個長字符串列的索引一樣大堕阔。
Hash索引有以下一些限制:
(1)由于索引僅包含hash code和記錄指針棍厂,所以,MySQL不能通過使用索引避免讀取記錄超陆。但是訪問內(nèi)存中的記錄是非常迅速的牺弹,不會對性造成太大的影響。
(2)不能使用hash索引排序。
(3)Hash索引不支持鍵的部分匹配张漂,因為是通過整個索引值來計算hash值的晶默。
(4)Hash索引只支持等值比較,例如使用=航攒,IN( )和<=>磺陡。對于WHERE price>100并不能加速查詢。