問題現象
同一個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都是同樣的值,不能做加減等操作。