MySQL-4- 索引及執行計劃

1. 索引做用

提供了相似於書中目錄的做用,目的是爲了優化查詢

2. 索引的種類(算法)

B樹索引 Hash索引 R樹 Full text GIS

3. B樹 基於不一樣的查找算法分類介紹

 
B-tree B+Tree 在範圍查詢方面提供了更好的性能(> < >= <= like) B*Tree 

4. 在功能上的分類

4.1 輔助索引(S)怎麼構建B樹結構的?

(1). 索引是基於表中,列(索引鍵)的值生成的B樹結構 (2). 首先提取此列全部的值,進行自動排序 (3). 將排好序的值,均勻的分佈到索引樹的葉子節點中(16K) (4). 而後生成此索引鍵值所對應得後端數據頁的指針 (5). 生成枝節點和根節點,根據數據量級和索引鍵長度,生成合適的索引樹高度 id name age gender select * from t1 where id=10; 問題: 基於索引鍵作where查詢,對於id列是順序IO,可是對於其餘列的查詢,多是隨機IO. 

4.2 彙集索引(C)

4.2.1 前提

(1)表中設置了主鍵,主鍵列就會自動被做爲彙集索引. (2)若是沒有主鍵,會選擇惟一鍵做爲彙集索引. (3)彙集索引必須在建表時纔有意義,通常是表的無關列(ID)

4.2.2 輔助索引(S)怎麼構建B樹結構的?

(1) 在建表時,設置了主鍵列(ID) (2) 在未來錄入數據時,就會按照ID列的順序存儲到磁盤上.(咱們又稱之爲彙集索引組織表) (3) 將排好序的整行數據,生成葉子節點.能夠理解爲,磁盤的數據頁就是葉子節點

4.2.3 彙集索引和輔助索引構成區別

彙集索引只能有一個,非空惟一,通常時主鍵 輔助索引,能夠有多個,是配合彙集索引使用的 彙集索引葉子節點,就是磁盤的數據行存儲的數據頁 MySQL是根據彙集索引,組織存儲數據,數據存儲時就是按照彙集索引的順序進行存儲數據 輔助索引,只會提取索引鍵值,進行自動排序生成B樹結構
 
 

5.輔助索引細分

1.普通的單列輔助索引 2.聯合索引 多個列做爲索引條件,生成索引樹,理論上設計的好的,能夠減小大量的回表 查詢 3.惟一索引 索引列的值都是惟一的.

6. 關於索引樹的高度受什麼影響

1. 數據量級, 解決方法:分表,分庫,分佈式 2. 索引列值過長 , 解決方法:前綴索引 3. 數據類型: 變長長度字符串,使用了char,解決方案:變長字符串使用varchar enum類型的使用enum ('山東','河北','黑龍江','吉林','遼寧','陝西'......) 1 2 3 

7. 索引的基本管理

7.1 索引創建前

db01 [world]>desc city; +-------------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+----------+------+-----+---------+----------------+ | ID | int(11) | NO | PRI | NULL | auto_increment | | Name | char(35) | NO | | | | | CountryCode | char(3) | NO | MUL | | | | District | char(20) | NO | | | | | Population | int(11) | NO | | 0 | | +-------------+----------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) Field :列名字 key :有沒有索引,索引類型 PRI: 主鍵索引 UNI: 惟一索引 MUL: 輔助索引(單列,聯和,前綴) 

7.1 單列普通輔助索引

7.1.1 建立索引

db01 [world]>alter table city add index idx_name(name); 表 索引名(列名) db01 [world]>create index idx_name1 on city(name); db01 [world]>show index from city;  注意: 以上操做不表明生產操做,咱們不建議在一個列上建多個索引同一個表中,索引名不能同名。
### 7.1.2 刪除索引: db01 [world]>alter table city drop index idx_name1; 表名 索引名

7.2 覆蓋索引(聯合索引)

Master [world]>alter table city add index idx_co_po(countrycode,population); 

7.3 前綴索引

db01 [world]>alter table city add index idx_di(district(5)); 注意:數字列不能用做前綴索引。 

7.4 惟一索引

db01 [world]>alter table city add unique index idx_uni1(name); ERROR 1062 (23000): Duplicate entry 'San Jose' for key 'idx_uni1' 

統計city表中,以省的名字爲分組,統計組的個數javascript

select district,count(id) from city group by district; 需求: 找到world下,city表中 name列有重複值的行,最後刪掉重複的行 db01 [world]>select name,count(id) as cid from city group by name having cid>1 order by cid desc; db01 [world]>select * from city where name='suzhou'; 

===============================================php

8. 執行計劃獲取及分析

8.0 介紹

