mysql數據庫索引類型和原理

索引初識:

最普通的狀況,是爲出如今where子句的字段建一個索引。爲方便講述,咱們先創建一個以下的表。前端

CREATE TABLE mytable (
 id serial primary key,
 category_id int not null default 0,
 user_id int not null default 0,
 adddate int not null default 0
);

很簡單吧,不過對於要說明這個問題,已經足夠了。若是你在查詢時經常使用相似如下的語句:mysql

SELECT * FROM mytable WHERE category_id=1; 

  最直接的應對之道,是爲category_id創建一個簡單的索引:sql

CREATE INDEX mytable_categoryid ON mytable (category_id);

  OK,搞定?先別高興,若是你有不止一個選擇條件呢?例如:數據庫

SELECT * FROM mytable WHERE category_id=1 AND user_id=2;

  你的第一反應多是,再給user_id創建一個索引。很差,這不是一個最佳的方法。你能夠創建多重的索引。數據庫設計

CREATE INDEX mytable_categoryid_userid ON mytable (category_id,user_id);

  注意到我在命名時的習慣了嗎?我使用"表名_字段1名_字段2名"的方式。你很快就會知道我爲何這樣作了。

  如今你已經爲適當的字段創建了索引,不過,仍是有點不放心吧,你可能會問,數據庫會真正用到這些索引嗎?測試一下就OK,對於大多數的數據庫來講,這是很容易的,只要使用EXPLAIN命令:post

EXPLAIN

 SELECT * FROM mytable 
  WHERE category_id=1 AND user_id=2;

This is what Postgres 7.1 returns (exactly as I expected) 

 NOTICE: QUERY PLAN:

