聚簇索引:索引的葉節點指向數據
非聚簇索引:索引的葉節點指向數據的引用mysql
索引類型 | 優 | 劣 |
---|---|---|
聚簇索引 | 查詢數據少時,無須回行 | 不規則插入數據,頻繁的頁分裂 |
myisam使用非聚簇索引,innodb使用聚簇索引sql
對於innodb引擎:緩存
primary key() 要求關鍵字不能重複,也不能爲null,同時增長主鍵約束
主鍵索引定義時,不能命名數據結構
unique index() 要求關鍵字不能重複,同時增長惟一約束post
index() 對關鍵字沒有要求性能
fulltext key() 關鍵字的來源不是全部字段的數據,而是字段中提取的特別關鍵字測試
關鍵字:能夠是某個字段或多個字段,多個字段稱爲複合索引優化
建表: creat table student( stu_id int unsigned not null auto_increment, name varchar(32) not null default '', phone char(11) not null default '', stu_code varchar(32) not null default '', stu_desc text, primary key ('stu_id'), //主鍵索引 unique index 'stu_code' ('stu_code'), //惟一索引 index 'name_phone' ('name','phone'), //普通索引,複合索引 fulltext index 'stu_desc' ('stu_desc'), //全文索引 ) engine=myisam charset=utf8; 更新: alert table student add primary key ('stu_id'), //主鍵索引 add unique index 'stu_code' ('stu_code'), //惟一索引 add index 'name_phone' ('name','phone'), //普通索引,複合索引 add fulltext index 'stu_desc' ('stu_desc'); //全文索引 刪除: alert table sutdent drop primary key, drop index 'stu_code', drop index 'name_phone', drop index 'stu_desc';
保證索引包含的字段獨立在查詢語句中,不能是在表達式中url
like:匹配模式左邊不能以通配符開始,才能使用索引
注意:前綴索引在排序 order by 和分組 group by 操做的時候沒法使用。code
創建聯合索引,要同時考慮列查詢的頻率和列的區分度。
語句 | 索引是否發揮做用 |
---|---|
where a=3 | 是,只使用了a |
where a=3 and b=5 | 是,使用了a,b |
where a=3 and b=5 and c=4 | 是,使用了a,b,c |
where b=3 or where c=4 | 否 |
where a=3 and c=4 | 是,僅使用了a |
where a=3 and b>10 and c=7 | 是,使用了a,b |
where a=3 and b like '%xx%' and c=7 | 使用了a,b |
or的兩邊都有存在可用的索引,該語句才能用索引。
即便知足了上述原則,mysql仍是可能會棄用索引,由於有些查詢即便使用索引,也會出現大量的隨機io,相對於從數據記錄中的順序io開銷更大。
測試庫下載地址:https://downloads.mysql.com/d...
對索引中全部列都指定具體值,便是對索引中的全部列都有等值匹配的條件。
例如,租賃表 rental 中經過指定出租日期 rental_date + 庫存編號 inventory_id + 客戶編號 customer_id 的組合條件進行查詢,熊執行計劃的 key he extra 兩字段的值看到優化器選擇了複合索引 idx_rental_date:
MySQL [sakila]> explain select * from rental where rental_date='2005-05-25 17:22:10' and inventory_id=373 and customer_id=343 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: rental partitions: NULL type: const possible_keys: rental_date,idx_fk_inventory_id,idx_fk_customer_id key: rental_date key_len: 10 ref: const,const,const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
explain 輸出結果中字段 type 的值爲 const,表示是常量;字段 key 的值爲 rental_date, 表示優化器選擇索引 rental_date 進行掃描。
對索引的值可以進行範圍查找。
例如,檢索租賃表 rental 中客戶編號 customer_id 在指定範圍內的記錄:
MySQL [sakila]> explain select * from rental where customer_id >= 373 and customer_id < 400 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: rental partitions: NULL type: range possible_keys: idx_fk_customer_id key: idx_fk_customer_id key_len: 2 ref: NULL rows: 718 filtered: 100.00 Extra: Using index condition 1 row in set, 1 warning (0.05 sec)
類型 type 爲 range 說明優化器選擇範圍查詢,索引 key 爲 idx_fk_customer_id 說明優化器選擇索引 idx_fk_customer_id 來加速訪問,注意到這個列子中 extra 列爲 using index codition ,表示 mysql 使用了 ICP(using index condition) 來進一步優化查詢。
僅僅使用索引中的最左邊列進行查詢,好比在 col1 + col2 + col3 字段上的聯合索引可以被包含 col一、(col1 + col2)、(col1 + col2 + col3)的等值查詢利用到,但是不可以被 col二、(col二、col3)的等值查詢利用到。
最左匹配原則能夠算是 MySQL 中 B-Tree 索引使用的首要原則。
當查詢的列都在索引的字段中時,查詢的效率更高,因此應該儘可能避免使用 select *,須要哪些字段,就只查哪些字段。
僅僅使用索引中的第一列,而且只包含索引第一列的開頭一部分進行查找。
例如,如今須要查詢出標題 title 是以 AFRICAN 開頭的電影信息,從執行計劃可以清楚看到,idx_title_desc_part 索引被利用上了:
MySQL [sakila]> create index idx_title_desc_part on film_text(title (10), description(20)); Query OK, 0 rows affected (0.07 sec) Records: 0 Duplicates: 0 Warnings: 0 MySQL [sakila]> explain select title from film_text where title like 'AFRICAN%'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: film_text partitions: NULL type: range possible_keys: idx_title_desc_part,idx_title_description key: idx_title_desc_part key_len: 32 ref: NULL rows: 1 filtered: 100.00 Extra: Using where 1 row in set, 1 warning (0.00 sec)
extra 值爲 using where 表示優化器須要經過索引回表查詢數據。
例如,須要查詢出租日期 rental_date 爲指定日期且客戶編號 customer_id 爲指定範圍的庫存:
MySQL [sakila]> MySQL [sakila]> explain select inventory_id from rental where rental_date='2006-02-14 15:16:03' and customer_id >= 300 and customer_id <=400\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: rental partitions: NULL type: ref possible_keys: rental_date,idx_fk_customer_id key: rental_date key_len: 5 ref: const rows: 182 filtered: 16.85 Extra: Using where; Using index 1 row in set, 1 warning (0.00 sec)
例如,查詢支付表 payment 的租賃編號 rental_id 字段爲空的記錄就用到了索引:
MySQL [sakila]> explain select * from payment where rental_id is null \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: payment partitions: NULL type: ref possible_keys: fk_payment_rental key: fk_payment_rental key_len: 5 ref: const rows: 5 filtered: 100.00 Extra: Using index condition 1 row in set, 1 warning (0.00 sec)
有些時候雖然有索引,可是並不被優化器選擇使用,下面舉例幾個不能使用索引的場景。
MySQL [sakila]> explain select * from actor where last_name like "%NI%"\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 200 filtered: 11.11 Extra: Using where 1 row in set, 1 warning (0.00 sec)
由於 B-Tree 索引的結構,因此以%開頭的插敘很天然就無法利用索引了。通常推薦使用全文索引(Fulltext)來解決相似的全文檢索的問題。或者考慮利用 innodb 的表都是聚簇表的特色,採起一種輕量級別的解決方式:通常狀況下,索引都會比表小,掃描索引要比掃描表更快,而Innodb 表上二級索引 idx_last_name 實際上存儲字段 last_name 還有主鍵 actot_id,那麼理想的訪問應該是首先掃描二級索引 idx_last_name 得到知足條件的last_name like '%NI%' 的主鍵 actor_id 列表,以後根據主鍵回表去檢索記錄,這樣訪問避開了全表掃描演員表 actor 產生的大量 IO 請求。
ySQL [sakila]> explain select * from (select actor_id from actor where last_name like '%NI%') a , actor b where a.actor_id = b.actor_id \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: index possible_keys: PRIMARY key: idx_actor_last_name key_len: 137 ref: NULL rows: 200 filtered: 11.11 Extra: Using where; Using index *************************** 2. row *************************** id: 1 select_type: SIMPLE table: b partitions: NULL type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 2 ref: sakila.actor.actor_id rows: 1 filtered: 100.00 Extra: NULL
從執行計劃中可以看出,extra 字段 using wehre;using index。理論上比全表掃描更快一下。
當列的類型是字符串,那麼必定記得在 where 條件中把字符常量值用引號引發來
,不然即使這個列上有索引,mysql 也不會用到,由於 MySQL 默認把輸入的常量值進行轉換之後才進行檢索。
例如,演員表 actor 中的姓氏字段 last_name 是字符型的,可是 sql 語句中的條件值 1 是一個數值型值,所以即使存在索引 idx_last_name, mysql 也不能正確的用上索引,而是繼續進行全表掃描:
MySQL [sakila]> explain select * from actor where last_name = 1 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ALL possible_keys: idx_actor_last_name key: NULL key_len: NULL ref: NULL rows: 200 filtered: 10.00 Extra: Using where 1 row in set, 3 warnings (0.00 sec) MySQL [sakila]> explain select * from actor where last_name = '1'\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: actor partitions: NULL type: ref possible_keys: idx_actor_last_name key: idx_actor_last_name key_len: 137 ref: const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
若是索引正在工做, Handler_read_key 的值將很高,這個值表明了一個行被索引值讀的次數,很低的值表名增長索引獲得的性能改善不高,由於索引並不常用。
Handler_read_rnd_next 的值高則意味着查詢運行低效,而且應該創建索引補救。這個值的含義是在數據文件中讀下一行的請求數。若是正在進行大量的表掃描,Handler_read_rnd_next 的值較高,則一般說明表索引不正確或寫入的查詢沒有利用索引,具體以下。
MySQL [sakila]> show status like 'Handler_read%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Handler_read_first | 1 | | Handler_read_key | 5 | | Handler_read_last | 0 | | Handler_read_next | 200 | | Handler_read_prev | 0 | | Handler_read_rnd | 0 | | Handler_read_rnd_next | 0 | +-----------------------+-------+
截取不一樣長度,測試區分度
# 這裏假設截取6個字符長度計算區別度,直到區別度達到0.1,就能夠把這個字段的這個長度做爲索引了 mysql> select count(distinct left([varchar]],6))/count(*) from table; #注意:設置前綴索引時指定的長度表示字節數,而對於非二進制類型(CHAR, VARCHAR, TEXT)字段而言的字段長度表示字符數,所 # 以,在設置前綴索引前須要把計算好的字符數轉化爲字節數,經常使用字符集與字節的關係以下: # latin 單字節:1B # GBK 雙字節:2B # UTF8 三字節:3B # UTF8mb4 四字節:4B # myisam 表的索引大小默認爲 1000字節,innodb 表的索引大小默認爲 767 字節,能夠在配置文件中修改 innodb_large_prefix # 項的值增大 innodb 索引的大小,最大 3072 字節。
區別度能達到0.1,就能夠。
這樣的字段,左邊有大量重複字符,好比url字段彙總的http://
概念:若是查詢的列剛好是索引的一部分,那麼查詢只須要在索引文件上進行,不須要回行到磁盤,這種查詢,速度極快,江湖人稱——索引覆蓋
在根據條件查詢數據時,若是查詢條件不能用的索引,能夠先查出數據行的id,再根據id去取數據行。
eg.
//普通查詢 沒有用到索引 select * from post where content like "%新聞%"; //延遲關聯優化後 內層查詢走content索引,取出id,在用join查全部行 select a.* from post as a inner join (select id from post where content like "%新聞%") as b on a.id=b.id;
排序的字段上加入索引,能夠提升速度。
重複索引:在同一列或者相同順序的幾個列創建了多個索引,成爲重複索引,沒有任何意義,刪掉
冗餘索引:兩個或多個索引所覆蓋的列有重疊,好比對於列m,n ,加索引index m(m),indexmn(m,n),稱爲冗餘索引。
在數據表長期的更改過程當中,索引文件和數據文件都會產生空洞,造成碎片。修復表的過程十分耗費資源,能夠用比較長的週期修復表。
//清理方法 alert table xxx engine innodb; //或 optimize table xxx;
Innodb 表要儘可能本身指定主鍵,若是有幾個列都是惟一的,要選擇最常做爲訪問條件的列做爲主鍵,另外,Innodb 表的普通索引都會保存主鍵的鍵值,因此主鍵要儘量選擇較短的數據類型,能夠有效的減小索引的磁盤佔用,提升索引的緩存效果。