天天看點

openGauss核心分析(七):SQL by pass & 經典執行器

作者:liwt

執行引擎一般負責查詢的執行,執行引擎在SQL執行棧中起到接收優化器生成的執行計劃Plan、并對通過存儲引擎提供的資料讀寫接口,實作對資料進行計算得到查詢的結果集。

openGauss核心分析(七):SQL by pass & 經典執行器

在典型的OLTP場景中,簡單查詢占了很大一部分比例。這種查詢的特征是隻涉及單表和簡單表達式的查詢,是以為了加速這類查詢,openGauss提出了SQL by pass架構,在parse層對這類查詢做簡單的模式判别後,進入到特殊的執行路徑裡,跳過經典的執行器執行架構,包括算子的初始化與執行、表達式與投影等經典架構,直接重寫一套簡潔的執行路徑,并且直接調用存儲接口,這樣可以大大加速簡單查詢的執行速度。

SQL by pass

enable_opfusion用于控制是否對簡單增删改查進行優化,簡單insert語句在開啟enable_opfusion時的執行計劃如下

openGauss核心分析(七):SQL by pass & 經典執行器

由于開啟SQL BY PASS,從exec_simple_query過來的語句,會判斷可以走SQL BY PASS,否則進入CreatePortal走經典執行流程。

static void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg = NULL)

{

/* SQL bypass */

if (runOpfusionCheck) { // 進入SQL by pass

(void)MemoryContextSwitchTo(oldcontext);

void* opFusionObj = OpFusion::FusionFactory(

OpFusion::getFusionType(NULL, NULL, plantree_list), oldcontext, NULL, plantree_list, NULL);

if (opFusionObj != NULL) {

((OpFusion*)opFusionObj)->setCurrentOpFusionObj((OpFusion*)opFusionObj);

if (OpFusion::process(FUSION_EXECUTE, NULL, completionTag, isTopLevel, NULL)) {

CommandCounterIncrement();

finish_xact_command();

EndCommand(completionTag, dest);

MemoryContextReset(OptimizerContext);

break;

}

Assert(0);

}

(void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt);

}

/*

* Create unnamed portal to run the query or queries in. If there

* already is one, silently drop it.

*/

portal = CreatePortal("", true, true); // 經典執行流程

}

進入InsertFusion::execute完成資料插入操作。

#0 InsertFusion::execute (this=0x7fd93a4104f8, max_rows=9223372036854775807, completionTag=0x7fd933e67020 "@p\346\063\331\177")

at opfusion_insert.cpp:297

#1 0x0000000001ac00d9 in OpFusion::fusionExecute (this=0x7fd93a4104f8, msg=0x0, completionTag=0x7fd933e67020 "@p\346\063\331\177",

isTopLevel=true, isQueryCompleted=0x0) at opfusion.cpp:453

#2 0x0000000001ac0389 in OpFusion::process (op=0, msg=0x0, completionTag=0x7fd933e67020 "@p\346\063\331\177", isTopLevel=true,

isQueryCompleted=0x0) at opfusion.cpp:491

#3 0x000000000193a910 in exec_simple_query (query_string=0x7fd966ad2060 "insert into t1 values(1,200);",

messageType=QUERY_MESSAGE, msg=0x7fd933e67210) at postgres.cpp:2624

SQL by pass适應的場景有:

Ÿ隻支援indexscan和indexonlyscan,且全部WHERE語句的過濾條件都在索引上。

Ÿ隻支援單表增删改查,不支援join、using。

Ÿ隻支援行存表,不支援分區表,表不支援有觸發器。

Ÿ不支援active sql、QPS等資訊統計特性。

Ÿ不支援正在擴容和縮容的表。

Ÿ不支援查詢或者修改系統列。

Ÿ隻支援簡單SELECT語句,例如

SELECT c3 FROM t1 WHERE c1 = ? and c2 =10;

僅可以查詢目标表的列,c1和c2列為索引列,後邊可以是常量或者參數,可以使用 for update。

Ÿ隻支援簡單INSERT語句,例如:

INSERT INTO t1 VALUES (?,10,?);

僅支援一個VALUES,VALUES裡面的類型可以是常量和參數,不支援returning。

Ÿ隻支援簡單DELETE語句,例如:

DELETE FROM t1 WHERE c1 = ? and c2 = 10;

c1和c2列為索引列,後邊可以是常量或者參數。

Ÿ隻支援簡單UPDATE語句,例如

UPDATE t1 SET c3 = c3+? WHERE c1 = ? and c2 = 10;

c3列修改的值可以是常量和參數,也可以是一個簡單的表達式,c1和c2列為索引列,後邊可以是常量或者參數。

經典的執行器

關閉enable_opfusion,簡單insert的執行計劃是這樣的

