问题现象
同一个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是动态表,数据都存放在内存中。
anrDedicatedServiceNoLogin
anrResponse
anrDirectExecute
anlExecute
doAnlExecute
doExecute
fetchQuery
fetchPlanWithPreProj
anlFetchPlan
doAnlFetchPlan
fetchSelectPlan
anlFetchPlan
doAnlFetchPlan
fetchTableFullScan
ankFetch
ankFtFetch
{
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,调用流程
doParseDML
anlHashSQL // 生成sql hash
anlTryReuseContext // 从 inst->sqlpool,buckets中比较已有的anlcontext,是否有同一个sql
anlCreateContext // 没有从sqlpool中找到,从ANL_MAIN_POOL中申请新的anlcontext
anlPoolInsert // anlcontext插入pool中,anlLruInsert lru插入
//第二条执行计划,调用流程
//sql的第二个plan,在anlexecute中生成。放在anlcontext的planContexts中
execExplain
replaceNewPlan
doReplaceNewPlan
anlCreateAndUseNewPlan
anlInsertPlan
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都是同样的值,不能做加减等操作。