(1)獲取到的是優化器選擇完成的,他認爲代價最小的執行計劃. 做用: 語句執行前,先看執行計劃信息,能夠有效的防止性能較差的語句帶來的性能問題. 若是業務中出現了慢語句,咱們也須要藉助此命令進行語句的評估,分析優化方案。 (2) select 獲取數據的方法 1. 全表掃描(應當儘可能避免,由於性能低) 2. 索引掃描 3. 獲取不到數據

8.1 執行計劃獲取

獲取優化器選擇後的執行計劃css

 
 
image

8.2 執行計劃分析

8.2.0 重點關注的信息

table: city  ---->查詢操做的表 ** possible_keys: CountryCode,idx_co_po  ---->可能會走的索引 ** key: CountryCode             ---->真正走的索引 *** type: ref                 ---->索引類型 ***** Extra: Using index condition ---->額外信息 ***** 

8.2.1 type詳解

從左到右性能依次變好. ALL : 全表掃描,不走索引 例子: 1. 查詢條件列,沒有索引 SELECT * FROM t_100w WHERE k2='780P'; 2. 查詢條件出現如下語句(輔助索引列) USE world DESC city; DESC SELECT * FROM city WHERE countrycode <> 'CHN'; DESC SELECT * FROM city WHERE countrycode NOT IN ('CHN','USA'); DESC SELECT * FROM city WHERE countrycode LIKE '%CH%'; 注意:對於彙集索引列,使用以上語句,依然會走索引 DESC SELECT * FROM city WHERE id <> 10; INDEX : 全索引掃描 1. 查詢須要獲取整個索引樹種的值時: DESC SELECT countrycode FROM city; 2. 聯合索引中,任何一個非最左列做爲查詢條件時: idx_a_b_c(a,b,c) ---> a ab abc SELECT * FROM t1 WHERE b SELECT * FROM t1 WHERE c RANGE : 索引範圍掃描 輔助索引> < >= <= LIKE IN OR 主鍵 <> NOT IN 例子: 1. DESC SELECT * FROM city WHERE id<5; 2. DESC SELECT * FROM city WHERE countrycode LIKE 'CH%'; 3. DESC SELECT * FROM city WHERE countrycode IN ('CHN','USA'); 注意: 1和2例子中,能夠享受到B+樹的優點,可是3例子中是不能享受的. 因此,咱們能夠將3號列子改寫: DESC SELECT * FROM city WHERE countrycode='CHN' UNION ALL SELECT * FROM city WHERE countrycode='USA'; ref: 非惟一性索引,等值查詢 DESC SELECT * FROM city WHERE countrycode='CHN'; eq_ref: 在多表鏈接時,鏈接條件使用了惟一索引(uk pK) DESC SELECT b.name,a.name FROM city AS a JOIN country AS b ON a.countrycode=b.code WHERE a.population <100; DESC country system,const : 惟一索引的等值查詢 DESC SELECT * FROM city WHERE id=10; 

8.2.2 其餘字段解釋

extra: filesort ,文件排序. SHOW INDEX FROM city; ALTER TABLE city ADD INDEX CountryCode(CountryCode); ALTER TABLE city DROP INDEX idx_c_p; DESC SELECT * FROM city WHERE countrycode='CHN' ORDER BY population ALTER TABLE city ADD INDEX idx_(population); DESC SELECT * FROM city WHERE countrycode='CHN' ORDER BY population ALTER TABLE city ADD INDEX idx_c_p(countrycode,population); ALTER TABLE city DROP INDEX idx_; ALTER TABLE city DROP INDEX CountryCode; DESC SELECT * FROM city WHERE countrycode='CHN' ORDER BY population 結論: 1.當咱們看到執行計劃extra位置出現filesort,說明由文件排序出現 2.觀察須要排序(ORDER BY,GROUP BY ,DISTINCT )的條件,有沒有索引 3. 根據子句的執行順序,去建立聯合索引 索引優化效果測試: 優化前: [root@db01 ~]# mysqlslap --defaults-file=/etc/my.cnf \ > --concurrency=100 --iterations=1 --create-schema='oldboy' \ > --query="select * from oldboy.t_100w where k2='780P'" engine=innodb \ > --number-of-queries=2000 -uroot -p123 -verbose mysqlslap: [Warning] Using a password on the command line interface can be insecure. Benchmark Running for engine rbose Average number of seconds to run all queries: 701.743 seconds Minimum number of seconds to run all queries: 701.743 seconds Maximum number of seconds to run all queries: 701.743 seconds Number of clients running queries: 100 Average number of queries per client: 20 優化後: [root@db01 ~]# mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='oldboy' --query="select * from oldboy.t_100w where k2='780P'" engine=innodb --number-of-queries=2000 -uroot -p123 -verbose mysqlslap: [Warning] Using a password on the command line interface can be insecure. Benchmark Running for engine rbose Average number of seconds to run all queries: 0.190 seconds Minimum number of seconds to run all queries: 0.190 seconds Maximum number of seconds to run all queries: 0.190 seconds Number of clients running queries: 100 Average number of queries per client: 20 聯合索引: 1. SELECT * FROM t1 WHERE a= b= 咱們創建聯合索引時: ALTER TABLE t1 ADD INDEX idx_a_b(a,b); ALTER TABLE t1 ADD INDEX idx_b_a(b,a); 以上的查詢不考慮索引的順序,優化器會自動調整where的條件順序 注意: 索引,咱們在這種狀況下建索引時,須要考慮哪一個列的惟一值更多,哪一個放在索引左邊. 2. 若是出現where 條件中出現不等值查詢條件 DESC SELECT * FROM t_100w WHERE num <1000 AND k2='DEEF'; 咱們建索引時: ALTER TABLE t_100w ADD INDEX idx_2_n(k2,num); 語句書寫時 DESC SELECT * FROM t_100w WHERE k2='DEEF' AND num <1000 ; 3. 若是查詢中出現多子句 咱們要按照子句的執行順序進行創建索引. 

