最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

深入淺出openGauss的執(zhí)行器基礎(chǔ)

2023-04-18 09:51 作者:Gauss松鼠會(huì)  | 我要投稿

火山模型

執(zhí)行器各個(gè)算子解耦合的基礎(chǔ)。對(duì)于每個(gè)算子來說,只有三步:

  1. 向自己的孩子拿一個(gè) tuple。即調(diào)用孩子節(jié)點(diǎn)的 Next 函數(shù);

  2. 執(zhí)行計(jì)算;

  3. 向上層返回一個(gè) tuple。即當(dāng)前節(jié)點(diǎn) Next 函數(shù)的返回結(jié)果。

所以整個(gè)執(zhí)行器的內(nèi)核可以用下面這個(gè)偽代碼來表達(dá)。

這種模型的好處是:

  1. 設(shè)計(jì)簡(jiǎn)單,算子解耦,互相不依賴;

  2. 內(nèi)存使用量小,沒有物化的情況下,每次只消耗一個(gè) tuple 的內(nèi)存。

Tuple 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)

兩個(gè)算子之間的傳遞的都是 tuple。所以 Tuple 數(shù)據(jù)結(jié)構(gòu)是整個(gè)執(zhí)行器的核心,也是執(zhí)行器和存儲(chǔ)引擎交互的數(shù)據(jù)結(jié)構(gòu)。

先看下幾個(gè)具體數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系(附上關(guān)鍵的變量,非全部)

以上圖為例,最下層的 seqscan,會(huì)調(diào)用存儲(chǔ)引擎的?heap_getnext?函數(shù)(astore)。

以上就是存儲(chǔ)引擎和執(zhí)行引擎之間 tuple 怎么交互的。總結(jié)下就是存儲(chǔ)引擎會(huì)把實(shí)際數(shù)據(jù)所在的地址傳給上層,最后執(zhí)行引擎拿到的結(jié)構(gòu)體為TupleTableSlot。?

所以,執(zhí)行引擎只關(guān)心?TupleTableSlot?這一個(gè)結(jié)構(gòu)體即可。

一個(gè)很自然的問題就是,如果算子之間的 Tuple 都是深拷貝傳遞,對(duì)于較大的 tuple 來說(包含 varchar 類型),性能很差。因此,PG 中的 tuple 分了4類(詳見頭文件tuptable.h):

  1. 第一類 tuple 是 Disk buffer page 上的 physical tuple,就是前文的 HeapTuple。Buffer 一定要 pin 住。這種 Tuple 可以直接根據(jù)頭地址進(jìn)行訪問。

  2. 內(nèi)存中的組裝的tuple,格式和文件上 tuple 完全一樣,也是進(jìn)行過壓縮。這種也算是 physical tuple,可以直接用地址。?

  3. Minimal physical tuple,也是內(nèi)存中的,唯一區(qū)別在于沒有系統(tǒng)列(xmin、xmax 等)。

  4. ?virtual tuple,只記錄每一個(gè)屬性數(shù)據(jù)的地址,并沒有深拷貝,而是直接通過地址來訪問?,F(xiàn)在約定的是,當(dāng)一個(gè)算子向上層吐一個(gè) tuple,直到它下次被調(diào)用時(shí),該tuple所在的內(nèi)存不會(huì)被釋放。

對(duì)于查詢來說,第一類和第四類最為常見。2、3兩類會(huì)在物化的時(shí)候使用,比如 CTEScan、HashJoin 建立 hash 表的時(shí)候,相當(dāng)于深拷貝。性能比較敏感的場(chǎng)合,盡量避免2、3類 tuple的使用。

slot 的創(chuàng)建一般通過?ExecAllocTableSlot、ExecSetSlotDescriptor?兩個(gè)函數(shù)來分配內(nèi)存和初始化信息。在執(zhí)行器初始化階段,每個(gè)算子會(huì)分配相應(yīng)的 slot。

以上圖為例,

  1. SeqScan 算子有一個(gè) tupleTableSlot,是一個(gè) physical tuple,指向的是 Buffer 中的地址。

    向上層返回自己的 tuple slot;

  2. HashJoin 一個(gè)一個(gè)拿到 內(nèi)表的 tuple slot,需要建立 hash 表,所以創(chuàng)建了一個(gè) minimal physical tuple,復(fù)制內(nèi)表的 tuple 內(nèi)容;

  3. Hash表建立后,HashJoin 算子然后一個(gè)一個(gè)拿到外表的 tuple slot,做 join 計(jì)算。

    HashJoin 自己有一個(gè) tuple slot,如果碰到匹配,則把自己的 tuple slot 設(shè)置成 virtual tuple,其中的 tts_values 指向的是孩子節(jié)點(diǎn)的 tuple 中的地址。

    再向上返回。

    其中,外表的內(nèi)容指向的是 Buffer 上的physical tuple, 內(nèi)表的內(nèi)容指向的是 hash 表中的 minimal physical tuple;

  4. 當(dāng) HashJoin 被再次調(diào)用時(shí),它會(huì)重置 tuple slot。因?yàn)槭?virtual tuple,所以沒做任何事情。然后 HashJoin 會(huì)調(diào)用 SeqScan 拿下一條tuple;

  5. SeqScan 被再次調(diào)用時(shí),也會(huì)重置 tuple slot。

    因?yàn)槭?physical tuple,它需要釋放之前的 Buffer。

    (當(dāng)然,如果一直是同一個(gè) Buffer 不會(huì)反復(fù) pin/unpin,這是存儲(chǔ)引擎的優(yōu)化)。

