本課程,適合具備一定Linux運(yùn)維或者開發(fā)基礎(chǔ)的朋友爆班,課程定級中、高級DBA辱姨。
只要掌握80%柿菩,輕松助力薪資15k-25K。
課程內(nèi)容均來自與MySQL官網(wǎng)+MySQL源碼雨涛。
配套精品視頻(2021 5月全新錄制枢舶,版權(quán)所有:郭加磊 oldguo。)替久,獲取方法私聊凉泄。
1. 介紹
相當(dāng)于一本書中的目錄,可以加速查詢(select ,update,delete ).
2. 種類
Btree (平衡多叉樹): b-tree b+tree(b*tree),優(yōu)點(diǎn):范圍查找
HASH : 優(yōu)點(diǎn),比較適合隨機(jī)的等值.
Rtree
3. Btree的細(xì)分
聚簇索引 : 主鍵索引
輔助索引 :
單列
聯(lián)合
唯一
前綴
4. 索引的管理
4.0 索引建立之前壓測:
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='test' \
--query="select * from test.t100w where k2='VWlm'" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose
4.1 查詢索引
a. desc city;
PRI : 主鍵索引
MUL : 普通索引
UNI : 唯一索引
b. show index from city;
Table 表名
Key_name 索引名
Column_name 列名
Cardinality 基數(shù)(選擇度),位置值的多少
Cardinality 建立索引之前,基數(shù)如何計(jì)算的?
select count(distinct countrycode) from city;
4.2 創(chuàng)建
alter table city add index i_name(name);
alter table city add index i_d_p(distinct,population);
alter table city add index i_x(name(10));
alter table t1 add unique index i_a(a);
4.3 刪除
alter table city drop index i_name;
4.4 8.0新特性--> invisible index 不可見索引
mysql> alter table city alter index idx_name invisible;
SELECT INDEX_NAME, IS_VISIBLE
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'world' AND table_name='city';
5. B+tree查找算法介紹
5.1 平衡
不管查找哪個數(shù),需要查找次數(shù)理論上是相同的.對于一個三層b樹來講,理論上查找每個值都是三次IO.
5.2 擅長范圍查找
講究快速鎖定范圍.
B+tree,加入了雙向指針(頭尾相接),進(jìn)一步增強(qiáng)范圍查找.減少對于ROOT和NON-LEAF的訪問次數(shù).
5.3 構(gòu)建過程
葉子: 先將數(shù)據(jù)排序,生成葉子節(jié)點(diǎn).
枝 : 保存葉子節(jié)點(diǎn)的范圍(>=1 <5)+指針(→)
根 : 保存枝節(jié)點(diǎn)范圍+指針
葉子節(jié)點(diǎn)和枝節(jié)點(diǎn)都有雙向指針.
6. MySQL中如何應(yīng)用B+TREE ******
6.1 名詞解釋
區(qū)(簇)extent : 連續(xù)的64pages ,默認(rèn)是1M存儲空間.
page頁 : 16KB大小,MySQL中最小的IO單元.
6.2 IOT 組織表
數(shù)據(jù)應(yīng)該按照索引結(jié)構(gòu)有序(順序)組織和存儲數(shù)據(jù).
MySQL使用聚簇索引組織存儲數(shù)據(jù).
6.3 聚簇(區(qū))索引
6.3.1 構(gòu)建條件
a. 如果表中有主鍵,主鍵就被作為聚簇索引.
b. 沒有主鍵,第一個不為空的唯一鍵.
c. 什么都沒有,自動生成一個6字節(jié)的隱藏列,作為聚簇索引.
https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html
6.3.2 如何形成B樹結(jié)構(gòu).
葉子節(jié)點(diǎn): 聚簇索引組織表,存數(shù)據(jù)時,已經(jīng)是按照ID列有序存儲數(shù)據(jù)到各個連續(xù)的數(shù)據(jù)頁中.原表數(shù)據(jù)存儲結(jié)構(gòu)就是葉子節(jié)點(diǎn).
枝節(jié)點(diǎn) : 葉子節(jié)點(diǎn)中ID范圍+指針
根節(jié)點(diǎn) : 枝節(jié)點(diǎn)的ID范圍+指針
6.3.3 優(yōu)化了哪些查詢?
只能優(yōu)化基于ID作為條件.索引單純使用ID列查詢,很局限.
6.4 輔助索引
6.4.1 構(gòu)建條件
需要人為按照需求創(chuàng)建輔助索引.
6.4.2 如何形成B樹結(jié)構(gòu)
alter table t1 add index idx(name);
葉子節(jié)點(diǎn) : 將輔助索引列值(name)+ID提取出來,按照輔助索引列值從小到大排序,存儲到各個page中,生成葉子節(jié)點(diǎn).
枝節(jié)點(diǎn) : 存儲了葉子節(jié)點(diǎn)中,name列范圍+指針.
根節(jié)點(diǎn) : 枝節(jié)點(diǎn)的name的范圍+指針.
6.4.3 優(yōu)化了哪些查詢?
如果查詢條件使用了name列,都會先掃描輔助索引,獲得ID,再回到聚簇索引(回表),按照ID進(jìn)行聚簇索引掃描,最終獲取到數(shù)據(jù)行.
https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html
6.5 聯(lián)合輔助索引結(jié)構(gòu)
6.5.1 構(gòu)建過程
alter table t1 add index idx_n_g(a,b)
葉子節(jié)點(diǎn) : 提取a+b+id列值,按照a,b聯(lián)合排序(從小到大),生成葉子節(jié)點(diǎn).
枝節(jié)點(diǎn) : 葉子節(jié)點(diǎn)最左列范圍+指針
根節(jié)點(diǎn) : 枝節(jié)點(diǎn)的范圍+指針.
6.5.2 優(yōu)化了哪些查詢?
查詢條件中必須包含最左列條件(a),先通過 a條件 掃描聯(lián)合索引的根節(jié)點(diǎn)和枝節(jié)點(diǎn),從而得到葉子節(jié)點(diǎn)范圍.再拿b作為條件過濾一次.
最終目的,得到更精確的ID .理論上減少回表的次數(shù).
6.5.3 最左原則
建立聯(lián)合索引時,選擇基數(shù)大(重復(fù)值少)作為最左列.
查詢條件中必須要包含最左列條件.
7. 索引樹高度影響因素
一般建議3-4層為佳,3層b樹,2000w+.
a. 數(shù)據(jù)行多
分區(qū)表.
定期歸檔: 一般按照時間字段,定期歸檔到歷史庫中. pt-archiver.
分庫分表:分布式
b. 索引列長度過長
前綴索引.
c. 數(shù)據(jù)類型
足夠
簡短
合適
8. 回表問題
8.1 回表是什么?
輔助索引掃描之后,得到ID,再回到聚簇索引查找的過程.
8.2 回表會帶來什么問題?
IO : 次數(shù)和量會增加.
IOPS : 1000次/s
吞吐量 : 300M/s
8.3 怎么減少回表
a. 建索引使用聯(lián)合索引(覆蓋),盡可能多將查詢條件的數(shù)據(jù)包含聯(lián)合索引中.
b. 精細(xì)查詢條件(業(yè)務(wù)方面,> and < ,limit)
c. 查詢條件要符合聯(lián)合索引規(guī)則,覆蓋的列越多越好.
9. 擴(kuò)展項(xiàng): 索引自優(yōu)化AHI(自適應(yīng)hash索引)\change buffer
AHI : 索引的索引. 為內(nèi)存中的熱點(diǎn)索引頁,做了一個HASH索引表,能夠快速找到需要的索引頁地址.
https://dev.mysql.com/doc/refman/8.0/en/innodb-adaptive-hash.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-architecture.html
change buffer :
對于輔助索引的變化,不會立即更新到索引中.暫存至change buffer .
https://dev.mysql.com/doc/refman/8.0/en/innodb-change-buffer.html
10. 分析執(zhí)行計(jì)劃
10.1 是什么?
優(yōu)化器(算法)最終得出的,代價(jià)最低的,SQL語句的執(zhí)行方案.
10.2 為什么要分析執(zhí)行計(jì)劃?
場景一: 分析比較慢的語句.
場景二: 上線新業(yè)務(wù),可能會包含很多select update delete...,提前發(fā)現(xiàn)問題.
10.3 如何抓取執(zhí)行計(jì)劃
a. 抓取目標(biāo)
select update delete
b. 方法
mysql> desc select * from world.city where countrycode='CHN';
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | city | NULL | ref | CountryCode | CountryCode | 3 | const | 363 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
10.4 如何分析執(zhí)行計(jì)劃
table : 操作的表
type : 操作類型(全表\索引) ,ALL index range ref eq_ref const(system)
possible_keys : 有可能用的索引
key : 真正要用是哪索引
key_len: 索引覆蓋長度(聯(lián)合索引)
rows : 預(yù)估需要掃描的行數(shù)
Extra : using where using index using index condition using filesort sort using temp
10.5 type 詳解
a. ALL 全表掃描
mysql> explain select * from world.city ;
mysql> explain select * from world.city where countrycode !='chn';
mysql> explain select * from world.city where countrycode like '%hn%';
mysql> explain select * from world.city where countrycode not in ('chn','usa');
b. index 全索引掃描
需要掃描整顆索引樹,才能得到想要的結(jié)果.
desc select id ,countrycode from world.city;
c. range 索引范圍
是我們應(yīng)用索引優(yōu)化的底線,也是應(yīng)用最多的.
mysql> desc select * from city where id<10;
mysql> desc select * from city where countrycode like 'ch%';
mysql> desc select * from city where countrycode in ('CHN','USA');
SQL 改寫為:
desc
select * from city where countrycode='CHN' union all select * from city where countrycode='USA'
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='world' \
--query=" select * from city where countrycode in ('CHN','USA')" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='world' \
--query="select * from city where countrycode='CHN' union all select * from city where countrycode='USA'" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose
小經(jīng)驗(yàn):
索引列基數(shù)多少 + 壓測結(jié)果,最終評估是否需要使用union .
d. ref : 輔助索引等值查詢
mysql> desc select * from city where countrycode='CHN';
e. eq_ref : 非驅(qū)動表,連接條件是主鍵或唯一鍵.
mysql> desc select a.name, b.name,a.countrycode,a.population
from city as a
join country as b
on a.countrycode=b.code where a.population<100;
補(bǔ)充:
多表連接時,小結(jié)果集的表驅(qū)動大表.
優(yōu)化會自動判斷查詢語句中的誰做為驅(qū)動表更合適.有可能會出現(xiàn)選擇錯誤.
我們可以通過left join 強(qiáng)制驅(qū)動表干預(yù)執(zhí)行計(jì)劃.
彩蛋 : 如何判斷一條連接語句中,誰是驅(qū)動表?
1. 優(yōu)化器的判斷
mysql> desc select * from city join country on city.countrycode=country.code where city.population<100000 and country.SurfaceArea>10000000;
2. 人為判斷
mysql> select count(*) from country where country.SurfaceArea>10000000;
+----------+
| count(*) |
+----------+
| 2 |
+----------+
1 row in set (0.00 sec)
mysql> select count(*) from city where city.population<100000 ;
+----------+
| count(*) |
+----------+
| 517 |
+----------+
補(bǔ)充: 如果 where后的列中都有索引,會選擇結(jié)果集小的作為驅(qū)動表.
3. 壓測.
f. const(system)
mysql> desc select * from city where id=1;
10.6 key_len詳解
a. 介紹
(聯(lián)合)索引覆蓋長度
idx(a,b,c) ----> a (10) b(20) c(30)
b. 如何計(jì)算索引列的key_len
key_len 和每個列的最大預(yù)留長度(字節(jié))有關(guān).
數(shù)據(jù)類型 utf8mb4 沒有 not null
tinyint 1 1
int 4 1
char(10) 4*10 1
varchar(10) 4*10+2 1
c. 聯(lián)合索引應(yīng)用細(xì)節(jié)
idx(a,b,c) ----> a ab abc
完全覆蓋 :
a= and b= and c=
a= and c= and b= 等值打亂順序的
a= and b= and c范圍
a= anb b字符范圍 and c=
部分覆蓋
a= and b=
a=
a= and c=
a= anb b數(shù)字范圍 and c=
完全不覆蓋 bc --> bc b
b
c
bc
cb
優(yōu)化案例:
idx(k1,num,k2)
1. mysql> desc select * from t100w where k1='Vs' and num<27779 and k2='mnij'
優(yōu)化方案: 修改索引為idx(k1,k2,num)
10.7 extra
using index 使用了索引覆蓋掃描
using where 使用where回表掃描數(shù)據(jù)行,說明目標(biāo)表的索引沒有設(shè)計(jì)好.
a. table ----> 獲取到出問題的表
b. 看原始查詢語句中的where條件列
c. 查詢列的索引情況-----> show index from t1;
d. 按需優(yōu)化索引.
using filesort 使用了額外排序.
a. table ---->獲取到出問題的表
b. 查看原始語句中的: order by group by distinct
c. 查看列的索引情況
d. 按需優(yōu)化索引.
優(yōu)化案例:
mysql> desc select *from city where countrycode='CHN' order by population;
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
| 1 | SIMPLE | city | NULL | ref | CountryCode | CountryCode | 3 | const | 363 | 100.00 | Using filesort |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
mysql> show index from city;
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| city | 0 | PRIMARY | 1 | ID | A | 4188 | NULL | NULL | | BTREE | | | YES | NULL |
| city | 1 | CountryCode | 1 | CountryCode | A | 232 | NULL | NULL | | BTREE | | | YES | NULL |
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
mysql> alter table city add index idx(population);
mysql> desc select *from city where countrycode='CHN' order by population;
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
| 1 | SIMPLE | city | NULL | ref | CountryCode | CountryCode | 3 | const | 363 | 100.00 | Using filesort |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
mysql> alter table city add index idx_c_p(countrycode,population);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc select *from city where countrycode='CHN' order by population;
+----+-------------+-------+------------+------+---------------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | city | NULL | ref | CountryCode,idx_c_p | idx_c_p | 3 | const | 363 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------------+---------+---------+-------+------+----------+-------+
查看冗余索引
mysql> select table_schema,table_name , redundant_index_name , redundant_index_columns from sys.schema_redundant_indexes;
using temp --->
a. 條件范圍是不是過大.
b. having order by 額外排序
c. 子查詢
大幾率開發(fā)需要改寫語句了.
11. 擴(kuò)展項(xiàng): 關(guān)于索引的優(yōu)化器算法:ICP \ MRR
11.1 ICP : Index Condition Pushdown
優(yōu)化器算法:
a. 查詢優(yōu)化器算法:
mysql> select @@optimizer_switch;
b. 設(shè)置優(yōu)化器算法:
mysql> set global optimizer_switch='index_condition_pushdown=off';
hits方式:
https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html
配置文件:
my.cnf
例子 :
mysql> set global optimizer_switch='index_condition_pushdown=off';
mysql> desc select * from t100w where k1='Vs' and num<27779 and k2='mnij';
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t100w | NULL | range | idx | idx | 14 | NULL | 29 | 10.00 | Using where |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> set global optimizer_switch='index_condition_pushdown=on';
mysql> desc select * from t100w where k1='Vs' and num<27779 and k2='mnij';
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | t100w | NULL | range | idx | idx | 14 | NULL | 29 | 10.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
壓測:
a. 開ICP 2000次語句壓測 索引順序不調(diào)整
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='test' \
--query=" select * from t100w where k1='Vs' and num<27779 and k2='mnij'" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose
4.580 seconds
4.569 seconds
4.431 seconds
4.433 seconds
4.391 seconds
b. 關(guān) ICP 2000次語句壓測 索引順序不調(diào)整
5.327
5.516
5.267
5.330
5.293 seconds
c. 索引順序優(yōu)化 壓測
4.251
4.143
11.2 MRR
https://dev.mysql.com/doc/refman/8.0/en/mrr-optimization.html
12. 索引應(yīng)用規(guī)范
12.1 建立索引的原則(DBA運(yùn)維規(guī)范)
(1) 必須要有主鍵,業(yè)務(wù)無關(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ù)繁忙期侣肄,建議用pt-osc旧困。
(7) 聯(lián)合索引最左原則
12.2 不走索引的情況(開發(fā)規(guī)范)
12.2.1 沒有查詢條件醇份,或者查詢條件沒有建立索引
select * from t1 ;
select * from t1 where id=1001 or 1=1;
作業(yè):
SQL審核和審計(jì). yearning.io github, inception
12.2.2 查詢結(jié)果集是原表中的大部分?jǐn)?shù)據(jù)稼锅,應(yīng)該是15-25%以上。
查詢的結(jié)果集僚纷,超過了總數(shù)行數(shù)25%矩距,優(yōu)化器覺得就沒有必要走索引了。
MySQL的預(yù)讀功能有關(guān)怖竭。
可以通過精確查找范圍锥债,達(dá)到優(yōu)化的效果。
1000000
>500000 and
12.2.3 索引本身失效,統(tǒng)計(jì)信息不真實(shí)(過舊)
索引有自我維護(hù)的能力哮肚。
對于表內(nèi)容變化比較頻繁的情況下登夫,有可能會出現(xiàn)索引失效。
一般是刪除重建
現(xiàn)象:
有一條select語句平常查詢時很快,突然有一天很慢,會是什么原因
select? --->索引失效,統(tǒng)計(jì)數(shù)據(jù)不真實(shí)
innodb_index_stats
innodb_table_stats
mysql> ANALYZE TABLE world.city;
12.2.4 查詢條件使用函數(shù)在索引列上允趟,或者對索引列進(jìn)行運(yùn)算恼策,運(yùn)算包括(+,-潮剪,*涣楷,/,! 等)
例子:
錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;
算術(shù)運(yùn)算
函數(shù)運(yùn)算
子查詢
12.2.5 隱式轉(zhuǎn)換導(dǎo)致索引失效.這一點(diǎn)應(yīng)當(dāng)引起重視.也是開發(fā)中經(jīng)常會犯的錯誤.
mysql> desc select * from b where telnum=110;
mysql> desc select * from b where telnum='110';