一、什麼是索引?
索引用來快速地尋找那些具有特定值的記錄,所有MySQL索引都以B-tree的形式保存。如果沒有索引,執行查詢時MySQL必須從第一個記錄開始掃描整個表的所有記錄,直至找到符合要求的記錄。表裏面的記錄數量越多,這個操作的代價就越高。如果作爲搜索條件的列上已經創建了索引,MySQL無需掃描任何記錄即可迅速得到目標記錄所在的位置。如果表有100萬條記錄,通過索引查找記錄至少要比順序掃描記錄快1000倍。
二、索引的原理
爲表設置索引要付出代價的:一是增加了數據庫的存儲空間,二是在插入和修改數據時要花費較多的時間(因爲索引也要隨之變動)。
上圖展示了一種可能的索引方式。左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也並不是一定物理相鄰的)。爲了加快 Col2 的查找,可以維護一個右邊所示的二叉查找樹,每個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就可以運用二叉查找在 O(log2n)的複雜度內獲取到相應數據。
創建索引可以大大提高系統的性能:
第一,通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性。
第二,可以大大加快數據的檢索速度,這也是創建索引的最主要的原因。
第三,可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義。
第四,在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間。
第五,通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。
也許會有人要問:增加索引有如此多的優點,爲什麼不對錶中的每一個列創建一個索引呢?
因爲,增加索引也有許多不利的方面:
第一,創建索引和維護索引要耗費時間,這種時間隨着數據量的增加而增加。
第二,索引需要佔物理空間,除了數據表佔數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。
第三,當對錶中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。
索引是建立在數據庫表中的某些列的上面。在創建索引的時候,應該考慮在哪些列上可以創建索引,在哪些列上不能創建索引。一般來說,應該在這些列上創建索引:
1、在經常需要搜索的列上,可以加快搜索的速度;
2、在作爲主鍵的列上,強制該列的唯一性和組織表中數據的排列結構;
3、在經常用在連接的列上,這些列主要是一些外鍵,可以加快連接的速度;
4、在經常需要根據範圍進行搜索的列上創建索引,因爲索引已經排序,其指定的範圍是連續的;
5、在經常需要排序的列上創建索引,因爲索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;
6、在經常使用在 WHERE 子句中的列上面創建索引,加快條件的判斷速度。
根據數據庫的功能,可以在數據庫設計器中創建三種索引:唯一索引、主鍵索引和聚集索引。
三、索引的分類:
主鍵索引
主鍵是一種唯一性索引,但它必須指定爲“PRIMARY KEY”。如果你曾經用過AUTO_INCREMENT類型的列,你可能已經熟悉主鍵之類的概念了。主鍵一般在創建表的時候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。但是,我們也可以通過修改表的方式加入主鍵,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每個表只能有一個主鍵。
當一張表,把某個列設爲主鍵的時候,則該列就是主鍵索引
-
create table aaa(
-
id int unsigned primary key auto_increment ,
-
name varchar(32) not null default '');
這是id 列就是主鍵索引.
如果你創建表時,沒有指定主鍵索引,也可以在創建表後,在添加, 指令:
實例:
alter table 表名 add primary key (列名);
刪除主鍵索引
alter table articles drop primary key;
查詢索引
-
desc 表名; 不能顯示索引名稱
-
show index from 表名
-
show keys from 表名
普通索引
普通索引(由關鍵字KEY或INDEX定義的索引)的唯一任務是加快對數據的訪問速度。因此,應該只爲那些最經常出現在查詢條件(WHERE column= )或排序條件(ORDER BY column)中的數據列創建索引。只要有可能,就應該選擇一個數據最整齊、最緊湊的數據列(如一個整數類型的數據列)來創建索引。
-
create table ccc(
-
id int unsigned,
-
name varchar(32)
-
);
-
create index 索引名 on 表 (列1,列2);
唯一索引(不能用重複'')
這種索引和前面的“普通索引”基本相同,但有一個區別:索引列的所有值都只能出現一次,即必須唯一。唯一性索引可以用以下幾種方式創建:
創建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表);
修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);
創建表的時候指定索引,例如CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) );
-
create table ddd(
-
id int primary key auto_increment ,
-
name varchar(32) unique);
注意
unique字段可以爲NULL,並可以有多NULL, 但是如果是具體內容,則不能重複,
但是不能存有重複的空字符串’’
組合索引(my_ind)
創建一個組合索引
alter table dept add index my_ind (dname,loc); //dname 左邊的列,loc就是右邊的列
只使用dname而不使用loc一起作爲條件查找,dname會使用組合索引進行查找TYPE爲my_ind,但只使用loc不使用dname作爲條件查找,不會使用索引TYPE爲ALL(後面會提到mysql的執行計劃ALL爲全文查找)。
全文索引
FULLTEXT
-
CREATE TABLE articles (
-
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
-
title VARCHAR(200),
-
body TEXT,
-
FULLTEXT (title,body)
-
)engine=myisam charset utf8;
-
INSERT INTO articles (title,body) VALUES
-
('MySQL Tutorial','DBMS stands for DataBase ...'),
-
('How To Use MySQL Well','After you went through a ...'),
-
('Optimizing MySQL','In this tutorial we will show ...'),
-
('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
-
('MySQL vs. YourSQL','In the following database comparison ...'),
-
('MySQL Security','When configured properly, MySQL ...');
錯誤用法:
select * from articles where body like '%mysql%'; 錯誤用法 索引不會生效
正確用法:
select * from articles where match(title,body) against ( 'database');
說明:
在mysql中fulltext 索引只針對 myisam生效,只有myisam支持全文索引
mysql自己提供的fulltext針對英文生效->sphinx (coreseek) 技術處理中文
使用方法是 match(字段名..) against(‘關鍵字’)
全文索引:停止詞, 因爲在一個文本中,創建索引是一個無窮大的數,因此,對一些常用詞和字符,就不會創建,這些詞,稱爲停止詞.比如(a,b,mysql,the)
mysql> select match(title,body) against ('database') from articles;(輸出的是每行和database的匹配度)
四、MySQL explain執行計劃解讀
當我們在查詢前能否預先估計查詢究竟要涉及多少行、使用哪些索引、運行時間呢?答案是能的,mysql提供了相應的功能和語法來實現該功能。
MySql提供了EXPLAIN語法用來進行查詢分析,在SQL語句前加一個"EXPLAIN"即可。比如我們要分析如下SQL語句:
explain select * from table where table.id = 1;
運行上面的sql語句後你會看到,下面的表頭信息:
table | type | possible_keys | key | key_len | ref | rows | Extra
table
顯示這一行的數據是關於哪張表的type
這是重要的列,顯示連接使用了何種類型。從最好到最差的連接類型爲const、eq_reg、ref、range、indexhe和ALL說明:不同連接類型的解釋(按照效率高低的順序排序)
system:表只有一行:system表。這是const連接類型的特殊情況。
const :表中的一個記錄的最大值能夠匹配這個查詢(索引可以是主鍵或唯一索引)。因爲只有一行,這個值實際就是常數,因爲MYSQL先讀這個值然後把它當做常數來對待。
eq_ref:在連接中,MYSQL在查詢時,從前面的表中,對每一個記錄的聯合都從表中讀取一個記錄,它在查詢使用了索引爲主鍵或惟一鍵的全部時使用。
ref:這個連接類型只有在查詢使用了不是唯一或主鍵的鍵或者是這些類型的部分(比如,利用最左邊前綴)時發生。對於之前的表的每一個行聯合,全部記錄都將從表中讀出。這個類型嚴重依賴於根據索引匹配的記錄多少—越少越好。
range:這個連接類型使用索引返回一個範圍中的行,比如使用>或<查找東西時發生的情況。
index:這個連接類型對前面的表中的每一個記錄聯合進行完全掃描(比ALL更好,因爲索引一般小於表數據)。
ALL:這個連接類型對於前面的每一個記錄聯合進行完全掃描,這一般比較糟糕,應該儘量避免。
my_ind:組合索引
key
實際使用的索引。如果爲NULL,則沒有使用索引。很少的情況下,MYSQL會選擇優化不足的索引。這種情況下,可以在SELECT語句中使用USE INDEX(indexname)來強制使用一個索引或者用IGNORE INDEX(indexname)來強制MYSQL忽略索引key_len
使用的索引的長度。在不損失精確性的情況下,長度越短越好ref
顯示索引的哪一列被使用了,如果可能的話,是一個常數rows
MYSQL認爲必須檢查的用來返回請求數據的行數extra列返回的描述的意義
Distinct :一旦mysql找到了與行相聯合匹配的行,就不再搜索了。
Not exists :mysql優化了LEFT JOIN,一旦它找到了匹配LEFT JOIN標準的行,就不再搜索了。
Range checked for each Record(index map:#) :沒有找到理想的索引,因此對從前面表中來的每一個行組合,mysql檢查使用哪個索引,並用它來從表中返回行。這是使用索引的最慢的連接之一。
Using filesort :看到這個的時候,查詢就需要優化了。mysql需要進行額外的步驟來發現如何對返回的行排序。它根據連接類型以及存儲排序鍵值和匹配條件的全部行的行指針來排序全部行。
Using index :列數據是從僅僅使用了索引中的信息而沒有讀取實際的行動的表返回的,這發生在對錶的全部的請求列都是同一個索引的部分的時候。
Using temporary :看到這個的時候,查詢需要優化了。這裏,mysql需要創建一個臨時表來存儲結果,這通常發生在對不同的列集進行ORDER BY上,而不是GROUP BY上。
Where used :使用了WHERE從句來限制哪些行將與下一張表匹配或者是返回給用戶。如果不想返回表中的全部行,並且連接類型ALL或index,這就會發生,或者是查詢有問題。
因此,弄明白了explain語法返回的每一項結果,我們就能知道查詢大致的運行時間了,如果查詢裏沒有用到索引、或者需要掃描的行過多,那麼可以感到明顯的延遲。因此需要改變查詢方式或者新建索引。mysql中的explain語法可以幫助我們改寫查詢,優化表的結構和索引的設置,從而最大地提高查詢效率。當然,在大規模數據量時,索引的建立和維護的代價也是很高的,往往需要較長的時間和較大的空間,如果在不同的列組合上建立索引,空間的開銷會更大。因此索引最好設置在需要經常查詢的字段中。
五、總結
優點:提高查詢效率
缺點:增刪慢,索引文件需要更新,增加內存
什麼情況需要用到索引
- 在where條件經常使用
- 該字段的內容不是唯一的幾個值,如性別男0,女1
- 字段內容不是頻繁變化
關於索引注意事項【重要】:
記住:聯合、like、or、null、字符串、in、group by、
1、聯合查詢 alter table dept add index my_ind (dname,loc) 使用dname或使用兩者作爲條件纔會使用到索引
2、模糊查詢在like前面有%百分號開頭會失效,因此在使用索引時候,不要使用%開頭,否則全表掃描,應寫成like "條件%"
3、如果條件中有or,即使其中有條件帶索引也不會使用。換言之,就是要求使用的所有字段,都必須建立索引, 我們建議大家儘量避免使用or關鍵字。or中,條件必須都爲索引,否則只要有一個不爲索引,則都不會進行索引
4、如果列類型是字符串,那一定要在條件中將數據使用引號引用起來,否則不使用索引。(添加時,字符串必須''), 也就是,如果列是字符串類型,就一定要用 '' 把他包括起來。