TiDB執(zhí)行計(jì)劃(二)

接一篇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ì)劃結(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ì)劃如下:

estRows
  • 因?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ù)estRowsuser_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ì)劃如下:

COUNT函數(shù)
  • 對于索引列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ì)劃如下:

group by
  • 對于索引列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ì)劃如下:

IN 子查詢
  • 首先,子查詢沒有加條件,是一個(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_42root也就是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ì)劃如下:

TableRowIDScan栗子
  • 看這個(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)識。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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