條件計(jì)算

Expr 和 Var

執(zhí)行器每個(gè)算子會(huì)對(duì)底下傳上來的 tuple 進(jìn)行計(jì)算和過濾。比如 NestLoop 需要計(jì)算內(nèi)外表傳上來的 tuple 是否滿足 join 條件。

這時(shí)候需要引出第二個(gè)重要的概念,表達(dá)式的抽象。個(gè)人理解,任何對(duì)數(shù)據(jù)的獲取操作都可以認(rèn)為是表達(dá)式。

Var/Const 也是表達(dá)式的一種。Var 表示直接從 tuple 中獲取數(shù)據(jù),Const 表示的是直接獲取一個(gè)常數(shù)。

每一個(gè)表達(dá)式會(huì)對(duì)應(yīng)一個(gè) ExprContext,ExprContext 中會(huì)記錄計(jì)算所需要的所有數(shù)據(jù),一般是孩子節(jié)點(diǎn)返回的 tuple。

表達(dá)式本身,在執(zhí)行器中用 ExprState 來表示。里面重點(diǎn)是表達(dá)式的計(jì)算函數(shù)

總結(jié)下,ExprState?結(jié)構(gòu)體表示表達(dá)式計(jì)算的邏輯,ExprContext結(jié)構(gòu)體表示的是表達(dá)式計(jì)算要用到的數(shù)據(jù)。

?從 OpExpr 可以看出,ExprState 本身也是一棵樹。一直遞歸調(diào)用 ExecEvalExpr 來獲取最終的結(jié)果。

需要注意的是,執(zhí)行樹中除了葉子節(jié)點(diǎn)上的掃描節(jié)點(diǎn),其它節(jié)點(diǎn)的數(shù)據(jù)都來源于孩子節(jié)點(diǎn)。

所以,這些計(jì)算節(jié)點(diǎn)上的 Var,不能直接指向某個(gè) table,而是需要指向的是內(nèi)表還是外表的 tuple slot。

因此,在優(yōu)化器最后的階段,set_plan_refs?函數(shù)中,會(huì)把中間節(jié)點(diǎn)的 Var 的 varno 改寫成特定的值。

而 Var 的表達(dá)式處理函數(shù)?ExecEvalScalarVar?也是根據(jù)這個(gè)信息決定去找 ExprContext 中的 哪個(gè) tuple slot。

表達(dá)式如下:

示例1?filter

以 SeqScan 為例,在優(yōu)化器階段, SeqScan 上會(huì)有一個(gè) qual,表示過濾條件。在執(zhí)行器階段會(huì)生成一個(gè)對(duì)應(yīng)的 ExprState,用于計(jì)算。

示例2??join

以 Nestloop 為例,優(yōu)化器結(jié)束的時(shí)候,join 節(jié)點(diǎn)會(huì)有一個(gè) joinqual 表示 join 條件。

示例3??index scan & index only scan

之前很多人搞不清楚這里面 index cond/ filter 是什么關(guān)系。但是,通過執(zhí)行器源碼很容易得知它們的用處。先看 IndexOnlyScan

總結(jié):

  • Index Cond 是用來做 btree 掃描的 key,定位到第一個(gè) IndexTuple。存儲(chǔ)引擎中用

  • 之后索引掃描會(huì)順著 btree 的鏈表掃描所有的葉子頁面,對(duì)葉子頁面上的每一個(gè) tuple 用 ScanKey 檢查是否滿足條件,滿足再返回

  • Filter 是在 執(zhí)行器層用,針對(duì) HeapTuple 再做一次過濾

  • IndexOnlyScan 和 Index Scan的唯一區(qū)別是,IndexOnlyScan 的HeapTuple是根據(jù)IndexTuple直接構(gòu)建的,不需要回表,其它邏輯是一樣。

  • 所以理論上講 IndexOnlyScan 不應(yīng)該出現(xiàn) filter,上述場(chǎng)景可能有改進(jìn)空間。


深入淺出openGauss的執(zhí)行器基礎(chǔ)的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
门源| 南郑县| 福泉市| 潍坊市| 贵德县| 若羌县| 聂荣县| 红原县| 枝江市| 若尔盖县| 西林县| 长宁县| 周宁县| 海原县| 团风县| 北安市| 凌海市| 乾安县| 武定县| 新泰市| 尼勒克县| 汉中市| 海伦市| 略阳县| 连平县| 大宁县| 仁布县| 新龙县| 蒙自县| 开江县| 黔东| 临江市| 兰西县| 黎川县| 宁都县| 五台县| 永寿县| 林州市| 康保县| 峨眉山市| 平原县|