Index Scan using mytable_categoryid_userid on 
  mytable (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

  以上是postgres的數據,能夠看到該數據庫在查詢的時候使用了一個索引(一個好開始),並且它使用的是我建立的第二個索引。看到我上面命名的好處了吧,你立刻知道它使用適當的索引了。

  接着,來個稍微複雜一點的,若是有個ORDER BY字句呢?無論你信不信,大多數的數據庫在使用order by的時候,都將會從索引中受益。性能

SELECT * FROM mytable WHERE category_id=1 AND user_id=2 ORDER BY adddate DESC;

  有點迷惑了吧?很簡單,就象爲where字句中的字段創建一個索引同樣,也爲ORDER BY的字句中的字段創建一個索引:測試

CREATE INDEX mytable_categoryid_userid_adddate ON mytable (category_id,user_id,adddate);

  

注意: "mytable_categoryid_userid_adddate" 將會被截短爲
"mytable_categoryid_userid_addda"大數據

CREATE

  EXPLAIN SELECT * FROM mytable
  WHERE category_id=1 AND user_id=2
   ORDER BY adddate DESC;

 NOTICE: QUERY PLAN:

 Sort (cost=2.03..2.03 rows=1 width=16)
  -> Index Scan using mytable_categoryid_userid_addda 
    on mytable (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

  看看EXPLAIN的輸出,好象有點恐怖啊,數據庫多作了一個咱們沒有要求的排序,這下知道性能如何受損了吧,看來咱們對於數據庫的自身運做是有點過於樂觀了,那麼,給數據庫多一點提示吧。

  爲了跳過排序這一步,咱們並不須要其它另外的索引,只要將查詢語句稍微改一下。這裏用的是postgres,咱們將給該數據庫一個額外的提示--在ORDER BY語句中,加入where語句中的字段。這只是一個技術上的處理,並非必須的,由於實際上在另外兩個字段上,並不會有任何的排序操做,不過若是加入,postgres將會知道哪些是它應該作的。優化

EXPLAIN SELECT * FROM mytable 
  WHERE category_id=1 AND user_id=2
  ORDER BY category_id DESC,user_id DESC,adddate DESC;

NOTICE: QUERY PLAN:

Index Scan Backward using 
 mytable_categoryid_userid_addda on mytable 
   (cost=0.00..2.02 rows=1 width=16)

EXPLAIN

  如今使用咱們料想的索引了,並且它還挺聰明,知道能夠從索引後面開始讀,從而避免了任何的排序。

  以上說得細了一點,不過若是你的數據庫很是巨大,而且每日的頁面請求達上百萬算,我想你會獲益良多的。不過,若是你要作更爲複雜的查詢呢,例如將多張表結合起來查詢,特別是where限制字句中的字段是來自不止一個表格時,應該怎樣處理呢?我一般都儘可能避免這種作法,由於這樣數據庫要將各個表中的東西都結合起來,而後再排除那些不合適的行,搞很差開銷會很大。

  若是不能避免,你應該查看每張要結合起來的表,而且使用以上的策略來創建索引,而後再用EXPLAIN命令驗證一下是否使用了你料想中的索引。若是是的話,就OK。不是的話,你可能要創建臨時的表來將他們結合在一塊兒,而且使用適當的索引。

  要注意的是,創建太多的索引將會影響更新和插入的速度,由於它須要一樣更新每一個索引文件。對於一個常常須要更新和插入的表格,就沒有必要爲一個不多使用的where字句單獨創建索引了,對於比較小的表,排序的開銷不會很大,也沒有必要創建另外的索引。

  以上介紹的只是一些十分基本的東西,其實裏面的學問也很多,單憑EXPLAIN咱們是不能斷定該方法是否就是最優化的,每一個數據庫都有本身的一些優化器,雖然可能還不太完善,可是它們都會在查詢時對比過哪一種方式較快,在某些狀況下,創建索引的話也未必會快,

例如索引放在一個不連續的存儲空間時,這會增長讀磁盤的負擔,所以,哪一個是最優,應該經過實際的使用環境來檢驗。

  在剛開始的時候,若是表不大,沒有必要做索引,個人意見是在須要的時候才做索引,也可用一些命令來優化表,例如MySQL可用"OPTIMIZE TABLE"。

  綜上所述,在如何爲數據庫創建恰當的索引方面,你應該有一些基本的概念了。

 

---------------------------------------------------------------

 

關於MySQL索引的好處,若是正確合理設計而且使用索引的MySQL是一輛蘭博基尼的話,那麼沒有設計和使用索引的MySQL就是一我的力三輪車。對於沒有索引的表,單表查詢可能幾十萬數據就是瓶頸,而一般大型網站單日就可能會產生幾十萬甚至幾百萬的數據,沒有索引查詢會變的很是緩慢。仍是以WordPress來講,其多個數據表都會對常常被查詢的字段添加索引,好比wp_comments表中針對5個字段設計了BTREE索引。

一個簡單的對比測試

以我去年測試的數據做爲一個簡單示例,20多條數據源隨機生成200萬條數據,平均每條數據源都重複大概10萬次,表結構比較簡單,僅包含一個自增ID,一個char類型,一個text類型和一個int類型,單表2G大小,使用MyIASM引擎。開始測試未添加任何索引。

執行下面的SQL語句:

 SELECT id,FROM_UNIXTIME(time) FROM article WHERE a.title='測試標題';

查詢須要的時間很是恐怖的,若是加上聯合查詢和其餘一些約束條件,數據庫會瘋狂的消耗內存,而且會影響前端程序的執行。這時給title字段添加一個BTREE索引:

ALTER TABLE article ADD INDEX index_article_title ON title(200);

再次執行上述查詢語句,其對比很是明顯:

MySQL索引的概念

索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏全部記錄的引用指針。更通俗的說,數據庫索引比如是一本書前面的目錄,能加快數據庫的查詢速度。上述SQL語句,在沒有索引的狀況下,數據庫會遍歷所有200條數據後選擇符合條件的;而有了相應的索引以後,數據庫會直接在索引中查找符合條件的選項。若是咱們把SQL語句換成「SELECT * FROM article WHERE id=2000000」,那麼你是但願數據庫按照順序讀取完200萬行數據之後給你結果仍是直接在索引中定位呢?上面的兩個圖片鮮明的用時對比已經給出了答案(注:通常數據庫默認都會爲主鍵生成索引)。

索引分爲聚簇索引和非聚簇索引兩種,聚簇索引是按照數據存放的物理位置爲順序的,而非聚簇索引就不同了;聚簇索引能提升多行檢索的速度,而非聚簇索引對於單行的檢索很快。

MySQL索引的類型

1. 普通索引

這是最基本的索引,它沒有任何限制,好比上文中爲title字段建立的索引就是一個普通索引,MyIASM中默認的BTREE類型的索引,也是咱們大多數狀況下用到的索引。

–直接建立索引
CREATE INDEX index_name ON table(column(length))
–修改表結構的方式添加索引
ALTER TABLE table_name ADD INDEX index_name ON (column(length))
–建立表的時候同時建立索引
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
INDEX index_name (title(length))
)
–刪除索引
DROP INDEX index_name ON table

2. 惟一索引

與普通索引相似,不一樣的就是:索引列的值必須惟一,但容許有空值(注意和主鍵不一樣)。若是是組合索引,則列值的組合必須惟一,建立方法和普通索引相似。

–建立惟一索引
CREATE UNIQUE INDEX indexName ON table(column(length))
–修改表結構
ALTER TABLE table_name ADD UNIQUE indexName ON (column(length))
–建立表的時候直接指定
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
UNIQUE indexName (title(length))
);

