最普通的狀況,是爲出如今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);
再次執行上述查詢語句,其對比很是明顯:
索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着對數據表裏全部記錄的引用指針。更通俗的說,數據庫索引比如是一本書前面的目錄,能加快數據庫的查詢速度。上述SQL語句,在沒有索引的狀況下,數據庫會遍歷所有200條數據後選擇符合條件的;而有了相應的索引以後,數據庫會直接在索引中查找符合條件的選項。若是咱們把SQL語句換成「SELECT * FROM article WHERE id=2000000」,那麼你是但願數據庫按照順序讀取完200萬行數據之後給你結果仍是直接在索引中定位呢?上面的兩個圖片鮮明的用時對比已經給出了答案(注:通常數據庫默認都會爲主鍵生成索引)。
索引分爲聚簇索引和非聚簇索引兩種,聚簇索引是按照數據存放的物理位置爲順序的,而非聚簇索引就不同了;聚簇索引能提升多行檢索的速度,而非聚簇索引對於單行的檢索很快。
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;
上面都在說使用索引的好處,但過多的使用索引將會形成濫用。所以索引也會有它的缺點:雖然索引大大提升了查詢速度,同時卻會下降更新表的速度,如對錶進行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` )
轉載標明出處謝謝!