索引及執(zhí)行計劃管理

索引的作用

類似于一本書的目錄,起到優(yōu)化查詢的功能

索引類型(筆試)

BTREE索引*****

RTREE索引

HASH索引

全文索引

image

BTREE的細(xì)分離(算法)

B-TREE

B +樹

B *樹

Btree索引的功能分類

聚集索引(集群索引)*****

輔助索引(二級索引)

B樹是如何構(gòu)建的

輔助索引
alter table t1 add index idx_name(name);
1.將name列的所有值取出來,進(jìn)行自動排序
2.將排完序的值均勻的落到16KB葉子節(jié)點(diǎn)數(shù)據(jù)頁中,并將索引鍵值所對應(yīng)的數(shù)據(jù)行的幾句索引列值
3.向上生成枝節(jié)點(diǎn)和根節(jié)點(diǎn)

聚集索引(IOT)
1.默認(rèn)是按照主鍵生成聚集索引,沒有主鍵,存儲引擎會使用唯一鍵。
如果都沒有,會自動生成隱藏的聚集索引
2.數(shù)據(jù)在存儲時,就會按照聚集索引打的順序存儲到磁盤的數(shù)據(jù)頁
3.由于本身書籍就是有序的,所以聚集索引構(gòu)建時,不需要進(jìn)行排序
4.聚集索引直接將磁盤的數(shù)據(jù)頁,作為葉子節(jié)點(diǎn)
5.枝節(jié)點(diǎn)和根節(jié)點(diǎn)只會調(diào)取下層節(jié)點(diǎn)主鍵的最小值

image

輔助索引和聚集索引區(qū)別

(1) 輔助索引:
葉子節(jié)點(diǎn)只保存主鍵值+索引鍵值的有序存儲
對索引鍵值會知道排序
需要手工創(chuàng)建
輔助索引可以有多個
任何列都可以創(chuàng)建
(2)
數(shù)據(jù)存儲時,就是按照聚集索引順序進(jìn)行有序存儲
葉子節(jié)點(diǎn)保存的是整個有序的數(shù)據(jù)行
葉子節(jié)點(diǎn)不需要單獨(dú)生成

輔助索引細(xì)分

單列輔助索引

select * from t1 where name=''

聯(lián)合索引

select * from t1 where a and b and c 

唯一索引


索引樹的高度(越低越好)

1. 表的數(shù)據(jù)量級大

  • 分區(qū)表
  • 分庫分表(分布式架構(gòu))

2. 列的長度

  • 盡可能選擇列值短的創(chuàng)建索引
  • 采用前綴索引

3. 數(shù)據(jù)類型選擇

  • varchar 和 char
  • enum

索引管理

壓力測試準(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)


索引命令操作

  1. 查詢索引
use school
show tables;
desc student;

Key : PRI(主鍵),UNI(唯一索引),MUL(輔助索引)

show index from student\G   

image
  1. 創(chuàng)建單列索引
alter table student add index idx_name(sname);

image
  1. 創(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   

  1. 創(chuàng)建前綴索引
alter table student add index idx(sname(5));

alter table student add telnum char(11) not null;

image
  1. 創(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)

  1. 刪除索引
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;


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)化前

image
use test;
show tables;
desc t100w;
alter table t100w add index idx_k2(k2);  #優(yōu)化

image

優(yōu)化后

image

explain (desc) ☆☆☆☆☆

explain select * from test.t100w where k2='VWtu';
or
desc select * from test.t100w where k2='VWtu';

作用 : 抓取優(yōu)化器優(yōu)化過的執(zhí)行計劃

執(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  |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+

重點(diǎn)!??!

table:以上SQL語句設(shè)計到的表☆☆☆
type:查詢的類型(全表掃描(ALL),索引掃描,查不到數(shù)據(jù)(NULL))☆☆☆☆☆
possible_keys:可能會用到的索引☆☆☆
key:使用到的索引☆☆☆☆
key_len:索引的覆蓋長度☆☆☆☆☆
Extra:額外的信息☆☆☆☆

image

type 詳細(xì)說明☆☆☆☆☆

ALL:全表掃描,不走索引

(1) 查詢條件,沒建索引
>use test;
>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  |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
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  | MUL | NULL              |                             |
| dt    | timestamp | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------+
5 rows in set (0.00 sec)

(2) 有索引不走
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);

index 全索引掃描

MySQL [test]>desc select k2 from t100w;

image

=====從range開始,我們才認(rèn)為索引是有價值的======

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 進(jìn)行改性
mysql[world]>desc select * from city where countrycode = 'CHN' union all  select * from city where countrycode = 'USA';

ref 輔助索引等值查詢

MySQL [world]>desc select * from city where countrycode='CHN';

image

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;

const(system) 主鍵或唯一鍵的等值

desc select * from city where id=100000000000;

image

possible_keys:可能會用到的索引☆ ☆ ☆

NULL:沒有和查詢條件匹配的所有條目
有值:有和查詢條件匹配的索引條目,但是沒走,大部分原因是語句查詢方式不符合索引應(yīng)用條件

10.4 key:使用到的索引☆☆☆☆

最終使用的索引,可以幫助我們判斷是否走了合適的索引

10.5 key_len:索引的覆蓋長度☆☆☆☆☆

在聯(lián)合索引應(yīng)用的判斷時,會經(jīng)???/p>

單列索引:

字符集 字節(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

說明:

  1. 有非空約束時,key_length就是最大字節(jié)長度
  2. 在沒有非空約束時:字符最大長度+1
  3. 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)整順序,達(dá)到最佳的優(yōu)化效果
所以,我們重點(diǎn)需要關(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)合索引.

