通过分析Profile,可以为查询性能的调优提供参考和依据。本文介绍如何获取云数据库 SelectDB 版的查询Profile。
开启查询Profile
通过设置enable_profile
变量,开启查询Profile。更多变量详情,请参见变量管理。
SET enable_profile=true;
开启查询Profile后,执行查询时,SelectDB会产生该查询的一个Profile。
Profile包含了一个查询在各个节点上的具体执行情况,有助于分析查询瓶颈。
查看查询Profile
通过以下指令查看当前保存的所有的查询Profile。
SelectDB 4.0.0版本及后续版本将不再支持
SHOW QUERY PROFILE
命令。SelectDB 4.0.0版本及后续版本您可以使用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 版3.0.4及以上版本。
打开查询Profile。
SET enable_profile=true;
执行查询。
以下为查询示例,请替换为您的实际 SQL 查询。
-- 发送SQL请求,此时查询的Profile信息会被记录 SELECT count(1) FROM test_table LIMIT 10;
获取Profile ID。
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() | +-----------------------------------+-----------+---------------------+---------------------+-------+------------+-------+------------------------------------+-------------------------------+
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 }
通过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窗口或整个分区,用于分析函数计算。 |
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的限制量。 |