8.2.3 explain(desc)使用場景(面試題)

題目意思: 咱們公司業務慢,請你從數據庫的角度分析緣由 1.mysql出現性能問題,我總結有兩種狀況: (1)應急性的慢:忽然夯住 應急狀況:數據庫hang(卡了,資源耗盡) 處理過程: 1.show processlist; 獲取到致使數據庫hang的語句 2. explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況 3. 建索引,改語句 (2)一段時間慢(持續性的): (1)記錄慢日誌slowlog,分析slowlog (2)explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況 (3)建索引,改語句 

9. 索引應用規範

業務 1.產品的功能 2.用戶的行爲 "熱"查詢語句 --->較慢--->slowlog "熱"數據 

9.1 創建索引的原則(DBA運維規範)

9.1.0 說明

爲了使索引的使用效率更高,在建立索引時,必須考慮在哪些字段上建立索引和建立什麼類型的索引。那麼索引設計原則又是怎樣的?

9.1.1 (必須的) 建表時必定要有主鍵,通常是個無關列

略.回顧一下,彙集索引結構.java

9.1.2 選擇惟一性索引

惟一性索引的值是惟一的,能夠更快速的經過該索引來肯定某條記錄。 例如,學生表中學號是具備惟一性的字段。爲該字段創建惟一性索引能夠很快的肯定某個學生的信息。 若是使用姓名的話,可能存在同名現象,從而下降查詢速度。 優化方案: (1) 若是非得使用重複值較多的列做爲查詢條件(例如:男女),能夠將表邏輯拆分 (2) 能夠將此列和其餘的查詢類,作聯和索引 select count(*) from world.city; select count(distinct countrycode) from world.city; select count(distinct countrycode,population ) from world.city; 

9.1.3(必須的) 爲常常須要where 、ORDER BY、GROUP BY,join on等操做的字段,

排序操做會浪費不少時間。 where A B C ----》 A B C in where A group by B order by C A,B,C 若是爲其創建索引,優化查詢 注:若是常常做爲條件的列,重複值特別多,能夠創建聯合索引。 

9.1.4 儘可能使用前綴來索引

若是索引字段的值很長,最好使用值的前綴來索引。

9.1.5 限制索引的數目

索引的數目不是越多越好。 可能會產生的問題: (1) 每一個索引都須要佔用磁盤空間,索引越多,須要的磁盤空間就越大。 (2) 修改表時,對索引的重構和更新很麻煩。越多的索引,會使更新表變得很浪費時間。 (3) 優化器的負擔會很重,有可能會影響到優化器的選擇. percona-toolkit中有個工具,專門分析索引是否有用

9.1.6 刪除再也不使用或者不多使用的索引(percona toolkit)

pt-duplicate-key-checker 表中的數據被大量更新,或者數據的使用方式被改變後,原有的一些索引可能再也不須要。數據庫管理 員應當按期找出這些索引,將它們刪除,從而減小索引對更新操做的影響。

9.1.7 大表加索引,要在業務不繁忙期間操做

9.1.8 儘可能少在常常更新值的列上建索引

9.1.9 建索引原則

