接一篇TiDB執(zhí)行計(jì)劃(一)翁都,上一篇中主要介紹了執(zhí)行計(jì)劃中涉及到的算子耕蝉,今天把執(zhí)行計(jì)劃中剩余的東西講完
查詢計(jì)劃命令
EXPLAIN
命令乾颁,可以查看TiDB執(zhí)行sql時(shí)的執(zhí)行計(jì)劃蜜氨,用法和mysql一樣炎滞,跟上sql即可
EXPLAIN SQL語(yǔ)句
舉個(gè)栗子(脫敏數(shù)據(jù))
執(zhí)行 EXPLAIN
EXPLAIN
select
a0_.id,
a0_.create_time,
a0_.end_time,
a0_.flow_id,
a0_.campaign_id,
a0_.unit_id,
a0_.oa_id,
a0_.org_path_,
a0_.param,
a0_.start_time,
a0_.state,
a0_.user_type,
a0_.update_time,
a0_.user_id
from
table_a a0_
where
a0_.campaign_id = 354361236223
and a0_.user_id = 25325123
and a0_.user_type = 1
and a0_.param = '1'
limit
1000
執(zhí)行計(jì)劃結(jié)果
執(zhí)行計(jì)劃以一個(gè)樹(shù)形結(jié)構(gòu)展示出來(lái),來(lái)說(shuō)說(shuō)每一列的含義吧:
-
id
為算子舵匾,是執(zhí)行sql時(shí)俊抵,每一步需要執(zhí)行子任務(wù) -
estRows
為每一個(gè)子任務(wù)預(yù)估需要處理的行數(shù) -
task
為子任務(wù)執(zhí)行時(shí)候所在的位置 -
access-object
子任務(wù)的對(duì)象,比如說(shuō)表坐梯、索引等 -
operator info
子任務(wù)執(zhí)行時(shí)候的一些算是操作日志的信息吧
上一篇文章說(shuō)了算子徽诲,今天來(lái)說(shuō)下執(zhí)行計(jì)劃中,剩下這幾個(gè)字段estRows
吵血、task
谎替、access-object
、operator info
的含義吧
estRows
:為每一個(gè)子任務(wù)預(yù)估需要處理的行數(shù)
這個(gè)很容易理解蹋辅,就直接上栗子了
select
user_id
from
tablea a0_
GROUP by
user_id
這個(gè)sql钱贯,對(duì)于索引列user_id
使用了group by
,導(dǎo)致了執(zhí)行時(shí)需要對(duì)所有索引數(shù)據(jù)進(jìn)行掃描侦另,會(huì)出現(xiàn)IndexFullScan
算子秩命,執(zhí)行計(jì)劃如下:
- 因?yàn)檫@個(gè)sql的執(zhí)行計(jì)劃是,先對(duì)于索引列
user_id
進(jìn)行了索引數(shù)據(jù)全量進(jìn)行掃描褒傅,使用了IndexFullScan
算子弃锐,所以IndexFullScan_11
這一步的算子的預(yù)估行數(shù)estRows
是索引列user_id
全量數(shù)據(jù)的數(shù)據(jù)量,133270314 - 這個(gè)sql后一步的執(zhí)行是通過(guò)
IndexReader
算子對(duì)下層算子的數(shù)據(jù)進(jìn)行一個(gè)聚合樊卓,所以IndexReader_13
算子的預(yù)估行數(shù)estRows
是user_id
列 group by以后的數(shù)據(jù)拿愧,也就是873229.35了
task
:為子任務(wù)執(zhí)行時(shí)候所在的位置
- 為子任務(wù)執(zhí)行時(shí)候所在的位置
- 主要有兩種
-
cop
,是指使用TiKV
中的Coprocessor
執(zhí)行的計(jì)算任務(wù),支持大部分函數(shù)(包括聚合函數(shù)和標(biāo)量函數(shù))碌尔、LIMIT
操作浇辜、索引掃描和表掃描 -
root
,是指在TiDB
中執(zhí)行的計(jì)算任務(wù),一般所有匯聚TiKV/TiFlash
上掃描的數(shù)據(jù)或者計(jì)算結(jié)果的算子都只能作為root
task在TiDB
上執(zhí)行,所有的Join
操作都只能作為root
task在TiDB
上執(zhí)行 - TiDB的SQL優(yōu)化的目標(biāo)之一是將計(jì)算盡可能地下推到
TiKV
中執(zhí)行
舉個(gè)栗子
栗子1:聚合查詢栗子唾戚,使用COUNT
:
select
COUNT(user_id)
from
tablea a0_
這個(gè)sql柳洋,對(duì)于索引列user_id
使用了COUNT函數(shù)
,導(dǎo)致了執(zhí)行時(shí)需要對(duì)所有索引數(shù)據(jù)進(jìn)行掃描叹坦,會(huì)出現(xiàn)IndexFullScan
算子熊镣,執(zhí)行計(jì)劃如下:
- 對(duì)于索引列
user_id
使用了COUNT函數(shù)
,先會(huì)對(duì)索引列user_id
進(jìn)行索引數(shù)據(jù)全量掃描募书,IndexFullScan_19
算子的執(zhí)行位置為cop[tikv]
- 后續(xù)執(zhí)行
SteamAgg_8
算子時(shí)候绪囱,因?yàn)槭?聚合函數(shù),也會(huì)在cop[tikv]
上執(zhí)行 - 最終的
IndexReader_21
算子對(duì)下層算子的數(shù)據(jù)進(jìn)行一個(gè)聚合莹捡,在root
也就是TiDB
中執(zhí)行
栗子2:聚合查詢栗子鬼吵,使用group by
:
select
user_id
from
tablea a0_
GROUP by
user_id
這個(gè)sql,對(duì)于索引列user_id
使用了group by
篮赢,導(dǎo)致了執(zhí)行時(shí)需要對(duì)所有索引數(shù)據(jù)進(jìn)行掃描齿椅,會(huì)出現(xiàn)IndexFullScan
算子琉挖,執(zhí)行計(jì)劃如下:
- 對(duì)于索引列
user_id
使用了group by
,先會(huì)對(duì)索引列user_id
進(jìn)行索引數(shù)據(jù)全量掃描涣脚,IndexFullScan_11
算子的執(zhí)行位置為cop[tikv]
- 后續(xù)執(zhí)行
HashAgg_5
算子時(shí)候示辈,因?yàn)槭?code>group by,也會(huì)在cop[tikv]
上執(zhí)行 - 最終的
IndexReader_13
算子對(duì)下層算子的數(shù)據(jù)進(jìn)行一個(gè)聚合遣蚀,在root
也就是TiDB
中執(zhí)行
栗子3:子查詢栗子矾麻,使用索引IN 子查詢,當(dāng)子查詢?yōu)槿繒r(shí)
:
select
*
from
tablea a0_
where
user_id IN (
select
user_id
from
tablea
)
這個(gè)sql妙同,對(duì)于索引列user_id
使用了in射富,子查詢?yōu)槿頀呙瑁詴?huì)導(dǎo)致外層查詢會(huì)對(duì)索引列user_id
進(jìn)行全索引數(shù)據(jù)進(jìn)行掃描粥帚,會(huì)出現(xiàn)IndexFullScan
算子,執(zhí)行計(jì)劃如下:
- 首先限次,子查詢沒(méi)有加條件芒涡,是一個(gè)全表掃描,看執(zhí)行計(jì)劃2的地方卖漫,出現(xiàn)了一個(gè)
TableFullScan_49
,由于子查詢是全量數(shù)據(jù)费尽,會(huì)在cop[tikv]
上執(zhí)行 - 聚合子查詢結(jié)果的
TableReader_50
會(huì)在root
也就是TiDB
中執(zhí)行 - 看執(zhí)行計(jì)劃1的地方,當(dāng)外層sql對(duì)索引列
user_id
進(jìn)行In時(shí)候羊始,會(huì)對(duì)索引列user_id
進(jìn)行全索引數(shù)據(jù)的掃描旱幼,IndexFullScan_40
會(huì)在cop[tikv]
上執(zhí)行 - 同樣1位置的聚合算子
IndexReader_42
在root
也就是TiDB
中執(zhí)行 - 最終的
HashJoin_22
算子對(duì)下層算子的數(shù)據(jù)進(jìn)行一個(gè)聚合,在root
也就是TiDB
中執(zhí)行
access-object
: 子任務(wù)的對(duì)象突委,比如說(shuō)表柏卤、索引等
這個(gè)很容易理解,就直接上栗子了
select
*
from
tablea a1_
where
a1_.user_id = 123214125
執(zhí)行計(jì)劃如下:
- 看這個(gè)sql匀油,是一個(gè)通過(guò)索引列
user_id
進(jìn)行了索引范圍掃描缘缚,他的執(zhí)行邏輯是,先通過(guò)對(duì)于索引列user_id
進(jìn)行了一個(gè)范圍掃描敌蚜,得到所有符合條件的rowId
桥滨,然后通過(guò)rowId
掃描表獲得數(shù)據(jù),看執(zhí)行也是弛车,首先在Build
端齐媒,通過(guò)IndexRangeScan
算子,對(duì)于索引列user_id
進(jìn)行了范圍掃描纷跛,掃描到的rowId
喻括,在Probe
端,在通過(guò)TableRowIDScan
算子,通過(guò)rowId
掃描表獲取數(shù)據(jù)忽舟,最終通過(guò)IndexLookUp
算子來(lái)匯聚最終的數(shù)據(jù) - 看這個(gè)sql執(zhí)行計(jì)劃的
access-object
- 首先在
Build
端双妨,通過(guò)IndexRangeScan_8(Build)
算子淮阐,對(duì)于索引列user_id
進(jìn)行了范圍掃描,所以該算子的對(duì)象是table:a0_,index:idx_user_id(user_id)
刁品,意思為操作的對(duì)象是表a0_
的索引idx_user_id(user_id)
- 然后通過(guò)范圍掃描索引得到的
rowId
掃描表獲得數(shù)據(jù)泣特,所以TableRowIDScan_9(Probe)
算子的操作對(duì)象是表a0_
operator info
: 子任務(wù)執(zhí)行時(shí)候的一些算是操作日志的信息
這個(gè)很容易理解,基本是每一步的操作日志挑随,就不舉栗子說(shuō)明状您,從原來(lái)的栗子中都可以看的懂
TiDB執(zhí)行計(jì)劃中的算子就為大家說(shuō)到這里,歡迎大家來(lái)交流兜挨,指出文中一些說(shuō)錯(cuò)的地方膏孟,讓我加深認(rèn)識(shí)。