接一篇TiDB執(zhí)行計(jì)劃(一),上一篇中主要介紹了執(zhí)行計(jì)劃中涉及到的算子,今天把執(zhí)行計(jì)劃中剩余的東西講完
查詢計(jì)劃命令
EXPLAIN命令,可以查看TiDB執(zhí)行sql時(shí)的執(zhí)行計(jì)劃,用法和mysql一樣,跟上sql即可
EXPLAIN SQL語句
舉個(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è)樹形結(jié)構(gòu)展示出來,來說說每一列的含義吧:
-
id為算子,是執(zhí)行sql時(shí),每一步需要執(zhí)行子任務(wù) -
estRows為每一個(gè)子任務(wù)預(yù)估需要處理的行數(shù) -
task為子任務(wù)執(zhí)行時(shí)候所在的位置 -
access-object子任務(wù)的對象,比如說表、索引等 -
operator info子任務(wù)執(zhí)行時(shí)候的一些算是操作日志的信息吧
上一篇文章說了算子,今天來說下執(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,對于索引列user_id使用了group by,導(dǎo)致了執(zhí)行時(shí)需要對所有索引數(shù)據(jù)進(jìn)行掃描,會(huì)出現(xiàn)IndexFullScan算子,執(zhí)行計(jì)劃如下:

- 因?yàn)檫@個(gè)sql的執(zhí)行計(jì)劃是,先對于索引列
user_id進(jìn)行了索引數(shù)據(jù)全量進(jìn)行掃描,使用了IndexFullScan算子,所以IndexFullScan_11這一步的算子的預(yù)估行數(shù)estRows是索引列user_id全量數(shù)據(jù)的數(shù)據(jù)量,133270314 - 這個(gè)sql后一步的執(zhí)行是通過
IndexReader算子對下層算子的數(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é)果的算子都只能作為roottask在TiDB上執(zhí)行,所有的Join操作都只能作為roottask在TiDB上執(zhí)行 - TiDB的SQL優(yōu)化的目標(biāo)之一是將計(jì)算盡可能地下推到
TiKV中執(zhí)行
舉個(gè)栗子
栗子1:聚合查詢栗子,使用COUNT:
select
COUNT(user_id)
from
tablea a0_
這個(gè)sql,對于索引列user_id使用了COUNT函數(shù),導(dǎo)致了執(zhí)行時(shí)需要對所有索引數(shù)據(jù)進(jìn)行掃描,會(huì)出現(xiàn)IndexFullScan算子,執(zhí)行計(jì)劃如下:

- 對于索引列
user_id使用了COUNT函數(shù),先會(huì)對索引列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算子對下層算子的數(shù)據(jù)進(jìn)行一個(gè)聚合,在root也就是TiDB中執(zhí)行
栗子2:聚合查詢栗子,使用group by:
select
user_id
from
tablea a0_
GROUP by
user_id
這個(gè)sql,對于索引列user_id使用了group by,導(dǎo)致了執(zhí)行時(shí)需要對所有索引數(shù)據(jù)進(jìn)行掃描,會(huì)出現(xiàn)IndexFullScan算子,執(zhí)行計(jì)劃如下:

- 對于索引列
user_id使用了group by,先會(huì)對索引列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算子對下層算子的數(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,對于索引列user_id使用了in,子查詢?yōu)槿頀呙?,所以?huì)導(dǎo)致外層查詢會(huì)對索引列user_id進(jìn)行全索引數(shù)據(jù)進(jìn)行掃描,會(huì)出現(xiàn)IndexFullScan算子,執(zhí)行計(jì)劃如下:

- 首先,子查詢沒有加條件,是一個(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對索引列
user_id進(jìn)行In時(shí)候,會(huì)對索引列user_id進(jìn)行全索引數(shù)據(jù)的掃描,IndexFullScan_40會(huì)在cop[tikv]上執(zhí)行 - 同樣1位置的聚合算子
IndexReader_42在root也就是TiDB中執(zhí)行 - 最終的
HashJoin_22算子對下層算子的數(shù)據(jù)進(jìn)行一個(gè)聚合,在root也就是TiDB中執(zhí)行
access-object: 子任務(wù)的對象,比如說表、索引等
這個(gè)很容易理解,就直接上栗子了
select
*
from
tablea a1_
where
a1_.user_id = 123214125
執(zhí)行計(jì)劃如下:

- 看這個(gè)sql,是一個(gè)通過索引列
user_id進(jìn)行了索引范圍掃描,他的執(zhí)行邏輯是,先通過對于索引列user_id進(jìn)行了一個(gè)范圍掃描,得到所有符合條件的rowId,然后通過rowId掃描表獲得數(shù)據(jù),看執(zhí)行也是,首先在Build端,通過IndexRangeScan算子,對于索引列user_id進(jìn)行了范圍掃描,掃描到的rowId,在Probe端,在通過TableRowIDScan算子,通過rowId掃描表獲取數(shù)據(jù),最終通過IndexLookUp算子來匯聚最終的數(shù)據(jù) - 看這個(gè)sql執(zhí)行計(jì)劃的
access-object - 首先在
Build端,通過IndexRangeScan_8(Build)算子,對于索引列user_id進(jìn)行了范圍掃描,所以該算子的對象是table:a0_,index:idx_user_id(user_id),意思為操作的對象是表a0_的索引idx_user_id(user_id) - 然后通過范圍掃描索引得到的
rowId掃描表獲得數(shù)據(jù),所以TableRowIDScan_9(Probe)算子的操作對象是表a0_
operator info: 子任務(wù)執(zhí)行時(shí)候的一些算是操作日志的信息
這個(gè)很容易理解,基本是每一步的操作日志,就不舉栗子說明,從原來的栗子中都可以看的懂
TiDB執(zhí)行計(jì)劃中的算子就為大家說到這里,歡迎大家來交流,指出文中一些說錯(cuò)的地方,讓我加深認(rèn)識。