(1) 必需要有主鍵,若是沒有能夠作爲主鍵條件的列,建立無關列 (2) 常常作爲where條件列 order by group by join on, distinct 的條件(業務:產品功能+用戶行爲) (3) 最好使用惟一值多的列做爲索引,若是索引列重複值較多,能夠考慮使用聯合索引 (4) 列值長度較長的索引列,咱們建議使用前綴索引. (5) 下降索引條目,一方面不要建立沒用索引,不常使用的索引清理,percona toolkit(xxxxx) (6) 索引維護要避開業務繁忙期 

9.2 不走索引的狀況(開發規範)

9.2.1 沒有查詢條件,或者查詢條件沒有創建索引

select * from tab; 全表掃描。 select * from tab where 1=1; 在業務數據庫中,特別是數據量比較大的表。 是沒有全表掃描這種需求。 一、對用戶查看是很是痛苦的。 二、對服務器來說毀滅性的。 (1) select * from tab; SQL改寫成如下語句: select * from tab order by price limit 10 ; 須要在price列上創建索引 (2) select * from tab where name='zhangsan' name列沒有索引 改: 一、換成有索引的列做爲查詢條件 二、將name列創建索引 

9.2.2 查詢結果集是原表中的大部分數據,應該是25%以上。

查詢的結果集,超過了總數行數25%,優化器以爲就沒有必要走索引了。 假如:tab表 id,name id:1-100w ,id列有(輔助)索引 select * from tab where id>500000; 若是業務容許,可使用limit控制。 怎麼改寫 ? 結合業務判斷,有沒有更好的方式。若是沒有更好的改寫方案 儘可能不要在mysql存放這個數據了。放到redis裏面。 

9.2.3 索引自己失效,統計數據不真實

索引有自我維護的能力。 對於表內容變化比較頻繁的狀況下,有可能會出現索引失效。 通常是刪除重建 現象: 有一條select語句日常查詢時很快,忽然有一天很慢,會是什麼緣由 select? --->索引失效,,統計數據不真實 DML ? --->鎖衝突

9.2.4 查詢條件使用函數在索引列上,或者對索引列進行運算,運算包括(+,-,*,/,! 等)

例子: 錯誤的例子:select * from test where id-1=9; 正確的例子:select * from test where id=10; 算術運算 函數運算 子查詢 

9.2.5 隱式轉換致使索引失效.這一點應當引發重視.也是開發中常常會犯的錯誤.

這樣會致使索引失效. 錯誤的例子: mysql> alter table tab add index inx_tel(telnum); Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> mysql> desc tab; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | YES | | NULL | | | name | varchar(20) | YES | | NULL | | | telnum | varchar(20) | YES | MUL | NULL | | +--------+-------------+------+-----+---------+-------+ 3 rows in set (0.01 sec) mysql> select * from tab where telnum='1333333'; +------+------+---------+ | id | name | telnum | +------+------+---------+ | 1 | a | 1333333 | +------+------+---------+ 1 row in set (0.00 sec) mysql> select * from tab where telnum=1333333; +------+------+---------+ | id | name | telnum | +------+------+---------+ | 1 | a | 1333333 | +------+------+---------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum='1333333'; +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | 1 | SIMPLE | tab | ref | inx_tel | inx_tel | 63 | const | 1 | Using index condition | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum=1333333; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tab | ALL | inx_tel | NULL | NULL | NULL | 2 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum=1555555; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | tab | ALL | inx_tel | NULL | NULL | NULL | 2 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum='1555555'; +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ | 1 | SIMPLE | tab | ref | inx_tel | inx_tel | 63 | const | 1 | Using index condition | +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+ 1 row in set (0.00 sec) mysql> 

9.2.6 <> ,not in 不走索引(輔助索引)

EXPLAIN SELECT * FROM teltab WHERE telnum <> '110'; EXPLAIN SELECT * FROM teltab WHERE telnum NOT IN ('110','119'); mysql> select * from tab where telnum <> '1555555'; +------+------+---------+ | id | name | telnum | +------+------+---------+ | 1 | a | 1333333 | +------+------+---------+ 1 row in set (0.00 sec) mysql> explain select * from tab where telnum <> '1555555'; 單獨的>,<,in 有可能走,也有可能不走,和結果集有關,儘可能結合業務添加limit or或in 儘可能改爲union EXPLAIN SELECT * FROM teltab WHERE telnum IN ('110','119'); 改寫成: EXPLAIN SELECT * FROM teltab WHERE telnum='110' UNION ALL SELECT * FROM teltab WHERE telnum='119' 

9.2.7 like "%_" 百分號在最前面不走

EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%' 走range索引掃描 EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110' 不走索引 %linux%類的搜索需求,可使用elasticsearch+mongodb 專門作搜索服務的數據庫產品
相關文章
相關標籤/搜索