写点什么

【YashanDB 知识库】绑定参数,同一个 sql 多个执行计划的问题

作者:YashanDB
  • 2024-07-26
    广东
  • 本文字数:2843 字

    阅读完需:约 9 分钟

问题现象

同一个 sql 有两个执行计划,是否合理?


它的 EXECUTIONS,ELAPSED_TIME 等统计信息怎么看,是独立分开的还是统一计算的?


如下图:


问题影响版本

tpcc 测试:23.2.1.100

问题的风险及影响

影响 EXECUTIONS 等 sql 统计信息的计算

问题发生原因

同一条 sql,特别是绑定参数的 sql,参数类型不同,会导致生成不同的执行计划。

分析及处理过程

例子测试分析


复现例子:


drop table if exists tmp2;create table tmp2(c1 int,c2 double,c3 int,c4 int,c5 int,c6 int,c7 int,c8 timestamp,c9 varchar(500),c10 int) ;
复制代码



如上,第二个参数是 int 和 double 时,生成了两个执行计划,有两个 plan_hash_value,如下图:



后跑几次,发现其中 executions、cpu_time 这些值共享,统计到一起,所以两条结果的这些字段值一样。

代码分析

select * from v$sql; 调用过程


v$sql 是动态表,数据都存放在内存中。


anrDedicatedServiceNoLoginanrResponseanrDirectExecuteanlExecutedoAnlExecutedoExecutefetchQueryfetchPlanWithPreProjanlFetchPlandoAnlFetchPlanfetchSelectPlananlFetchPlandoAnlFetchPlanfetchTableFullScanankFetchankFtFetch{ftSqlFetch //获取一条v$sql数据ankExecFilter //filter筛选}
复制代码


ftsqlFetch 数据:


  • 每一条 sql 都是一个 anlcontext 对象,存放在 sqlpool 中。

  • anlGetNextNewPlan(stmt, ctxCursor),通过这个接口从 sqlpool 中,根据 poolid 和 bucketid 从 sqlpool 中取的 anlcontext,根据 anlcontext 的 plan 取数据。

  • 一个 plan 一条数据,每条数据的 plan_hash_value 都不一样。

  • 统计数据存放在 anlcontext 中的 stats:iostat、timestat、runstat、gcstat


所以同一条 sql 不同的 plan,stats 数据其实相同。


anlcontext 管理


sqlpool有两个:typedef enum EnAnlPoolType {    ANL_MAIN_POOL = 0, -- 普通sql pool    ANL_PL_POOL, -- pl sql pool    __ANL_POOL_TYPE_COUNT__} AnlPoolType; //生成一个新sql的anlcontext,调用流程doParseDMLanlHashSQL // 生成sql hashanlTryReuseContext // 从 inst->sqlpool,buckets中比较已有的anlcontext,是否有同一个sqlanlCreateContext // 没有从sqlpool中找到,从ANL_MAIN_POOL中申请新的anlcontextanlPoolInsert // anlcontext插入pool中,anlLruInsert lru插入 //第二条执行计划,调用流程//sql的第二个plan,在anlexecute中生成。放在anlcontext的planContexts中execExplainreplaceNewPlandoReplaceNewPlananlCreateAndUseNewPlananlInsertPlan
复制代码


pool 内存池分配方式


SGA 总内存分配接口:anrCreateGlobalArea


SGA 总内存组成方式:data buff + vm buff + large pool + redo buff + shared pool + dbwr buff + audit buff + app pool + hot cache + pq pool +job pool


globalArea:anrStartInstance 时一次性申请如下表空间数据,


typedef enum EnMzoneId {    MZONE_DATA_BUFFER = 0, // 配置参数:DATA_BUFFER_SIZE attr->dataBufSize(64M)    MZONE_TEMP_BUFFER, // 配置参数:VM_BUFFER_SIZE attr->tempBufSize (32M)    MZONE_LARGE_POOL, // 配置参数:LARGE_POOL_SIZE attr->largePoolSize(16M)    MZONE_LOG_BUFFER, // 配置参数:REDO_BUFFER_SIZE attr->logBufSize(8M)    MZONE_HOT_CACHE, // 配置(隐藏)参数:_HOT_CACHE_SIZE attr->hotCacheSize(16M)    MZONE_SHARE_POOL, // 配置参数:SHARE_POOL_SIZE attr->sharePoolSize(256M)    MZONE_APP_POOL, // 配置参数:WORK_AREA_POOL_SIZE attr->appPoolSize(16M)    MZONE_DBWR_BUFFER, // 配置参数:DBWR_BUFFER_SIZE attr->dbwrBufSize(4M)    MZONE_JOB_POOL, // 默认4M    MZONE_PQ_POOL, // 配置(隐藏)参数:PQ_POOL_SIZE attr->pqPoolSize(parallel execute buff 默认:16M)    MZONE_AUDIT_BUFFER, // 配置参数:AUDIT_QUEUE_SIZE attr->auditQueueSize(16M)    MZONE_COUNT,} MzoneId;
复制代码


shared pool 分配


分配接口:setShareBuffers


shared pool 内存拆分:sql pool + dc pool + lock pool + cursor pool 等


typedef enum EnSharePoolItemId {    SHARE_SQL_POOL = 0, // 配置(隐藏)参数:SQL_POOL_SIZE attr->sqlPoolBuf  (sqlPoolSize:百分比默认50)    SHARE_DC_POOL, //  配置(隐藏)参数:DICTIONARY_CACHE_SIZE profile->dictCache (dictCacheSize:百分比默认25)    SHARE_LOCK_POOL, //  配置(隐藏)参数:LOCK_POOL_SIZE profile->lockPool(lockPoolSize:16M)    SHARE_CURSOR_POOL, //  配置(隐藏)参数:CURSOR_POOL_SIZE profile->cursorPool(cursorPoolSize:32M)    SHARE_DSTB_POOL, //  配置(隐藏)参数:DSTB_POOL_SIZE  (分布式,dstbPoolSize:默认百分比0 )    SHARE_GCS_RESOURCE, // 集群(主备)才有  根据dataBufSize的block数计算出来    SHARE_GLS_RESOURCE, // 集群才有   根据dataBufSize的block数计算出来    SHARE_GRC_REQUEST, // 集群才有   根据lockPoolSize计算    SHARE_GCS_PASTCOPY, // 集群才有  根据 maxHandlers计算    SHARE_COUNT, // 剩余 profile->sharePool中} SharePoolItemId;
复制代码


sql pool buf 分配:


调用接口:anlCreateSQLPool


sql pool 组成:main pool buff + pl pool buff


ANL_MAIN_POOL: (9/10) * sqlpoolsize   mainPoolSize = attr->sqlPoolSize - plPoolSize;    AnlPool* mainPool = &inst->sqlPool[ANL_MAIN_POOL]; ANL_PL_POOL:(1/10) * sqlpoolsize        #define ANL_PL_POOL_MEMORY_PCT (CodUint64)10        #define ANL_PL_POOL_SIZE(totalSize) ((totalSize) * ANL_PL_POOL_MEMORY_PCT / 100)        plPoolSize = ANL_PL_POOL_SIZE(attr->sqlPoolSize)
复制代码


sql buff 分配


static void setSQLBuffers(){    AnlAttr* attr = anlGetAttr(gInstance->sql);    attr->appPoolBuf = g_MemoryZones[MZONE_APP_POOL].buffer;    attr->jobPoolBuf = g_MemoryZones[MZONE_JOB_POOL].buffer;    attr->jobPoolSize = g_MemoryZones[MZONE_JOB_POOL].size;    attr->pqPoolBuf = g_MemoryZones[MZONE_PQ_POOL].buffer;    attr->auditQueueBuf = g_MemoryZones[MZONE_AUDIT_BUFFER].buffer;}
复制代码


kernel buff 分配


static void setKernelBuffers(){    KernelAttr* profile = ankGetKernelAttr(gInstance->kernel);    profile->dataBuf = g_MemoryZones[MZONE_DATA_BUFFER].buffer;    profile->logBuf = g_MemoryZones[MZONE_LOG_BUFFER].buffer;    profile->tempBuf = g_MemoryZones[MZONE_TEMP_BUFFER].buffer;    profile->hotCache = g_MemoryZones[MZONE_HOT_CACHE].buffer;    profile->largePool = g_MemoryZones[MZONE_LARGE_POOL].buffer;    profile->dbwrBuf = g_MemoryZones[MZONE_DBWR_BUFFER].buffer;}
复制代码


缓存相关视图


经验分享

1、同一个 sql,有多个执行计划,是正常现象

2、v$sql 中的 executions、cpu_times 等一些统计字段,同一个 sql 都是同样的值,不能做加减等操作。

用户头像

YashanDB

关注

全自研国产新型大数据管理系统 2022-02-15 加入

还未添加个人简介

评论

发布
暂无评论
【YashanDB知识库】绑定参数,同一个sql多个执行计划的问题_yashandb_YashanDB_InfoQ写作社区