3. 全文索引(FULLTEXT)

MySQL從3.23.23版開始支持全文索引和全文檢索,FULLTEXT索引僅可用於 MyISAM 表;他們能夠從CHAR、VARCHAR或TEXT列中做爲CREATE TABLE語句的一部分被建立,或是隨後使用ALTER TABLE 或CREATE INDEX被添加。////對於較大的數據集,將你的資料輸入一個沒有FULLTEXT索引的表中,而後建立索引,其速度比把資料輸入現有FULLTEXT索引的速度更爲快。不過切記對於大容量的數據表,生成全文索引是一個很是消耗時間很是消耗硬盤空間的作法。

–建立表的適合添加全文索引
CREATE TABLE `table` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ,
`content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL ,
`time` int(10) NULL DEFAULT NULL ,
PRIMARY KEY (`id`),
FULLTEXT (content)
);
–修改表結構添加全文索引
ALTER TABLE article ADD FULLTEXT index_content(content)
–直接建立索引
CREATE FULLTEXT INDEX index_content ON article(content)

4. 單列索引、多列索引

多個單列索引與單個多列索引的查詢效果不一樣,由於執行查詢時,MySQL只能使用一個索引,會從多個索引中選擇一個限制最爲嚴格的索引。

5. 組合索引(最左前綴)

平時用的SQL查詢語句通常都有比較多的限制條件,因此爲了進一步榨取MySQL的效率,就要考慮創建組合索引。例如上表中針對title和time創建一個組合索引:ALTER TABLE article ADD INDEX index_titme_time (title(50),time(10))。創建這樣的組合索引,實際上是至關於分別創建了下面兩組組合索引:

–title,time

–title

爲何沒有time這樣的組合索引呢?這是由於MySQL組合索引「最左前綴」的結果。簡單的理解就是隻從最左面的開始組合。並非只要包含這兩列的查詢都會用到該組合索引,以下面的幾個SQL所示:

–使用到上面的索引
SELECT * FROM article WHREE title='測試' AND time=1234567890;
SELECT * FROM article WHREE utitle='測試';
–不使用上面的索引
SELECT * FROM article WHREE time=1234567890;

MySQL索引的優化

上面都在說使用索引的好處,但過多的使用索引將會形成濫用。所以索引也會有它的缺點:雖然索引大大提升了查詢速度,同時卻會下降更新表的速度,如對錶進行INSERT、UPDATE和DELETE。由於更新表時,MySQL不只要保存數據,還要保存一下索引文件。創建索引會佔用磁盤空間的索引文件。通常狀況這個問題不太嚴重,但若是你在一個大表上建立了多種組合索引,索引文件的會膨脹很快。索引只是提升效率的一個因素,若是你的MySQL有大數據量的表,就須要花時間研究創建最優秀的索引,或優化查詢語句。下面是一些總結以及收藏的MySQL索引的注意事項和優化方法。

1. 什麼時候使用匯集索引或非彙集索引?

動做描述 使用匯集索引 使用非彙集索引
列常常被分組排序 使用 使用
返回某範圍內的數據 使用 不使用
一個或極少不一樣值 不使用 不使用
小數目的不一樣值 使用 不使用
大數目的不一樣值 不使用 使用
頻繁更新的列 不使用 使用
外鍵列 使用 使用
主鍵列 使用 使用
頻繁修改索引列 不使用 使用

事實上,咱們能夠經過前面彙集索引和非彙集索引的定義的例子來理解上表。如:返回某範圍內的數據一項。好比您的某個表有一個時間列,剛好您把聚合索引創建在了該列,這時您查詢2004年1月1日至2004年10月1日之間的所有數據時,這個速度就將是很快的,由於您的這本字典正文是按日期進行排序的,聚類索引只須要找到要檢索的全部數據中的開頭和結尾數據便可;而不像非彙集索引,必須先查到目錄中查到每一項數據對應的頁碼,而後再根據頁碼查到具體內容。其實這個具體用法我還不是很理解,只能等待後期的項目開發中慢慢學學了。

2. 索引不會包含有NULL值的列

只要列中包含有NULL值都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引就是無效的。因此咱們在數據庫設計時不要讓字段的默認值爲NULL。

3. 使用短索引

對串列進行索引,若是可能應該指定一個前綴長度。例如,若是有一個CHAR(255)的列,若是在前10個或20個字符內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不只能夠提升查詢速度並且能夠節省磁盤空間和I/O操做。

4. 索引列排序

MySQL查詢只使用一個索引,所以若是where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。所以數據庫默認排序能夠符合要求的狀況下不要使用排序操做;儘可能不要包含多個列的排序,若是須要最好給這些列建立複合索引。

5. like語句操做

通常狀況下不鼓勵使用like操做,若是非使用不可,如何使用也是一個問題。like 「%aaa%」 不會使用索引而like 「aaa%」可使用索引。

6. 不要在列上進行運算

例如:select * from users where YEAR(adddate)<2007,將在每一個行上進行運算,這將致使索引失效而進行全表掃描,所以咱們能夠改爲:select * from users where adddate<’2007-01-01′。關於這一點能夠圍觀:一個單引號引起的MYSQL性能損失。

最後總結一下,MySQL只對一下操做符才使用索引:<,<=,=,>,>=,between,in,以及某些時候的like(不以通配符%或_開頭的情形)。而理論上每張表裏面最多可建立16個索引,不過除非是數據量真的不少,不然過多的使用索引也不是那麼好玩的,好比我剛纔針對text類型的字段建立索引的時候,系統差點就卡死了。

----------------------------------------------------------------------

 

創建索引的優缺點:

爲何要建立索引呢?

        這是由於,建立索引能夠大大提升系統的性能。 
        第1、經過建立惟一性索引,能夠保證數據庫表中每一行數據的惟一性。 
        第2、能夠大大加快 數據的檢索速度,這也是建立索引的最主要的緣由。 
        第3、能夠加速表和表之間的鏈接,特別是在實現數據的參考完整性方面特別有意義。 
        第4、在使用分組和排序子句進行數據檢索時,一樣能夠顯著減小查詢中分組和排序的時間。 
        第5、經過使用索引,能夠在查詢的過程當中,使用優化隱藏器,提升系統的性能。

        也許會有人要問:增長索引有如此多的優勢,爲何不對錶中的每個列建立一個索引呢?這種想法當然有其合理性,然而也有其片面性。雖然,索引有許多優勢, 可是,爲表中的每個列都增長索引,是很是不明智的。

       這是由於,增長索引也有許多不利的一個方面:

        第1、建立索引和維護索引要耗費時間,這種時間隨着數據量的增長而增長。 

        第2、索引須要佔物理空間,除了數據表佔數據空間以外,每個索引還要佔必定的物理空間。若是要創建聚簇索引,那麼須要的空間就會更大。 

        第3、當對錶中的數據進行增長、刪除和修改的時候,索引也要動態的維護,這樣就下降了數據的維護速度。

什麼樣的字段適合建立索引:

 索引是創建在數據庫表中的某些列的上面。所以,在建立索引的時候,應該仔細考慮在哪些列上能夠建立索引,在哪些列上不能建立索引。

       通常來講,應該在這些列上建立索引,例如:

       第1、在常常須要搜索的列上,能夠加快搜索的速度; 

       第2、在做爲主鍵的列上,強制該列的惟一性和組織表中數據的排列結構; 

       第3、在常常用在鏈接的列上,這些列主要是一些外鍵,能夠加快鏈接的速度; 

       第4、在常常須要根據範圍進行搜索的列上建立索引,由於索引已經排序,其指定的範圍是連續的; 

       第5、在常常須要排序的列上建立索引,由於索引已經排序,這樣查詢能夠利用索引的排序,加快排序查詢時間; 

       第6、在常用在WHERE子句中的列上面建立索引,加快條件的判斷速度。

 

       創建索引,通常按照select的where條件來創建,好比: select的條件是where f1 and f2,那麼若是咱們在字段f1或字段f2上簡歷索引是沒有用的,只有在字段f1和f2上同時創建索引纔有用等。

什麼樣的字段不適合建立索引:

一樣,對於有些列不該該建立索引。通常來講,不該該建立索引的的這些列具備下列特色:

  第一,對於那些在查詢中不多使用或者參考的列不該該建立索引。這是由於,既然這些列不多使用到,所以有索引或者無索引,

並不能提升查詢速度。相反,因爲增長了索引,反而下降了系統的維護速度和增大了空間需求。 
       第二,對於那些只有不多數據值的列也不該該增長索引。這是由於,因爲這些列的取值不多,例如人事表的性別列,

在查詢的結果中,結果集的數據行佔了表中數據行的很大比 例,即須要在表中搜索的數據行的比例很大。

增長索引,並不能明顯加快檢索速度。 
       第三,對於那些定義爲text, image和bit數據類型的列不該該增長索引。這是由於,這些列的數據量要麼至關大,要麼取值不多。 
       第四,當修改性能遠遠大於檢索性能時,不該該建立索 引。這是由於,修改性能和檢索性能是互相矛盾的。

當增長索引時,會提升檢索性能,可是會下降修改性能。當減小索引時,會提升修改性能,下降檢索性能。

所以,當修改性能遠遠大於檢索性能時,不該該建立索引。

建立索引的方法::

一、建立索引,例如 create index <索引的名字> on table_name (列的列表); 
      二、修改表,例如 alter table table_name add index[索引的名字] (列的列表); 
      三、建立表的時候指定索引,例如create table table_name ( [...], INDEX [索引的名字] (列的列表) );

查看錶中索引的方法:

show index from table_name; 查看索引

索引的類型及建立例子::

1.PRIMARY KEY (主鍵索引)

MySQL> alter table table_name add primary key ( `column` )

2.UNIQUE 或 UNIQUE KEY (惟一索引)

mysql> alter table table_name add unique (`column`)

3.FULLTEXT (全文索引)

mysql> alter table table_name add fulltext (`column` )

4.INDEX (普通索引)

mysql> alter table table_name add index index_name ( `column` )

5.多列索引 (聚簇索引)

 mysql> alter table `table_name` add index index_name ( `column1`, `column2`, `column3` )

 

轉載標明出處謝謝!

相關文章
相關標籤/搜索