第四章 索引及執(zhí)行計劃管理
1. 索引的作用
類似于一本書的目錄,起到優(yōu)化查詢的功能
2. 索引類型(筆試)
BTREE索引 *****
RTREE索引
HASH索引
全文索引
3. BTREE的細(xì)分離(算法)
B-TREE渣淳,B+TREE,B*TREE 原理圖 提取碼: p1h5
B-TREE
B+TREE
B*TREE
4. Btree索引的功能分類
聚集索引(集群索引) *****
輔助索引(二級索引)
5.B樹是如何構(gòu)建的
輔助索引
alter table t1 add index idx_name(name);
1.將name列的所有值取出來猖任,進行自動排序
2.將排完序的值均勻的落到16KB葉子節(jié)點數(shù)據(jù)頁中尺上,并將索引鍵值所對應(yīng)的數(shù)據(jù)行的幾句索引列值
3.向上生成枝節(jié)點和根節(jié)點
聚集索引(IOT)
1.默認(rèn)是按照主鍵生成聚集索引金顿,沒有主鍵,存儲引擎會使用唯一鍵贰您。
如果都沒有坏平,會自動生成隱藏的聚集索引
2.數(shù)據(jù)在存儲時,就會按照聚集索引打的順序存儲到磁盤的數(shù)據(jù)頁
3.由于本身書籍就是有序的锦亦,所以聚集索引構(gòu)建時舶替,不需要進行排序
4.聚集索引直接將磁盤的數(shù)據(jù)頁,作為葉子節(jié)點
5.枝節(jié)點和根節(jié)點只會調(diào)取下層節(jié)點主鍵的最小值
6. 輔助索引和聚集索引區(qū)別
(1) 輔助索引:
葉子節(jié)點只保存主鍵值+索引鍵值的有序存儲
對索引鍵值會知道排序
需要手工創(chuàng)建
輔助索引可以有多個
任何列都可以創(chuàng)建
(2)
只能在主鍵列生成杠园,唯一且非空
數(shù)據(jù)存儲時顾瞪,就是按照聚集索引順序進行有序存儲
葉子節(jié)點保存的是整個有序的數(shù)據(jù)行
葉子節(jié)點不需要單獨生成
7. 輔助索引細(xì)分
7.1 單列輔助索引
select * from t1 where name=''
7.2 聯(lián)合索引
select * from t1 where a and b and c
7.3唯一索引
8.索引樹的高度(越低越好)
1. 表的數(shù)據(jù)量級大
- 分區(qū)表
- 分庫分表(分布式架構(gòu))
2. 列的長度
- 盡可能選擇列值短的創(chuàng)建索引
- 采用前綴索引
3. 數(shù)據(jù)類型選擇
- varchar 和 char
- enum
9.索引管理
9.1 壓力測試準(zhǔn)備
create database test charset utf8mb4;
use test;
create table t100w (id int,num int,k1 char(2),k2 char(4),dt timestamp);
delimiter //
create procedure rand_data(in num int)
begin
declare str char(62) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
declare str2 char(2);
declare str4 char(4);
declare i int default 0;
while i<num do
set str2=concat(substring(str,1+floor(rand()*61),1),substring(str,1+floor(rand()*61),1));
set str4=concat(substring(str,1+floor(rand()*61),2),substring(str,1+floor(rand()*61),2));
set i=i+1;
insert into t100w values (i,floor(rand()*num),str2,str4,now());
end while;
end;
//
delimiter ;
插入100w條數(shù)據(jù):
call rand_data(1000000);
commit;
MySQL [test]>select count(*) from t100w;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
1 row in set (0.47 sec)
9.2 索引命令操作
- 查詢索引
use school
show tables;
desc student;
Key : PRI(主鍵),UNI(唯一索引)抛蚁,MUL(輔助索引)
show index from student\G
- 創(chuàng)建單列索引
alter table student add index idx_name(sname);
- 創(chuàng)建聯(lián)合索引
alter table student add index idx_sname_sage_ssex(sname,sage,ssex);
idx_a_b_c -----> idx_a idx_a_b idx_a_b_c
- 創(chuàng)建前綴索引
alter table student add index idx(sname(5));
alter table student add telnum char(11) not null;
- 創(chuàng)建唯一索引
mysql[school]>alter table student add telnum char(11) not null ;
mysql[school]>alter table student add unique index idx_tel(telnum);
# 判斷某個列有或沒有重復(fù)值
MySQL [world]>select name,count(id) from city group by name having count(id)>1 order by count(id) desc ;
+---------------+-----------+
| name | count(id) |
+---------------+-----------+
| San José | 4 |
| Córdoba | 3 |
| Springfield | 3 |
...
.....
| Brest | 2 |
| Manchester | 2 |
| Santa Maria | 2 |
| Colombo | 2 |
| San Carlos | 2 |
| Jinzhou | 2 |
+---------------+-----------+
67 rows in set (0.00 sec)
MySQL [world]>select count(*) from city;
+----------+
| count(*) |
+----------+
| 4079 |
+----------+
1 row in set (0.00 sec)
MySQL [world]>select count(distinct name) from city;
+----------------------+
| count(distinct name) |
+----------------------+
| 3998 |
+----------------------+
1 row in set (0.00 sec)
- 刪除索引
mysql[school]>alter table student drop index idx;
mysql[school]>alter table student drop index idx_name;
mysql[school]>alter table student drop index idx_sname_sage_ssex;
9.3沒有索引和添加索引的差別
mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='test' --query="select * from test.t100w where k2='VWtu'" engine=innodb --number-of-queries=2000 -uroot -p123456 -verbose
優(yōu)化前
use test;
show tables;
desc t100w;
alter table t100w add index idx_k2(k2); #優(yōu)化
優(yōu)化后
10. explain (desc) ☆☆☆☆☆
explain select * from test.t100w where k2='VWtu';
or
desc select * from test.t100w where k2='VWtu';
作用 : 抓取優(yōu)化器優(yōu)化過的執(zhí)行計劃
10.1 執(zhí)行計劃的分析
MySQL [test]>explain select * from test.t100w where k2='VWtu';
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| 1 | SIMPLE | t100w | NULL | ref | idx_k2 | idx_k2 | 17 | const | 1120 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
重點3滦选!瞧甩!
table:以上SQL語句設(shè)計到的表☆☆☆
type:查詢的類型(全表掃描(ALL)钉跷,索引掃描,查不到數(shù)據(jù)(NULL))☆☆☆☆☆
possible_keys:可能會用到的索引☆☆☆
key:使用到的索引☆☆☆☆
key_len:索引的覆蓋長度☆☆☆☆☆
Extra:額外的信息☆☆☆☆
10.2 type 詳細(xì)說明☆☆☆☆☆
10.2.1 ALL:全表掃描,不走索引
(1) 查詢條件肚逸,沒建索引
>use test;
>MySQL [test]>alter table t100w drop index idx_k2;
>MySQL [test]>explain select * from test.t100w where k2='VWtu';
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | t100w | NULL | ALL | NULL | NULL | NULL | NULL | 937395 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
MySQL [test]>desc t100w;
+-------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------+------+-----+-------------------+-----------------------------+
| id | int(11) | YES | | NULL | |
| num | int(11) | YES | | NULL | |
| k1 | char(2) | YES | | NULL | |
| k2 | char(4) | YES | | NULL | |
| dt | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------+
5 rows in set (0.00 sec)
(2) 有索引不走
mysql [test]>alter table t100w add index idx_k2(k2);
Query OK, 0 rows affected (3.57 sec)
Records: 0 Duplicates: 0 Warnings: 0
desc select * from t100w where k2 != 'asdf';
desc select * from t100w where k2 like '%aa%';
desc select * from t100w where k2 not in ('asda','asas');
desc select * from t100w;
注意: !=和not in 如果是主鍵列,是走range
mysql[test]>desc select * from world.city where id != 10;
mysql[test]>desc select * from world.city where id not in (10,20);
10.2.2 index 全索引掃描
MySQL [test]>desc select k2 from t100w;
=====從range開始爷辙,我們才認(rèn)為索引是有價值的======
10.2.3 range 索引范圍查詢
所有索引:
>, <, >=, <=, like, between and
mysql[world]>desc select * from city where id<10;
mysql[world]>desc select * from city where countrycode like 'CH%
in () or
oldguo[world]>desc select * from city where countrycode in ('CHN','USA');
聚集索引:
!= not in
mysql[test]>desc select * from world.city where id != 10;
mysql[test]>desc select * from world.city where id not in (10,20);
========================================
說明:
B+tree 索引能額外優(yōu)化到:> ,<, >=, <= ,like , between and
in 和 or 享受不到b+tree額外的優(yōu)化效果的,所以我一般情況會將in , or 進行改寫
mysql[world]>desc select * from city where countrycode = 'CHN' union all select * from city where countrycode = 'USA';
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| 1 | PRIMARY | city | NULL | ref | CountryCode | CountryCode | 3 | const | 363 | 100.00 | NULL |
| 2 | UNION | city | NULL | ref | CountryCode | CountryCode | 3 | const | 274 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
10.2.4 ref 輔助索引等值查詢
MySQL [world]>desc select * from city where countrycode='CHN';
10.2.5 eq_ref 多表連接查詢中, 非驅(qū)動表的on的調(diào)劑列是主鍵唯一鍵
MySQL [world]>desc select a.name,b.name from city as a join country as b on a.countrycode=b.code where a.population<100;
+----+-------------+-------+------------+--------+---------------+---------+---------+---------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+---------------+---------+---------+---------------------+------+----------+-------------+
| 1 | SIMPLE | a | NULL | ALL | CountryCode | NULL | NULL | NULL | 4188 | 33.33 | Using where |
| 1 | SIMPLE | b | NULL | eq_ref | PRIMARY | PRIMARY | 3 | world.a.CountryCode | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+---------------+---------+---------+---------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
10.2.6 const(system) 主鍵或唯一鍵的等值
MySQL [world]>desc select * from city where id=10;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | city | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
NULL : 獲取不到數(shù)據(jù)
oldguo[world]>desc select * from city where id=100000000000000;
10.3 possible_keys:可能會用到的索引☆ ☆ ☆
NULL:沒有和查詢條件匹配的所有條目
有值:有和查詢條件匹配的索引條目彬坏,但是沒走,大部分原因是語句查詢方式不符合索引應(yīng)用條件
10.4 key:使用到的索引☆☆☆☆
最終使用的索引膝晾,可以幫助我們判斷是否走了合適的索引
10.5 key_len:索引的覆蓋長度☆☆☆☆☆
在聯(lián)合索引應(yīng)用的判斷時苍鲜,會經(jīng)常看
單列索引:
字符集 | 字節(jié)個數(shù) | not null | 沒有指定not null |
---|---|---|---|
utf8 | 3個字節(jié) | ||
int | 4個字節(jié) | 4 | 4+1 |
tinyint | 1個字節(jié) | 1 | 2 |
utf8mb4 | 一個字符最大是4個字節(jié) | ||
char(2) | 2*4 | 2*4+1 | |
varchar(2) | 2*4+2 | 2*4+2+1 |
說明:
- 有非空約束時玷犹,key_length就是最大字節(jié)長度
- 在沒有非空約束時:字符最大長度+1
- varchar類型,需要額外在最大字符長度+2(存儲字符長度的最長度占位)
create table t1 (
id int ,
num int not null,
k1 char(2),
k2 char(2) not null,
k3 varchar(2),
k4 varchar(2) not null)charset utf8mb4;
聯(lián)合搜索優(yōu)化
MySQL [world]>desc t1;
+-------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| num | int(11) | NO | | NULL | |
| k1 | char(2) | YES | | NULL | |
| k2 | char(2) | NO | | NULL | |
| k3 | varchar(2) | YES | | NULL | |
| k4 | varchar(2) | NO | | NULL | |
+-------+------------+------+-----+---------+-------+
====================================
alter table t1 add index idx(id,num,k1,k2,k3,k4);
select 5+4+9+8+11+10; \\ 47
(1)最完美的查詢情況
idx(id,num,k1,k2,k3,k4)
desc select * from t1 where id=1 and num=1 and k1='a' and k2'a' and k3='a' and k4='a';
desc select * from t1 where num=1 and id=1 and k1='a' and k2'a' and k3='a' and k4='a';
結(jié)論:
當(dāng)查詢條件中歹颓,包含了索引列中所有的列條件油湖,并且都是等值的查詢,那么無關(guān)排列順序乏德,都可以走全聯(lián)合索引優(yōu)化撤奸;
原因是優(yōu)化器會自動調(diào)整順序喊括,達到最佳的優(yōu)化效果
所以,我們重點需要關(guān)注的是聯(lián)合索引建立的順序,從左到右郑什,唯一值越多的列放在最左邊
(2) 查詢條件中府喳,哪些因素會key_len長度
#安裝索引的建立順序,在查詢條件中蘑拯,少了任意一個中間列,后續(xù)列都無法走索引
desc select * from t1 where id=1 and k1='a' and k2'a' and k3='a' and k4='a';
#在條件查詢中間,出現(xiàn)不等值查詢時,從不等值列開始,所有列都無法使用聯(lián)合索引 (暫存)
oldguo[world]>desc select * from t1 where id=1 and num>10 and k1='a' and k2='a' and k3='a' and k4='a';
優(yōu)化方法:
將不等值列放在最后.
oldguo[world]>alter table t1 add index idx1(id,k1,k2,k3,k4,num);
oldguo[world]>alter table t1 drop index idx;
(3) 如果有多子句的條件查詢(必須是聯(lián)合索引)
按照子句的執(zhí)行順序,建立聯(lián)合索引.
10.6 Extra:額外的信息 ☆☆☆☆
Using filesort : 原因是 在 group by ,order by,distinct等.
一般優(yōu)化的方法是,和where條件的列建立聯(lián)合索引
11.小結(jié)
11.1 索引的類型(算法)
BTree
HASH
RTREE
11.2 BTREE
B-tree
B+TREE #在葉子節(jié)點加了雙向指針
B*TREE #在枝節(jié)點加了雙向指針
11.3 B+Tree有什么優(yōu)勢
在葉子節(jié)點增加了相鄰葉子節(jié)點雙向指針
為了優(yōu)化范圍查詢
11.4 輔助索引和聚集索引區(qū)別
葉子節(jié)點 ☆☆☆☆☆
- 輔助索引是手工創(chuàng)建的弯蚜,
- 聚集索引是自動跟主鍵生成
- 聚集索引只有一個
- 輔助索引可以有多個
11.5 輔助索引細(xì)分
單列
聯(lián)合
唯一
前綴
11.6 索引樹高度
數(shù)據(jù)量
數(shù)據(jù)類型
數(shù)據(jù)列的長度
聚集索引列的長度
11.7 索引基本管理
show index from t1;
desc t1;
alter table t1 add idnex 索引名(列名)
alter table t1 drop index 索引名
11.8 explain
1. 作用
- 獲取優(yōu)化器選擇的執(zhí)行計劃
2. 分析執(zhí)行計劃
type :
ALL:全表掃描
(1) 查詢條件沒有建索引
use test
desc select * from t100w where id=10;
(2) 有索引不走
desc select * from t100w where k2 !='aaaa';
desc select * from t100w where k2 like '%aa%'; #前面不加%走索引
desc select * from t100w where k2 not in ('aaaa','bbbb');
desc select * from t100w; #查全表也不走索引
index:全索引掃描
desc select k2 from t100w;
Range: 索引范圍掃描
#>, <, >=, <=, like, between and
desc select * from world.city where id > 10;
or in
desc select * from world.city where countrycode in ('CHN','USA');
改寫:
desc select * from world.city where countrycode='CHN' union all
select * from world.city where countrycode='USA';
ref : 輔助索引等值查詢
oldguo[test]>desc select * from world.city where countrycode='CHN';
eq_ref : 多表連接查詢,非驅(qū)動表on的條件列是主鍵或者唯一鍵
desc select city.name,country.name from city left join country on city.countrycode=country.code where city.population<100;
const(system) : 主鍵或者唯一鍵的等值查詢
oldguo[world]>desc select * from city where id=10;
NULL:略
key_len: utf8mb4
not null 沒設(shè)置
int 4 +1
varchar(10) 4*10+2 +1
char(10) 4*10 +1
====================================
聯(lián)合索引準(zhǔn)備
create table t1(a int not null ,b char(10) not null ,c varchar(10) not null )charset utf8mb4;
oldguo[test]>desc t1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a | int(11) | NO | | NULL | |
| b | char(10) | NO | | NULL | |
| c | varchar(10) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
idx(a,b,c) = 4 + 40 + 42 = 86
(1) 最理想的
desc select * from t1 where a=1 and b='a' and c='a';
desc select * from t1 where b='1' and a=1 and c='a';
desc select * from t1 where c='1' and a=1 and b='a';
desc select * from t1 where c='1' and b='a' and a=1;
desc select * from t1 where a=1 and c='a' and b='a';
desc select * from t1 where b='1' and c='a' and a=1;
唯一值多的放在左邊
(2) 部分索引
desc select * from t1 where a=1 and c='a';
oldguo[test]>desc select * from t1 where a=1 and b like 'a%' and c='a';
(3) where + order by
Extra:filesort ===> 排序不走索引,走的額外排序
oldguo[test]>desc select * from world.city where countrycode='CHN' order by population;
擴展:
oldguo[test]>desc format=json select * from t1 where a=1 and c='a' and b='a';