1.1 索引的介紹
索引是對數據庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問數據庫表中的特定信息。如果想按特定職員的姓來查找他或她,則與在表中搜索所有的行相比,索引有助於更快地獲取信息。
索引的一個主要目的就是加快檢索表中數據的方法,亦即能協助信息搜索者盡快的找到符合限制條件的記錄ID的輔助數據結構。

1.1.1 唯一索引
唯一索引是不允許其中任何兩行具有相同索引值的索引。當現有數據中存在重復的鍵值時,大多數數據庫不允許將新創建的唯一索引與表一起保存。數據庫還可能防止添加將在表中創建重復鍵值的新數據。
例如,如果在employee表中職員的姓(lname)上創建了唯一索引,則任何兩個員工都不能同姓。
1.1.2 主鍵索引
數據庫表經常有一列或多列組合,其值唯一標識表中的每一行。該列稱為表的主鍵。在數據庫關系圖中為表定義主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特定類型。
該索引要求主鍵中的每個值都唯一。當在查詢中使用主鍵索引時,它還允許對數據的快速訪問。
1.1.3 聚集索引
在聚集索引中,表中行的物理順序與鍵值的邏輯(索引)順序相同。一個表只能包含一個聚集索引。如果某索引不是聚集索引,則表中行的物理順序與鍵值的邏輯順序不匹配。與非聚集索引相比,聚集索引通常提供更快的數據訪問速度。
聚集索引和非聚集索引的區別,如字典默認按字母順序排序,讀者如知道某個字的讀音可根據字母順序快速定位。因此聚集索引和表的內容是在一起的。如讀者需查詢某個生僻字,則需按字典前面的索引,舉例按偏旁進行定位,找到該字對應的頁數,再打開對應頁數找到該字。
這種通過兩個地方而查詢到某個字的方式就如非聚集索引。
1.1.4 索引列
可以基於數據庫表中的單列或多列創建索引。多列索引可以區分其中一列可能有相同值的行。如果經常同時搜索兩列或多列或按兩列或多列排序時,索引也很有幫助。
例如,如果經常在同一查詢中為姓和名兩列設置判據,那么在這兩列上創建多列索引將很有意義。
檢查查詢的WHERE和JOIN子句。在任一子句中包括的每一列都是索引可以選擇的對象。對新索引進行試驗以檢查它對運行查詢性能的影響。考慮已在表上創建的索引數量。最好避免在單個表上有很多索引。
檢查已在表上創建的索引的定義。最好避免包含共享列的重疊索引。
檢查某列中唯一數據值的數量,並將該數量與表中的行數進行比較。比較的結果就是該列的可選擇性,這有助於確定該列是否適合建立索引,如果適合,確定索引的類型。
1.1.5 B樹算法

