通過分析Profile,可以為查詢效能的調優提供參考和依據。本文介紹如何擷取ApsaraDB for SelectDB的查詢Profile。
開啟查詢Profile
通過設定enable_profile變數,開啟查詢Profile。更多變數詳情,請參見查詢變數。
SET enable_profile=true;開啟查詢Profile後,執行查詢時,SelectDB會產生該查詢的一個Profile。
Profile包含了一個查詢在各個節點上的具體執行情況,有助於分析查詢瓶頸。
查看查詢Profile
通過以下指令查看當前儲存的所有的查詢Profile。
僅SelectDB版本號碼小於4.0.0的執行個體支援此指令。如果版本號碼大於等於4.0.0,推薦您使用可視化介面預覽或下載Profile資訊。具體操作,請參見查詢審計。您也可通過HTTP介面擷取查詢Profile資訊。具體操作,請參見匯出查詢Profile。
SHOW QUERY PROFILE "/"\G;查詢結果集中,每行對應一個查詢。您可以根據Profile ID,查看對應查詢的Profile具體資訊。
在Profile相關的HTTP介面中,存在Query ID、query_id等描述,他們都代表Profile ID。
SHOW QUERY PROFILE "/"\G
*************************** 1. row ***************************
Profile ID: c257c52f93e149ee-ace8ac14e8c9fef9
Task Type: QUERY
Start Time: 2021-04-08 11:30:50
End Time: 2021-04-08 11:30:50
Total: 9ms
Task State: EOF
User: root
Default Db: default_cluster:db1
Sql Statement: select tbl1.k1, sum(tbl1.k2) from tbl1 join tbl2 on tbl1.k1 = tbl2.k1 group by tbl1.k1 order by tbl1.k1使用查詢Profile
您可以通過以下三個步驟,使用查詢Profile的資訊,逐步排查一個SQL的效能瓶頸。
查看整體執行計畫樹。
這一步主要用於從整體分析執行計畫,並查看每個Fragment的執行耗時。查詢結果中每個節點都標註了自己所屬的Fragment,並且在每個Fragment的Sender節點標註了該Fragment的執行耗時。這個耗時是Fragment下所有執行單元的執行耗時中最長的一個。該統計報告有助於從整體角度發現最耗時的Fragment,查詢樣本如下。
SHOW QUERY PROFILE "/c257c52f93e149ee-ace8ac14e8c9fef9"\G *************************** 1. row *************************** Fragments: ┌──────────────────────┐ │[-1: DataBufferSender]│ │Fragment: 0 │ │MaxActiveTime: 6.626ms│ └──────────────────────┘ │ ┌──────────────────┐ │[9: EXCHANGE_NODE]│ │Fragment: 0 │ └──────────────────┘ │ ┌──────────────────────┐ │[9: DataStreamSender] │ │Fragment: 1 │ │MaxActiveTime: 5.449ms│ └──────────────────────┘ │ ┌──────────────┐ │[4: SORT_NODE]│ │Fragment: 1 │ └──────────────┘ ┌┘ ┌─────────────────────┐ │[8: AGGREGATION_NODE]│ │Fragment: 1 │ └─────────────────────┘ └┐ ┌──────────────────┐ │[7: EXCHANGE_NODE]│ │Fragment: 1 │ └──────────────────┘ │ ┌──────────────────────┐ │[7: DataStreamSender] │ │Fragment: 2 │ │MaxActiveTime: 3.505ms│ └──────────────────────┘ ┌┘ ┌─────────────────────┐ │[3: AGGREGATION_NODE]│ │Fragment: 2 │ └─────────────────────┘ │ ┌───────────────────┐ │[2: HASH_JOIN_NODE]│ │Fragment: 2 │ └───────────────────┘ ┌────────────┴────────────┐ ┌──────────────────┐ ┌──────────────────┐ │[5: EXCHANGE_NODE]│ │[6: EXCHANGE_NODE]│ │Fragment: 2 │ │Fragment: 2 │ └──────────────────┘ └──────────────────┘ │ │ ┌─────────────────────┐ ┌────────────────────────┐ │[5: DataStreamSender]│ │[6: DataStreamSender] │ │Fragment: 4 │ │Fragment: 3 │ │MaxActiveTime: 1.87ms│ │MaxActiveTime: 636.767us│ └─────────────────────┘ └────────────────────────┘ │ ┌┘ ┌───────────────────┐ ┌───────────────────┐ │[0: OLAP_SCAN_NODE]│ │[1: OLAP_SCAN_NODE]│ │Fragment: 4 │ │Fragment: 3 │ └───────────────────┘ └───────────────────┘ │ │ ┌─────────────┐ ┌─────────────┐ │[OlapScanner]│ │[OlapScanner]│ │Fragment: 4 │ │Fragment: 3 │ └─────────────┘ └─────────────┘ │ │ ┌─────────────────┐ ┌─────────────────┐ │[SegmentIterator]│ │[SegmentIterator]│ │Fragment: 4 │ │Fragment: 3 │ └─────────────────┘ └─────────────────┘ 1 row in set (0.02 sec)查看具體Fragment下的執行單元列表。
例如上述樣本中Fragment1耗時最長,您可以繼續查看Fragment1的執行單元列表。查詢中Fragment 1上所有的3個執行單元所在的執行節點和耗時,查詢樣本如下。
SHOW QUERY PROFILE "/c257c52f93e149ee-ace8ac14e8c9fef9/1"; +-----------------------------------+-------------------+------------+ | Instances | Host | ActiveTime | +-----------------------------------+-------------------+------------+ | c257c52f93e149ee-ace8ac14e8c9ff03 | 10.200.00.01:9060 | 5.449ms | | c257c52f93e149ee-ace8ac14e8c9ff05 | 10.200.00.02:9060 | 5.367ms | | c257c52f93e149ee-ace8ac14e8c9ff04 | 10.200.00.03:9060 | 5.358ms | +-----------------------------------+-------------------+------------+查看具體執行單元。
您可以繼續查看某一個具體的執行單元上各個運算元的詳細Profile。例如查詢Fragment1中,執行單元c257c52f93e149ee-ace8ac14e8c9ff03的各個運算元的具體Profile,查詢樣本如下。
SHOW QUERY PROFILE "/c257c52f93e149ee-ace8ac14e8c9fef9/1/c257c52f93e149ee-ace8ac14e8c9ff03"\G *************************** 1. row *************************** Instance: ┌───────────────────────────────────────┐ │[9: DataStreamSender] │ │(Active: 37.222us, non-child: 0.40) │ │ - Counters: │ │ - BytesSent: 0.00 │ │ - IgnoreRows: 0 │ │ - OverallThroughput: 0.0 /sec │ │ - PeakMemoryUsage: 8.00 KB │ │ - SerializeBatchTime: 0ns │ │ - UncompressedRowBatchSize: 0.00 │ └───────────────────────────────────────┘ └┐ │ ┌──────────────────────────────────┐ │[4: SORT_NODE] │ │(Active: 5.421ms, non-child: 0.71)│ │ - Counters: │ │ - PeakMemoryUsage: 12.00 KB │ │ - RowsReturned: 0 │ │ - RowsReturnedRate: 0 │ └──────────────────────────────────┘ ┌┘ │ ┌───────────────────────────────────┐ │[8: AGGREGATION_NODE] │ │(Active: 5.355ms, non-child: 10.68)│ │ - Counters: │ │ - BuildTime: 3.701us │ │ - GetResultsTime: 0ns │ │ - HTResize: 0 │ │ - HTResizeTime: 1.211us │ │ - HashBuckets: 0 │ │ - HashCollisions: 0 │ │ - HashFailedProbe: 0 │ │ - HashFilledBuckets: 0 │ │ - HashProbe: 0 │ │ - HashTravelLength: 0 │ │ - LargestPartitionPercent: 0 │ │ - MaxPartitionLevel: 0 │ │ - NumRepartitions: 0 │ │ - PartitionsCreated: 16 │ │ - PeakMemoryUsage: 34.02 MB │ │ - RowsProcessed: 0 │ │ - RowsRepartitioned: 0 │ │ - RowsReturned: 0 │ │ - RowsReturnedRate: 0 │ │ - SpilledPartitions: 0 │ └───────────────────────────────────┘ └┐ │ ┌──────────────────────────────────────────┐ │[7: EXCHANGE_NODE] │ │(Active: 4.360ms, non-child: 46.84) │ │ - Counters: │ │ - BytesReceived: 0.00 │ │ - ConvertRowBatchTime: 387ns │ │ - DataArrivalWaitTime: 4.357ms │ │ - DeserializeRowBatchTimer: 0ns │ │ - FirstBatchArrivalWaitTime: 4.356ms│ │ - PeakMemoryUsage: 0.00 │ │ - RowsReturned: 0 │ │ - RowsReturnedRate: 0 │ │ - SendersBlockedTotalTimer(*): 0ns │ └──────────────────────────────────────────┘
匯出查詢Profile
如果您需要匯出查詢Profile的資訊以便進行分析,可以按照以下步驟操作。
如果SelectDB版本號碼大於等於4.0.0,推薦您使用可視化介面預覽或下載Profile資訊。具體操作,請參見查詢審計。僅SelectDB版本號碼小於4.0.0的執行個體,採用如下指令方式匯出查詢Profile。
開啟查詢Profile。
SET enable_profile=true;執行查詢。
以下為查詢樣本,請替換為您的實際 SQL 查詢。
-- 發送SQL請求,此時查詢的Profile資訊會被記錄 SELECT count(1) FROM test_table LIMIT 10;擷取Profile ID。
SelectDB 4.0.x版本
SelectDB 4.0版本及後續版本擷取樣本,其中
Query ID即Profile ID。curl -u'<userName>:<userPassword>' "http://<selectdbAddress>:<httpPort>/rest/v2/manager/query/query_info?is_all_node=true" # Response { "msg": "success", "code": 0, "data": { "column_names": [ "Query ID", "FE Node", "Query User", "Execution Database", "Sql", "Query Type", "Start Time", "End Time", "Execution Duration", "Status" ], "rows": [ [ ... ] ] }, "count": 0 }SelectDB 3.0.x版本
SelectDB 3.0版本擷取樣本:
SHOW QUERY PROFILE "/"; +-----------------------------------+-----------+---------------------+---------------------+-------+------------+-------+------------------------------------+-------------------------------+ | Profile ID | Task Type | Start Time | End Time | Total | Task State | User | Default Db | Sql Statement | +-----------------------------------+-----------+---------------------+---------------------+-------+------------+-------+------------------------------------+-------------------------------+ | b9c9ba063d9d4365-97878361371e757a | QUERY | 2024-02-07 17:40:04 | 2024-02-07 17:40:04 | 32ms | EOF | admin | default_cluster:information_schema | select * from user_privileges | | 8800c306137e4072-9bb1ed419f4ac9f2 | QUERY | 2024-02-07 17:40:00 | 2024-02-07 17:40:00 | 3ms | ERR | admin | default_cluster:information_schema | select * from user_priveleges | | e2efdd1a996c4de2-ab939ad49b409990 | QUERY | 2024-02-07 17:39:51 | 2024-02-07 17:39:51 | 13ms | EOF | admin | | SELECT DATABASE() | +-----------------------------------+-----------+---------------------+---------------------+-------+------------+-------+------------------------------------+-------------------------------+通過curl擷取Profile ID對應的Profile資訊。
curl -u'<userName>:<userPassword>' "http://<selectdbAddress>:<httpPort>/api/profile?query_id=<query_id>" # 可使用重新導向符號輸出到檔案 curl -u'<userName>:<userPassword>' "http://<selectdbAddress>:<httpPort>/api/profile?query_id=<query_id>" > res.txt參數說明
參數名稱
參數說明
userName
SelectDB使用者名稱。
userPassword
SelectDB密碼。
selectdbAddress
SelectDB串連地址。
httpPort
SelectDB的HTTP連接埠,預設為8080。
query_id
查詢語句的Profile ID。
Profile參數解析
以下對收集的統計資訊參數進行說明。
Fragment
參數名稱 | 參數說明 |
AverageThreadTokens | 執行Fragment使用線程數目,不包含線程池的使用方式。 |
Buffer Pool PeakReservation | Buffer Pool使用的記憶體的峰值。 |
MemoryLimit | 查詢時的記憶體限制。 |
PeakMemoryUsage | 整個執行單元在查詢時記憶體使用量的峰值。 |
RowsProduced | 處理列的行數。 |
BlockMgr
參數名稱 | 參數說明 |
BlocksCreated | BlockMgr建立的Blocks數目。 |
BlocksRecycled | 重用的Blocks數目。 |
BytesWritten | 總的落盤寫資料量。 |
MaxBlockSize | 單個Block的大小。 |
TotalReadBlockTime | 讀Block的總耗時。 |
DataStreamSender
參數名稱 | 參數說明 |
BytesSent | 發送的總資料量 = 接受者×發送資料量。 |
IgnoreRows | 過濾的行數。 |
LocalBytesSent | 記錄資料在Exchange過程中,本機節點的自發自收資料量。 |
OverallThroughput | 總的輸送量 = BytesSent/時間。 |
SerializeBatchTime | 發送資料序列化消耗的時間。 |
UncompressedRowBatchSize | 發送資料壓縮前的RowBatch的大小。 |
ODBC_TABLE_SINK
參數名稱 | 參數說明 |
NumSentRows | 寫入外表的總行數。 |
TupleConvertTime | 發送資料序列化為Insert語句的耗時。 |
ResultSendTime | 通過ODBC Driver寫入的耗時。 |
EXCHANGE_NODE
參數名稱 | 參數說明 |
BytesReceived | 通過網路接收的資料量大小。 |
MergeGetNext | 當下層節點存在排序時,會在EXCHANGE NODE進行統一的歸併排序,輸出有序結果。該指標記錄了Merge排序的總耗時,包含了MergeGetNextBatch耗時。 |
MergeGetNextBatch | Merge節點取資料的耗時。如果為單層Merge排序,則取資料的對象為網路隊列;若為多層Merge排序,則取資料對象為Child Merger。 |
ChildMergeGetNext | 當下層的發送資料的Sender過多時,單線程的Merge會成為效能瓶頸,SelectDB會啟動多個Child Merge線程並行歸併排序。該數值記錄了Child Merge的排序耗時,是多個線程的累加值。 |
ChildMergeGetNextBatch | Child Merge節點從取資料的耗時,如果耗時過大,說明下層的資料發送節點可能存在瓶頸。 |
DataArrivalWaitTime | 等待Sender發送資料的總時間。 |
FirstBatchArrivalWaitTime | 等待第一個Batch從Sender擷取的時間。 |
DeserializeRowBatchTimer | 還原序列化網路資料的耗時。 |
SendersBlockedTotalTimer(*) | DataStreamRecv的隊列的記憶體被打滿,Sender端等待的耗時。 |
ConvertRowBatchTime | 接收資料轉為RowBatch的耗時。 |
RowsReturned | 接收行的數目。 |
RowsReturnedRate | 接收行的速率。 |
SORT_NODE
參數名稱 | 參數說明 |
InMemorySortTime | 記憶體中排序的耗時。 |
InitialRunsCreated | 初始化排序的趟數(若為記憶體內排序,則該數為1)。 |
SortDataSize | 總的排序資料量。 |
MergeGetNext | MergeSort從多個sort_run擷取下一個Batch的耗時(僅在落盤時計時)。 |
MergeGetNextBatch | MergeSort提取下一個sort_run的Batch的耗時(僅在落盤時計時)。 |
TotalMergesPerformed | 進行外排Merge的次數。 |
AGGREGATION_NODE
參數名稱 | 參數說明 |
PartitionsCreated | 彙總查詢拆分成Partition的個數。 |
GetResultsTime | 從各個partition之中擷取彙總結果的時間。 |
HTResizeTime | HashTable進行resize消耗的時間。 |
HTResize | HashTable進行resize的次數。 |
HashBuckets | HashTable中Buckets的個數。 |
HashBucketsWithDuplicate | HashTable有DuplicateNode的Buckets的個數。 |
HashCollisions | HashTable產生雜湊衝突的次數。 |
HashDuplicateNodes | HashTable出現Buckets相同DuplicateNode的個數。 |
HashFailedProbe | HashTable Probe操作失敗的次數。 |
HashFilledBuckets | HashTable填入資料的Buckets數目。 |
HashProbe | HashTable查詢的次數。 |
HashTravelLength | HashTable查詢時移動的步數。 |
HASH_JOIN_NODE
參數名稱 | 參數說明 |
ExecOption | 對右子節點構造HashTable的方式(同步or非同步),Join中右子節點可能是表或子查詢,左子節點同理。 |
BuildBuckets | HashTable中Buckets的個數。 |
BuildRows | HashTable的行數。 |
BuildTime | 構造HashTable的耗時。 |
LoadFactor | HashTable的負載因子(即非空Buckets的數量)。 |
ProbeRows | 遍曆左子節點進行Hash Probe的行數。 |
ProbeTime | 遍曆左子節點進行Hash Probe的耗時,不包括對左子節點RowBatch調用GetNext的耗時。 |
PushDownComputeTime | 謂詞下推條件計算耗時。 |
PushDownTime | 謂詞下推的總耗時,Join時對滿足要求的右子節點,轉為左子節點的in查詢。 |
CROSS_JOIN_NODE
參數名稱 | 參數說明 |
ExecOption | 對右子節點構造RowBatchList的方式(同步or非同步)。 |
BuildRows | RowBatchList的行數(即右子節點的行數)。 |
BuildTime | 構造RowBatchList的耗時。 |
LeftChildRows | 左子節點的行數。 |
LeftChildTime | 遍曆左子節點和右子節點求笛卡爾積的耗時,不包括對左子節點RowBatch調用GetNext的耗時。 |
UNION_NODE
參數名稱 | 參數說明 |
MaterializeExprsEvaluateTime | Union兩端欄位類型不一致時,類型轉換運算式計算及物化結果的耗時。 |
ANALYTIC_EVAL_NODE
參數名稱 | 參數說明 |
EvaluationTime | 分析函數(視窗函數)計算總耗時。 |
GetNewBlockTime | 初始化時申請一個新的Block的耗時,Block用來緩衝Rows視窗或整個分區,用於分析Function Compute。 |
PinTime | 後續申請新的Block或將寫入磁碟的Block重新讀取回記憶體的耗時。 |
UnpinTime | 對暫不需要使用的Block或當前操作符記憶體壓力大時,將Block的資料刷入磁碟的耗時。 |
OLAP_SCAN_NODE
OLAP_SCAN_NODE節點負責具體的資料掃描任務。一個OLAP_SCAN_NODE會產生一個或多個OlapScanner。每個Scanner線程負責掃描部分資料。
查詢中的部分或全部謂詞條件會推送給OLAP_SCAN_NODE。這些謂詞條件中一部分會繼續下推給儲存引擎,以便利用儲存引擎的索引進行資料過濾。另一部分會保留在OLAP_SCAN_NODE中,用於過濾從儲存引擎中返回的資料。
OLAP_SCAN_NODE節點的Profile通常用於分析資料掃描的效率,依據調用關係分為OLAP_SCAN_NODE、OlapScanner、SegmentIterator三層。
一個典型的OLAP_SCAN_NODE節點的Profile如下。部分指標會因儲存格式的不同(V1或V2)而有不同含義。
OLAP_SCAN_NODE (id=0):(Active: 1.2ms, % non-child: 0.00%)
- BytesRead: 265.00 B # 從資料檔案中讀取到的資料量。假設讀取到了是10個32位整型,則資料量為 10 * 4B = 40 Bytes。這個資料僅表示資料在記憶體中全展開的大小,並不代表實際的 IO 大小。
- NumDiskAccess: 1 # 該 ScanNode 節點涉及到的磁碟數量。
- NumScanners: 20 # 該 ScanNode 產生的 Scanner 數量。
- PeakMemoryUsage: 0.00 # 查詢時記憶體使用量的峰值,暫未使用
- RowsRead: 7 # 從儲存引擎返回到 Scanner 的行數,不包括經 Scanner 過濾的行數。
- RowsReturned: 7 # 從 ScanNode 返回給上層節點的行數。
- RowsReturnedRate: 6.979K /sec # RowsReturned/ActiveTime
- TabletCount : 20 # 該 ScanNode 涉及的 Tablet 數量。
- TotalReadThroughput: 74.70 KB/sec # BytesRead除以該節點啟動並執行總時間(從Open到Close),對於IO受限的查詢,接近磁碟的總輸送量。
- ScannerBatchWaitTime: 426.886us # 用於統計transfer 線程等待scaner 線程返回rowbatch的時間。
- ScannerWorkerWaitTime: 17.745us # 用於統計scanner thread 等待線程池中可用背景工作執行緒的時間。
OlapScanner:
- BlockConvertTime: 8.941us # 將向量化Block轉換為行結構的 RowBlock 的耗時。向量化 Block 在 V1 中為 VectorizedRowBatch,V2中為 RowBlockV2。
- BlockFetchTime: 468.974us # Rowset Reader 擷取 Block 的時間。
- ReaderInitTime: 5.475ms # OlapScanner 初始化 Reader 的時間。V1 中包括組建 MergeHeap 的時間。V2 中包括產生各級 Iterator 並讀取第一組Block的時間。
- RowsDelFiltered: 0 # 包括根據 Tablet 中存在的 Delete 資訊過濾掉的行數,以及 unique key 模型下對被標記的刪除行過濾的行數。
- RowsPushedCondFiltered: 0 # 根據傳遞下推的謂詞過濾掉的條件,比如 Join 計算中從 BuildTable 傳遞給 ProbeTable 的條件。該數值不準確,因為如果過濾效果差,就不再過濾了。
- ScanTime: 39.24us # 從 ScanNode 返回給上層節點的時間。
- ShowHintsTime_V1: 0ns # V2 中無意義。V1 中讀取部分資料來進行 ScanRange 的切分。
SegmentIterator:
- BitmapIndexFilterTimer: 779ns # 利用 bitmap 索引過濾資料的耗時。
- BlockLoadTime: 415.925us # SegmentReader(V1) 或 SegmentIterator(V2) 擷取 block 的時間。
- BlockSeekCount: 12 # 讀取 Segment 時進行 block seek 的次數。
- BlockSeekTime: 222.556us # 讀取 Segment 時進行 block seek 的耗時。
- BlocksLoad: 6 # 讀取 Block 的數量
- CachedPagesNum: 30 # 僅 V2 中,當開啟 PageCache 後,命中 Cache 的 Page 數量。
- CompressedBytesRead: 0.00 # V1 中,從檔案中讀取的解壓前的資料大小。V2 中,讀取到的沒有命中 PageCache 的 Page 的壓縮前的大小。
- DecompressorTimer: 0ns # 資料解壓耗時。
- IOTimer: 0ns # 實際從作業系統讀取資料的 IO 時間。
- IndexLoadTime_V1: 0ns # 僅 V1 中,讀取 Index Stream 的耗時。
- NumSegmentFiltered: 0 # 在產生 Segment Iterator 時,通過列統計資訊和查詢條件,完全過濾掉的 Segment 數量。
- NumSegmentTotal: 6 # 查詢涉及的所有 Segment 數量。
- RawRowsRead: 7 # 儲存引擎中讀取的原始行數。詳情見下文。
- RowsBitmapIndexFiltered: 0 # 僅 V2 中,通過 Bitmap 索引過濾掉的行數。
- RowsBloomFilterFiltered: 0 # 僅 V2 中,通過 BloomFilter 索引過濾掉的行數。
- RowsKeyRangeFiltered: 0 # 僅 V2 中,通過 SortkeyIndex 索引過濾掉的行數。
- RowsStatsFiltered: 0 # V2 中,通過 ZoneMap 索引過濾掉的行數,包含刪除條件。V1 中還包含通過 BloomFilter 過濾掉的行數。
- RowsConditionsFiltered: 0 # 僅 V2 中,通過各種列索引過濾掉的行數。
- RowsVectorPredFiltered: 0 # 通過向量化條件過濾操作過濾掉的行數。
- TotalPagesNum: 30 # 僅 V2 中,讀取的總 Page 數量。
- UncompressedBytesRead: 0.00 # V1 中為讀取的資料檔案解壓後的大小(如果檔案無需解壓,則直接統計檔案大小)。V2 中,僅統計未命中 PageCache 的 Page 解壓後的大小(如果Page無需解壓,直接統計Page大小)
- VectorPredEvalTime: 0ns # 向量化條件過濾操作的耗時。
- ShortPredEvalTime: 0ns # 短路謂詞過濾操作的耗時。
- PredColumnReadTime: 0ns # 謂詞列讀取的耗時。
- LazyReadTime: 0ns # 非謂詞列讀取的耗時。
- OutputColumnTime: 0ns # 物化列的耗時。通過Profile中資料行數相關指標可以推斷謂詞條件下推和索引使用方式。如下對Segment V2格式資料讀取流程中的Profile進行說明。Segment V1格式中,這些指標的含義略有不同。
Init階段
當讀取一個V2格式的Segment時,若查詢存在key_ranges(首碼key組成的查詢範圍),首先通過SortkeyIndex索引過濾資料,過濾的行數記錄在
RowsKeyRangeFiltered。對查詢條件中含有Bitmap索引的列,使用Bitmap索引進行精確過濾,過濾的行數記錄在
RowsBitmapIndexFiltered。按查詢條件中的等值
(eq,in,is)條件,使用BloomFilter索引過濾資料,記錄在RowsBloomFilterFiltered。RowsBloomFilterFiltered的值是Segment的總行數(而不是Bitmap索引過濾後的行數)和經過BloomFilter過濾後剩餘行數的差值,因此BloomFilter過濾的資料可能會和Bitmap過濾的資料有重疊。按查詢條件和刪除條件,使用ZoneMap索引過濾資料,記錄在
RowsStatsFiltered。RowsConditionsFiltered是各種索引過濾的行數,包含了RowsBloomFilterFiltered和RowsStatsFiltered的值。
Next階段
Next階段刪除條件過濾的行數,記錄在
RowsDelFiltered。因該刪除條件實際過濾的行數,分別記錄在RowsStatsFiltered和RowsDelFiltered中。RawRowsRead是經過上述過濾後,最終需要讀取的行數。RowsRead是最終返回給Scanner的行數。RowsRead通常小於RawRowsRead,這是因為從儲存引擎返回到Scanner可能會經過一次資料彙總。如果RawRowsRead和RowsRead差距較大,則說明大量的行被彙總,而彙總可能比較耗時。RowsReturned是ScanNode最終返回給上層節點的行數。RowsReturned通常也會小於RowsRead。因為在Scanner上會有一些沒有下推給儲存引擎的謂詞條件,這通常意味著一次過濾。如果RowsRead和RowsReturned差距較大,則意味著Scanner中進行了大批量的行過濾。這說明很多選擇度高的謂詞條件並沒有推送給儲存引擎,而在Scanner中的過濾效率會比在儲存引擎中過濾效率差。
通過以上指標,可以大致分析出儲存引擎處理的行數以及最終過濾後的結果行數大小。通過Rows***Filtered這組指標,也可以分析查詢條件是否下推到了儲存引擎,以及不同索引的過濾效果。
其他指標
還可以通過如下幾個指標進行分析。
OlapScanner下的很多指標,如IOTimer,BlockFetchTime等是所有Scanner線程指標的累加,因此數值可能會比較大。並且因為Scanner線程非同步讀取資料,所以這些累加指標只能反映Scanner累加的工作時間,並不直接代表ScanNode的耗時。ScanNode在整個查詢計劃中的耗時佔比為Active欄位記錄的值。有時會出現例如IOTimer有幾十秒,而Active實際只有幾秒鐘。這種情況可能有以下幾個原因。IOTimer為多個Scanner的累加時間,而Scanner數量較多。上層節點比較耗時。例如上層節點耗時100秒,而底層ScanNode只需10秒。則反映在
Active的欄位可能只有幾毫秒。因為在上層處理資料的同時,ScanNode已經非同步進行了資料掃描並準備好了資料。當上層節點從ScanNode擷取資料時,可以擷取到已經準備好的資料,因此Active時間很短。
NumScanners表示Scanner提交到線程池的Task個數,由RuntimeState中的線程池調度。doris_scanner_thread_pool_thread_num和doris_scanner_thread_pool_queue_size兩個參數分別控制線程池的大小和隊列長度。線程數過多或過少都會影響查詢效率。同時可以用一些匯總指標除以線程數來大致的估算每個線程的耗時。TabletCount表示需要掃描的Tablet數量。數量過多可能意味著需要大量的隨機讀取和資料合併操作。UncompressedBytesRead間接反映了讀取的資料量。如果該數值較大,說明可能有大量的IO操作。CachedPagesNum和TotalPagesNum可以查看命中PageCache的情況。命中率越高,說明IO和解壓操作耗時越少。
Buffer pool
參數名稱 | 參數說明 |
AllocTime | 記憶體配置耗時。 |
CumulativeAllocationBytes | 累計記憶體配置的量。 |
CumulativeAllocations | 累計的記憶體配置次數。 |
PeakReservation | Reservation的峰值。 |
PeakUnpinnedBytes | Unpin的記憶體資料量。 |
PeakUsedReservation | Reservation的記憶體使用量量。 |
ReservationLimit | BufferPool的Reservation的限制量。 |