Extra:額外的信息 ☆☆☆☆

Using filesort :  原因是 在 group  by ,order by,distinct等.
一般優(yōu)化的方法是,和where條件的列建立聯(lián)合索引


小結(jié)

索引的類型(算法)

BTree
HASH
RTREE

BTREE

B-tree
B+TREE
B*TREE

B+Tree有什么優(yōu)勢

在葉子節(jié)點(diǎn)增加了相鄰葉子節(jié)點(diǎn)雙向指針
為了優(yōu)化范圍查詢

輔助索引和聚集索引區(qū)別

葉子節(jié)點(diǎn) ☆☆☆☆☆

  • 輔助索引是手工創(chuàng)建的,
  • 聚集索引是自動跟主鍵生成
  • 聚集索引只有一個
  • 輔助索引可以有多個

輔助索引細(xì)分

單列
聯(lián)合
唯一
前綴

索引樹高度

數(shù)據(jù)量
數(shù)據(jù)類型
數(shù)據(jù)列的長度
聚集索引列的長度

索引基本管理

show index from t1;
desc t1;
alter table t1 add idnex 索引名(列名)
alter table t1 drop index 索引名

explain

作用:獲取優(yōu)化器選擇的執(zhí)行計劃

分析執(zhí)行計劃

(1) 查詢條件沒有建索引

use test
desc select * from t100w where id=10;

(2) 有索引不走

desc select * from t100w  where 1=1;
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');

index:全索引掃描 
    desc select k2 from t100w;

Range: 索引范圍掃描
  >, <, >=, <=, like, between and
  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;

擴(kuò)展:
oldguo[test]>desc format=json select * from t1 where a=1 and c='a' and b='a';

建立索引的原則(DBA規(guī)范)

image

建表時一定要有主鍵,一般是個無關(guān)列

選擇唯一性索引

唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄。
例如,學(xué)生表中學(xué)號是具有唯一性的字段。為該字段建立唯一性索引可以很快的確定某個學(xué)生的信息。
如果使用姓名的話,可能存在同名現(xiàn)象,從而降低查詢速度。

優(yōu)化方案:

(1) 如果非得使用重復(fù)值較多的列作為查詢條件(例如:男女),可以將表邏輯拆分
(2) 可以將此列和其他的查詢類,做聯(lián)和索引
select count(*) from world.city;
select count(distinct countrycode) from world.city;
select count(distinct countrycode,population ) from world.city;

為經(jīng)常需要where 、ORDER BY、GROUP BY,join on等操作的字段,排序操作會浪費(fèi)很多時間

如果為其建立索引,優(yōu)化查詢
注:如果經(jīng)常作為條件的列,重復(fù)值特別多,可以建立聯(lián)合索引。

盡量使用前綴來索引

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

限制索引的數(shù)目

索引的數(shù)目不是越多越好。

可能會產(chǎn)生的問題:

(1) 每個索引都需要占用磁盤空間,索引越多,需要的磁盤空間就越大。
(2) 修改表時,對索引的重構(gòu)和更新很麻煩。越多的索引,會使更新表變得很浪費(fèi)時間。
(3) 優(yōu)化器的負(fù)擔(dān)會很重,有可能會影響到優(yōu)化器的選擇.
percona-toolkit中有個工具,專門分析索引是否有用

刪除不再使用或者很少使用的索引(percona toolkit)

pt-duplicate-key-checker
表中的數(shù)據(jù)被大量更新,或者數(shù)據(jù)的使用方式被改變后,原有的一些索引可能不再需要。數(shù)據(jù)庫管理
員應(yīng)當(dāng)定期找出這些索引,將它們刪除,從而減少索引對更新操作的影響。

大表加索引,要在業(yè)務(wù)不繁忙期間操作

https://www.cnblogs.com/TeyGao/p/7160421.html

盡量少在經(jīng)常更新值的列上建索引

建索引原則

(1) 必須要有主鍵,如果沒有可以做為主鍵條件的列,創(chuàng)建無關(guān)列
(2) 經(jīng)常做為where條件列 order by group by join on, distinct 的條件(業(yè)務(wù):產(chǎn)品功能+用戶行為)
(3) 最好使用唯一值多的列作為索引,如果索引列重復(fù)值較多,可以考慮使用聯(lián)合索引
(4) 列值長度較長的索引列,我們建議使用前綴索引.
(5) 降低索引條目,一方面不要創(chuàng)建沒用索引,不常使用的索引清理,percona toolkit(xxxxx)
(6) 索引維護(hù)要避開業(yè)務(wù)繁忙期


面試題:有索引,為什么查詢效率還是很低?

1.有索引不走
2.聯(lián)合索引沒有完全覆蓋
3.索引失效或同級信息不真實(shí)

4.索引重復(fù)值太多

我一般遇到這樣的問題,排查和解決的方法有這幾種方法

1.先看看這個語句的執(zhí)行計劃

table 
type 
key_len   判斷聯(lián)合索引覆蓋長度
extra     額外排序
rows  重復(fù)值 ----->

2.沒索引建索引,改語句

建索引規(guī)范:

  1. 主鍵
  1. where group by order by distinct join on
  2. 聯(lián)合最左側(cè),唯一值
  3. 盡量使用前綴索引
  4. 索引條目
  5. 頻繁更新的列,不適合做索引列,
  6. 避開業(yè)務(wù)繁忙期,pt-tools

不走索引:

  1. 本來全表掃描
  2. 25%
  3. 計算或函數(shù)
  4. 隱式轉(zhuǎn)換
  5. like %xx%
  6. != ,not in
  7. 索引失效,統(tǒng)計信息不真實(shí)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容