B樹的搜索,從根結點開始,如果查詢的關鍵字與結點的關鍵字相等,那么就命中;否則,如果查詢關鍵字比結點關鍵字小,就進入左邊;如果比結點關鍵字大,就進入右邊;如果左邊或右邊的指針為空,則報告找不到相應的關鍵。
如果B樹的所有非葉子結點的左右子樹的結點數目均保持差不多(平衡),那么B樹的搜索性能逼近二分查找;但它比連續內存空間的二分查找的優點是,改變B樹結構(插入與刪除結點)不需要移動大段的內存數據,甚至通常是常數開銷。
1.1.6 B+樹算法
B+樹是B-樹的變體,也是一種多路搜索樹:
1.其定義基本與B-樹同,除了: 2.非葉子結點的子樹指針與關鍵字個數相同; 3.非葉子結點的子樹指針P[i],指向關鍵字值屬於[K[i], K[i+1])的子樹(B-樹是開區間); 5.為所有葉子結點增加一個鏈指針; 6.所有關鍵字都在葉子結點出現; 如:(M=3)
B+的搜索與B-樹也基本相同,區別是B+樹只有達到葉子結點才命中(B-樹可以在非葉子結點命中),其性能也等價於在關鍵字全集做一次二分查找;
B+的特性:
1.所有關鍵字都出現在葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字恰好是有序的; 2.不可能在非葉子結點命中; 3.非葉子結點相當於是葉子結點的索引(稀疏索引),葉子結點相當於是存儲(關鍵字)數據的數據層; 4.更適合文件索引系統;
1.1.7 HASH:HASH算法
哈希索引只有Memory, NDB兩種引擎支持,Memory引擎默認支持哈希索引,如果多個hash值相同,出現哈希碰撞,那么索引以鏈表方式存儲。
但是,Memory引擎表只對能夠適合機器的內存切實有限的數據集。
要使InnoDB或MyISAM支持哈希索引,可以通過偽哈希索引來實現,叫自適應哈希索引。
主要通過增加一個字段,存儲hash值,將hash值建立索引,在插入和更新的時候,建立觸發器,自動添加計算后的hash到表里。
1.1.8 其他的索引
FULLTEXT:全文索引
RTREE:R樹索引
1.2 MySQL索引管理
索引建立在表的列上(字段)的。
在where后面的列建立索引才會加快查詢速度。
pages<---索引(屬性)<----查數據。
添加索引的方法:
alter table test add index index_name(name); create index index_name on test(name);
語法格式:
alter table 表 add index 索引名稱(name);
1.2.1 創建普通索引
創建普通索引方法一:
mysql> ALTER TABLE PLAYERS ADD INDEX name_idx(NAME); mysql> desc PLAYERS; +------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+-------------+------+-----+---------+-------+ | NAME | char(15) | NO | MUL | NULL | |
創建普通索引方法二:
mysql> ALTER TABLE PLAYERS ADD INDEX name_idx(NAME); mysql> desc PLAYERS; +------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+-------------+------+-----+---------+-------+ | NAME | char(15) | NO | MUL | NULL | |
1.2.2 刪除索引
alter table PLAYERS delete INDEX name_idx;
mysql> show index from PLAYERS\G *************************** 1. row *************************** Table: PLAYERS Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: PLAYERNO Collation: A Cardinality: 14 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment:
1.3 MySQL中的約束索引
主鍵索引
只能有一個主鍵。
主鍵索引:列的內容是唯一值,例如學號.
表創建的時候至少要有一個主鍵索引,最好和業務無關。
普通索引
加快查詢速度,工作中優化數據庫的關鍵。
在合適的列上建立索引,讓數據查詢更高效。
create index index_name on test(name); alter table test add index index_name(name);
用了索引,查一堆內容。
在where條件關鍵字后面的列建立索引才會加快查詢速度.
select id,name from test where state=1 order by id group by name;
唯一索引
內容唯一,但不是主鍵。
create unique index index_name on test(name);
1.3.1 創建主鍵索引
建立表時
CREATE TABLE `test` ( `id` int(4) NOT NULL AUTO_INCREMENT, `name` char(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
建立表后增加
CREATE TABLE `test` ( `id` int(4) NOT NULL, `name` char(20) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
增加自增主鍵
alter table test change id id int(4) primary key not null auto_increment;
1.3.2 使用字段前綴創建索引及聯合索引
前綴索引:根據字段的前N個字符建立索引
create index index_name on test(name(8));
聯合索引:多個字段建立一個索引。
where a女生 and b身高165 and c身材好 index(a,b,c)
特點:前綴生效特性。
a,ab,abc 可以走索引。 b ac bc c 不走索引(5.6之后 ac 可以走主鍵索引)。
原則:把最常用來作為條件查詢的列放在前面。
示例:
創建表
create table people (id int not null auto_increment ,name char(20),sr(20),sex int ,age int, primary key (id));
創建聯合索引
mysql> alter table people add key name_sex_idx(name,sex) -> ; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0
查看索引的類型
mysql> desc people; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | char(20) | YES | MUL | NULL | | | sex | int(11) | YES | | NULL | | | age | int(11) | YES | | NULL | | +-------+----------+------+-----+---------+----------------+
建立唯一鍵索引
mysql> alter table people add unique key age_uidx(age); Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0
查看數據表
mysql> desc people; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | char(20) | YES | MUL | NULL | | | sex | int(11) | YES | | NULL | | | age | int(11) | YES | UNI | NULL | | +-------+----------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
聯合主鍵是聯合索引的特殊形式
PRIMARY KEY (`Host`,`User`) alter table test add sex char(4) not null; create index ind_name_sex on test(name,sex);
前綴加聯合索引
create index index_name on test(name(8),sex(2));
1.4 SQL語句優化
1.4.1 企業SQL優化思路
1、把一個大的不使用索引的SQL語句按照功能進行拆分
2、長的SQL語句無法使用索引,能不能變成2條短的SQL語句讓它分別使用上索引。
3、對SQL語句功能的拆分和修改
4、減少“爛”SQL由運維(DBA)和開發交流(確認),共同確定如何改,最終由DBA執行
5、制定開發流程
1.4.2 不適合走索引的場景
1、唯一值少的列上不適合建立索引或者建立索引效率低。例如:性別列
2、小表可以不建立索引,100條記錄。
3、對於數據倉庫,大量全表掃描的情況,建索引反而會慢
1.4.3 查看表的唯一值數量
select count(distinct user) from mysql.user; select count(distinct user,host) from mysql.user;
1.4.4 建立索引流程
1、找到慢SQL。
show processlist;
記錄慢查詢日志。
2、explain select句,條件列多。
3、查看表的唯一值數量:
select count(distinct user) from mysql.user; select count(distinct user,host) from mysql.user;
條件列多。可以考慮建立聯合索引。
4、建立索引(流量低谷)
force index
5、拆開語句(和開發)。
6、like '%%'不用mysql
7、進行判斷重復的行數
查看行數:
mysql> select count(*) from city; +----------+ | count(*) | +----------+ | 4079 | +----------+ 1 row in set (0.00 sec)
查看去重后的行數:
mysql> select count(distinct countrycode) from city; +-----------------------------+ | count(distinct countrycode) | +-----------------------------+ | 232 | +-----------------------------+ 1 row in set (0.00 sec)
1.5 用explain查看SQL的執行計划
在工作中,我們用於捕捉性能問題最常用的就是打開慢查詢,定位執行效率差的SQL,那么當我們定位到一個SQL以后還不算完事,我們還需要知道該SQL的執行計划,比如是全表掃描,還是索引掃描,這些都需要通過EXPLAIN去完成。
EXPLAIN命令是查看優化器如何決定執行查詢的主要方法。可以幫助我們深入了解MySQL的基於開銷的優化器,還可以獲得很多可能被優化器考慮到的訪問策略的細節,以及當運行SQL語句時哪種策略預計會被優化器采用。
需要注意的是,生成的QEP並不確定,它可能會根據很多因素發生改變。MySQL不會將一個QEP和某個給定查詢綁定,QEP將由SQL語句每次執行時的實際情況確定,即便使用存儲過程也是如此。盡管在存儲過程中SQL語句都是預先解析過的,但QEP仍然會在每次調用存儲過程的時候才被確定。
1.5.1 查看 select 語句的執行過程
mysql> explain select id,name from test where name='clsn'; +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+ | 1 | SIMPLE | test | ref | name_idx | name_idx | 24 | const | 1 | Using where | +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+ 1 row in set (0.00 sec)
SQL_NO_CACHE的作用是禁止緩存查詢結果。
使用where條件查找
mysql> explain select user,host from mysql.user where user='root' and host='127.0.0.1'; +----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+ | 1 | SIMPLE | user | const | PRIMARY | PRIMARY | 228 | const,const | 1 | Using index | +----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+ 1 row in set (0.00 sec)
1.5.2 通過執行計划可以知道什么?
mysql> explain select d1.age, t2.id from (select age,name from t1 where id in (1,2))d1, t2 where d1.age=t2.age group by d1.age, t2.id order by t2.id; +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | Using temporary; Using filesort | | 1 | PRIMARY | t2 | ref | age | age | 5 | d1.age | 1 | Using where; Using index | | 2 | DERIVED | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ 3 rows in set (0.00 sec)
1.5.3 MySQL執行計划調用方式
1.EXPLAIN SELECT …… 2.EXPLAIN EXTENDED SELECT …… 將執行計划"反編譯"成SELECT語句,運行SHOW WARNINGS 可得到被MySQL優化器優化后的查詢語句 3.EXPLAIN PARTITIONS SELECT …… 用於分區表的EXPLAIN生成QEP的信息
1.5.4 執行計划包含的信息
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1.5.5 id
包含一組數字,表示查詢中執行select子句或操作表的順序
【示例一】id相同,執行順序由上至下
mysql> explain select t2.* from t1, t2, t3 where t1.id=t2.id and t1.id=t3.id and t1.name=''; +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ | 1 | SIMPLE | t1 | ref | PRIMARY,name | name | 63 | const | 1 | Using where; Using index | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | | | 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ 3 rows in set (0.00 sec)
【示例二】如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行
mysql> explain select t2.* from t2 where id = (select id from t1 where id = (select t3.id from t3 where t3.name='')); +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ | 1 | PRIMARY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE noticed after reading const tables | | 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table | | 3 | SUBQUERY | t3 | ref | name | name | 63 | | 1 | Using where; Using index | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ 3 rows in set (0.00 sec)
【示例三】id如果相同,可以認為是一組,從上往下順序執行;在所有組中,id值越大,優先級越高,越先執行
mysql> explain select t2.* from (select t3.id from t3 where t3.name='')s1, t2 where s1.id=t2.id; +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 1 | PRIMARY | t2 | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | DERIVED | t3 | ref | name | name | 63 | | 1 | Using where; Using index | +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ 3 rows in set (0.00 sec)
1.5.6 select_type
示查詢中每個select子句的類型(簡單OR復雜) a. SIMPLE:查詢中不包含子查詢或者UNION b. 查詢中若包含任何復雜的子部分,最外層查詢則被標記為:PRIMARY c. 在SELECT或WHERE列表中包含了子查詢,該子查詢被標記為:SUBQUERY d. 在FROM列表中包含的子查詢被標記為:DERIVED(衍生)用來表示包含在from子句中的子查詢的select,mysql會遞歸執行並將結果放到一個臨時表中。服務器內部稱為"派生表",因為該臨時表是從子查詢中派生出來的 e. 若第二個SELECT出現在UNION之后,則被標記為UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為:DERIVED f. 從UNION表獲取結果的SELECT被標記為:UNION RESULT
說明:
SUBQUERY和UNION還可以被標記為DEPENDENT和UNCACHEABLE。
DEPENDENT意味着select依賴於外層查詢中發現的數據。
UNCACHEABLE意味着select中的某些 特性阻止結果被緩存於一個item_cache中。
【示例】
mysql> explain select d1.name, ( select id from t3) d2 from (select id,name from t1 where name='')d1 union (select name,id from t2); +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ | 1 | PRIMARY | <derived3> | system | NULL | NULL | NULL | NULL | 0 | const row not found | | 3 | DERIVED | t1 | ref | name | name | 63 | | 1 | Using where; Using index | | 2 | SUBQUERY | t3 | index | NULL | age | 5 | NULL | 6 | Using index | | 4 | UNION | t2 | index | NULL | name | 63 | NULL | 4 | Using index | | NULL | UNION RESULT | <union1,4> | ALL | NULL | NULL | NULL | NULL | NULL | | +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ 5 rows in set (0.00 sec)
內容說明:
第一行:id列為1,表示第一個select,select_type列的primary表 示該查詢為外層查詢,table列被標記為<derived3>,表示查詢結果來自一個衍生表,其中3代表該查詢衍生自第三個select查詢,即id為3的select。 第二行:id為3,表示該查詢的執行次序為2( 4 => 3),是整個查詢中第三個select的一部分。因查詢包含在from中,所以為derived。 第三行:select列表中的子查詢,select_type為subquery,為整個查詢中的第二個select。 第四行:select_type為union,說明第四個select是union里的第二個select,最先執行。 第五行:代表從union的臨時表中讀取行的階段,table列的<union1,4>表示用第一個和第四個select的結果進行union操作。
1.5.7 type
表示MySQL在表中找到所需行的方式,又稱“訪問類型”,常見類型如下:
ALL, index, range, ref, eq_ref, const, system, NULL
從左到右,性能從最差到最好
【示例一】ALL:Full Table Scan, MySQL將遍歷全表以找到匹配的行
mysql> explain select * from t1 where email=''; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
【示例二】index:Full Index Scan,index與ALL區別為index類型只遍歷索引樹
mysql> explain select id from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
【示例三】range:索引范圍掃描,對索引的掃描開始於某一點,返回匹配值域的行。
顯而易見的索引范圍掃描是帶有between或者where子句里帶有<, >查詢。當mysql使用索引去查找一系列值時,例如IN()和OR列表,也會顯示range(范圍掃描),當然性能上面是有差異的。
mysql> explain select * from t1 where id in (1,4); +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec)
mysql> explain select * from t1 where id between 1 and 4; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 3 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec)
mysql> explain select * from t1 where id=1 or id=4; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.01 sec)
mysql> explain select * from t1 where id > 1; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 3 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec)
【示例四】ref:使用非唯一索引掃描或者唯一索引的前綴掃描,返回匹配某個單獨值的記錄行
mysql> explain select * from t1 where name='guo'; +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ | 1 | SIMPLE | t1 | ref | name | name | 63 | const | 1 | Using where | +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ 1 row in set (0.00 sec)
【示例五】eq_ref:類似ref,區別就在使用的索引是唯一索引,對於每個索引鍵值,表中只有一條記錄匹配,簡單來說,就是多表連接中使用primary key或者 unique key作為關聯條件。
mysql> explain select t1.name from t1, t2 where t1.id=t2.id; +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | 1 | SIMPLE | t1 | index | PRIMARY | name | 63 | NULL | 4 | Using index | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ 2 rows in set (0.00 sec)
【示例六】const、system:當MySQL對查詢某部分進行優化,並轉換為一個常量時,使用這些類型訪問。
如將主鍵置於where列表中,MySQL就能將該查詢轉換為一個常量
mysql> explain select * from ( select * from t1 where id=1)b1; +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 2 | DERIVED | t1 | const | PRIMARY | PRIMARY | 4 | | 1 | | +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ 2 rows in set (0.00 sec)
注:system是const類型的特例,當查詢的表只有一行的情況下,使用system
【示例七】NULL:MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引, 例如從一個索引列里選取最小值可以通過單獨索引查找完成。
mysql> explain select * from t1 where id = (select min(id) from t2); +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ | 1 | PRIMARY | t1 | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ 2 rows in set (0.00 sec)
1.5.8 possible_keys
指出MySQL能使用哪個索引在表中找到記錄,查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢使用
1.5.9 key
顯示MySQL在查詢中實際使用的索引,若沒有使用索引,顯示為NULL
【示例】
mysql> explain select id,age from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
1.5.10 key_len
表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度(key_len顯示的值為索引字段的最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的)。
1.5.11 ref
表示上述表的連接匹配條件,即哪些列或常量被用於查找索引列上的值。
1.5.12 rows
表示MySQL根據表統計信息及索引選用情況,估算的找到所需的記錄所需要讀取的行數。
【示例】
mysql> explain select * from t1 , t2 where t1.id=t2.id and t2.name='atlas'; +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | 1 | SIMPLE | t2 | ref | PRIMARY,name | name | 63 | const | 1 | Using where | | 1 | SIMPLE | t1 | eq_ref | PRIMARY | PRIMARY | 4 | test.t2.id | 1 | | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ 2 rows in set (0.00 sec)
1.5.13 Extra
包含不適合在其他列中顯示但十分重要的額外信息
【示例一】Using index
該值表示相應的select操作中使用了覆蓋索引(Covering Index)
mysql> explain select id from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
覆蓋索引(Covering Index)
MySQL可以利用索引返回select列表中的字段,而不必根據索引再次讀取數據文件
包含所有滿足查詢需要的數據的索引稱為覆蓋索引(Covering Index)
注意:如果要使用覆蓋索引,一定要注意select列表中只取出需要的列,不可select *,因為如果將所有字段一起做索引會導致索引文件過大,查詢性能下降
【示例二】Using where
表示mysql服務器將在存儲引擎檢索行后再進行過濾。許多where條件里涉及索引中的列,當(並且如果)它讀取索引時,就能被存儲引擎檢驗,因此不是所有帶where字句的查詢都會顯示"Using where"。
有時"Using where"的出現就是一個暗示:查詢可受益與不同的索引。
mysql> explain select id,name from t1 where id<4; +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ | 1 | SIMPLE | t1 | index | PRIMARY | name | 63 | NULL | 4 | Using where; Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ 1 row in set (0.00 sec)
【示例三】Using temporary
表示MySQL需要使用臨時表來存儲結果集,常見於排序和分組查詢
這個值表示使用了內部臨時(基於內存的)表。一個查詢可能用到多個臨時表。有很多原因都會導致MySQL在執行查詢期間創建臨時表。兩個常見的原因是在來自不同表的上使用了DISTINCT,或者使用了不同的ORDER BY和GROUP BY列。可以強制指定一個臨時表使用基於磁盤的MyISAM存儲引擎。這樣做的原因主要有兩個:
1)內部臨時表占用的空間超過min(tmp_table_size,max_heap_table_size)系統變量的限制
2)使用了TEXT/BLOB 列
mysql> explain select id from t1 where id in (1,2) group by age,name; +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where; Using temporary; Using filesort | +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ 1 row in set (0.00 sec)
【示例四】Using filesort
MySQL中無法利用索引完成的排序操作稱為“文件排序”
mysql> explain select id,age from t1 order by name; +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | Using filesort | +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 1 row in set (0.00 sec)
mysql> explain select id,age from t1 order by age; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec)
【示例五】Using join buffer
該值強調了在獲取連接條件時沒有使用索引,並且需要連接緩沖區來存儲中間結果。
如果出現了這個值,那應該注意,根據查詢的具體情況可能需要添加索引來改進能。
mysql> explain select t1.name from t1 inner join t2 on t1.name=t2.name; +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ | 1 | SIMPLE | t1 | index | name | name | 63 | NULL | 4 | Using index | | 1 | SIMPLE | t2 | ref | name | name | 63 | test.t1.name | 2 | Using where; Using index | +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ 2 rows in set (0.00 sec)
刪除t1索引
mysql> alter table t1 drop key name; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0
刪除t2索引
mysql> alter table t2 drop key name; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0
經常查找
mysql> explain select t1.name from t1 inner join t2 on t1.name=t2.name; +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | | | 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 4 | Using where; Using join buffer | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ 2 rows in set (0.00 sec)
【示例六】Impossible where
這個值強調了where語句會導致沒有符合條件的行。
mysql> EXPLAIN SELECT * FROM t1 WHERE 1=2; +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE | +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ 1 row in set (0.00 sec)
【示例七】Select tables optimized away
這個值意味着僅通過使用索引,優化器可能僅從聚合函數結果中返回一行.
mysql> explain select max(id) from t1; +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ 1 row in set (0.00 sec)
【示例八】Index merges
當MySQL 決定要在一個給定的表上使用超過一個索引的時候,就會出現以下格式中的一個,詳細說明使用的索引以及合並的類型。
Using sort_union(...) Using union(...) Using intersect(...)
1.5.14 小結
EXPLAIN不會告訴你關於觸發器、存儲過程的信息或用戶自定義函數對查詢的影響情況。
EXPLAIN不考慮各種Cache。
EXPLAIN不能顯示MySQL在執行查詢時所作的優化工作。
部分統計信息是估算的,並非精確值。
EXPALIN只能解釋SELECT操作,其他操作要重寫為SELECT后查看執行計划。
1.6 mysql不走索引的原因
1.6.1 一些常見的原因
1) 沒有查詢條件,或者查詢條件沒有建立索引
2) 在查詢條件上沒有使用引導列
3) 查詢的數量是大表的大部分,應該是30%以上。
4) 索引本身失效
5) 查詢條件使用函數在索引列上,或者對索引列進行運算,運算包括(+,-,*,/,! 等)
錯誤的例子:select * from test where id-1=9; 正確的例子:select * from test where id=10;
6) 對小表查詢
7) 提示不使用索引
8) 統計數據不真實
9) CBO計算走索引花費過大的情況。其實也包含了上面的情況,這里指的是表占有的block要比索引小。
10)隱式轉換導致索引失效.這一點應當引起重視.也是開發中經常會犯的錯誤.
由於表的字段tel_num定義為varchar2(20),但在查詢時把該字段作為number類型以where條件傳給數據庫,這樣會導致索引失效.
錯誤的例子:select * from test where tel_nume=13333333333;
正確的例子:select * from test where tel_nume='13333333333';
11) 注意使用的特殊符號
1,<> ,!=
2,單獨的>,<,(有時會用到,有時不會)
12)like "%_" 百分號在前.
select * from t1 where name like 'linux培訓%';
13) not in ,not exist.
14) in 盡量改成 union 。
15)當變量采用的是times變量,而表的字段采用的是date變量時.或相反情況。
16)B-tree索引is null不會走,is not null會走,位圖索引 is null,is not null 都會走 。
17)聯合索引 is not null 只要在建立的索引列(不分先后)都會走,
in null時 必須要和建立索引第一列一起使用,當建立索引第一位置條件是is null 時,其他建立索引的列可以是is null(但必須在所有列 都滿足is null的時候),或者=一個值;
當建立索引的第一位置是=一個值時,其他索引列可以是任何情況(包括is null =一個值),以上兩種情況索引都會走。其他情況不會走。
1.6.2 需要注意的一些
1) MyISAM 存儲引擎索引鍵長度總和不能超過1000 字節; 2) BLOB 和TEXT 類型的列只能創建前綴索引; 3) MySQL 目前不支持函數索引; 4) 使用不等於(!= 或者<>)的時候MySQL 無法使用索引; 5) 過濾字段使用了函數運算后(如abs(column)),MySQL 無法使用索引; 6) Join 語句中Join 條件字段類型不一致的時候MySQL 無法使用索引; 7) 使用LIKE 操作的時候如果條件以通配符開始( '%abc...')MySQL 無法使用索引; 8) 使用非等值查詢的時候MySQL 無法使用Hash 索引; 9) 在我們使用索引的時候,需要注意上面的這些限制,尤其是要注意無法使用索引的情況,因為這很容易讓我們因為疏忽而造成極大的性能隱患。
1.7 數據庫索引的設計原則
為了使索引的使用效率更高,在創建索引時,必須考慮在哪些字段上創建索引和創建什么類型的索引。
1.7.1 那么索引設計原則又是怎樣的
1.選擇唯一性索引
唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄。
例如,學生表中學號是具有唯一性的字段。為該字段建立唯一性索引可以很快的確定某個學生的信息。如果使用姓名的話,可能存在同名現象,從而降低查詢速度。
2.為經常需要排序、分組和聯合操作的字段建立索引
經常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作會浪費很多時間。
如果為其建立索引,可以有效地避免排序操作。
3.為常作為查詢條件的字段建立索引
如果某個字段經常用來做查詢條件,那么該字段的查詢速度會影響整個表的查詢速度。因此,
為這樣的字段建立索引,可以提高整個表的查詢速度。
4.限制索引的數目
索引的數目不是越多越好。每個索引都需要占用磁盤空間,索引越多,需要的磁盤空間就越大。修改表時,對索引的重構和更新很麻煩。越多的索引,會使更新表變得很浪費時間。
5.盡量使用數據量少的索引
如果索引的值很長,那么查詢的速度會受到影響。例如,對一個CHAR(100)類型的字段進行全文檢索需要的時間肯定要比對CHAR(10)類型的字段需要的時間要多。
6.盡量使用前綴來索引
如果索引字段的值很長,最好使用值的前綴來索引。例如,TEXT和BLOG類型的字段,進行全文檢索會很浪費時間。如果只檢索字段的前面的若干個字符,這樣可以提高檢索速度。
7.刪除不再使用或者很少使用的索引
表中的數據被大量更新,或者數據的使用方式被改變后,原有的一些索引可能不再需要。數據庫管理員應當定期找出這些索引,將它們刪除,從而減少索引對更新操作的影響。
8.小表不應建立索引
包含大量的列並且不需要搜索非空值的時候可以考慮不建索引
1.8 參考文獻
https://baike.baidu.com/item/數據庫索引/8751686?fr=aladdin https://www.cnblogs.com/oldhorse/archive/2009/11/16/1604009.html http://blog.csdn.net/manesking/archive/2007/02/09/1505979.aspx http://blog.csdn.net/woshiqjs/article/details/24135495