openGauss核心分析(七):SQL by pass & 經典執行器

在這種執行流程中Portal是執行SQL語句的載體,每一條SQL對應唯一的Portal,不同的查詢類型對應的Portal類型也有差別。

typedef enum PortalStrategy {

PORTAL_ONE_SELECT, // SQL語句包含單一的SELECT查詢

PORTAL_ONE_RETURNING, // INSERT/UPDATE/DELETE語句包含Returning

PORTAL_ONE_MOD_WITH, // 查詢語句包含With

PORTAL_UTIL_SELECT, // 工具類型查詢語句,如explain

PORTAL_MULTI_QUERY // 所有其他類型查詢語句

} PortalStrategy;

Portal的生命周期管理在exec_simple_query函數中實作,該函數負責Portal建立、執行和清理。Portal執行的主要執行流程包括PortalStart函數、PortalRun函數、PortalDrop函數幾個部分。其中PortalStart函數負責進行Portal結構體初始化工作,包括執行算子初始化、記憶體上下文配置設定等;PortalRun函數負責真正的執行和運算,它是執行器的核心;PortalDrop函數負責最後的清理工作,主要是資料結構、緩存的清理。

openGauss核心分析(七):SQL by pass & 經典執行器

PortalRun函數根據查詢類型進入不同的處理函數

bool PortalRun(

Portal portal, long count, bool isTopLevel, DestReceiver* dest, DestReceiver* altdest, char* completionTag)

{

switch (portal->strategy) {

case PORTAL_ONE_SELECT:

case PORTAL_MULTI_QUERY: // insert從這裡進入

PortalRunMulti(portal, isTopLevel, dest, altdest, completionTag);

/* Prevent portal's commands from being re-executed */

MarkPortalDone(portal);

/* Always complete at end of RunMulti */

result = true;

break;

}

最終執行ExecInsertT完成資料插入。

#0 ExecInsertT (state=0x7fdbf1836060, slot=0x7fdbf0c86460, planSlot=0x7fdbf0c86460, estate=0x7fdbf0c74060, canSetTag=true,

options=0, partitionList=0x7fdbf3125860) at nodeModifyTable.cpp:800

#1 0x0000000001a684cd in ExecModifyTable (node=0x7fdbf1836060) at nodeModifyTable.cpp:3043

#2 0x00000000019f3f93 in ExecModifyTableWrap (node=0x7fdbf1836060) at execProcnode.cpp:785

#3 0x00000000019f43b5 in ExecProcNode (node=0x7fdbf1836060) at execProcnode.cpp:1038

#4 0x00000000019ed9d5 in ExecutePlan (estate=0x7fdbf0c74060, planstate=0x7fdbf1836060, operation=CMD_INSERT, sendTuples=false,

numberTuples=0, direction=ForwardScanDirection, dest=0x7fdbf13bb9c8, motJitContext=0x0) at execMain.cpp:2163

#5 0x00000000019ea25a in standard_ExecutorRun (queryDesc=0x7fdbf1558060, direction=ForwardScanDirection, count=0)

at execMain.cpp:608

#6 0x000000000181d6ef in explain_ExecutorRun (queryDesc=0x7fdbf1558060, direction=ForwardScanDirection, count=0)

at auto_explain.cpp:121

#7 0x00000000019e9dee in ExecutorRun (queryDesc=0x7fdbf1558060, direction=ForwardScanDirection, count=0) at execMain.cpp:486

#8 0x000000000194fed6 in ProcessQuery (plan=0x7fdbf0b7b2e0, sourceText=0x7fdbf13ba060 "insert into t1 values(1,200);", params=0x0,

isMOTTable=false, motJitContext=0x0, dest=0x7fdbf13bb9c8, completionTag=0x7fdbf3126020 "") at pquery.cpp:292

#9 0x0000000001953fa1 in PortalRunMulti (portal=0x7fdbf0c7a060, isTopLevel=true, dest=0x7fdbf13bb9c8, altdest=0x7fdbf13bb9c8,

completionTag=0x7fdbf3126020 "") at pquery.cpp:1889

#10 0x00000000019525e0 in PortalRun (portal=0x7fdbf0c7a060, count=9223372036854775807, isTopLevel=true, dest=0x7fdbf13bb9c8,

altdest=0x7fdbf13bb9c8, completionTag=0x7fdbf3126020 "") at pquery.cpp:1191

#11 0x000000000193ac65 in exec_simple_query (query_string=0x7fdbf13ba060 "insert into t1 values(1,200);",

messageType=QUERY_MESSAGE, msg=0x7fdbf3126210) at postgres.cpp:2720

以上分析了簡單insert語句的兩種執行流程,對于delete,update,select基本工作流程一緻。

繼續閱讀