Perfil de Apache Doris y explicar en detalle
1. Breve introducción
Ejecute EXPLAIN + SQL en Apache Doris para obtener el plan de consulta correspondiente a SQL. Combinado con el perfil de Apache Doris, puede comprender cómo Doris maneja las declaraciones SQL, que se utiliza para analizar el cuello de botella de rendimiento de las declaraciones o estructuras de consulta, para ayudar a elegir mejores índices y escribir declaraciones de consulta más optimizadas.
2. Análisis del plan
2.1 preparación de SQL
tpcds query96.sql como ejemplo
explain
-- explain graph 生成对应执行计划图表
select count(*)
from store_sales
,household_demographics
,time_dim
, store
where ss_sold_time_sk = time_dim.t_time_sk
and ss_hdemo_sk = household_demographics.hd_demo_sk
and ss_store_sk = s_store_sk
and time_dim.t_hour = 8
and time_dim.t_minute >= 30
and household_demographics.hd_dep_count = 5
and store.s_store_name = 'ese'
order by count(*) limit 100;
2.2 explicar el análisis de resultados
El plan de consulta se puede dividir en plan de ejecución lógico (plan de consulta lógico) y plan de ejecución física (plan de consulta física). El plan de consulta actual se refiere al plan de ejecución lógico de forma predeterminada; el plan de consulta correspondiente a tpcds query96.sql se muestra a continuación.
-- graph
┌───────────────┐
│[8: ResultSink]│
│[Fragment: 4] │
│RESULT SINK │
└───────────────┘
│
│
┌─────────────┐
│[8: TOP-N] │
│[Fragment: 4]│
└─────────────┘
│
│
┌────────────────────────────────┐
│[13: AGGREGATE (merge finalize)]│
│[Fragment: 4] │
└────────────────────────────────┘
│
│
┌──────────────┐
│[12: EXCHANGE]│
│[Fragment: 4] │
└──────────────┘
│
│
┌────────────────────┐
│[12: DataStreamSink]│
│[Fragment: 0] │
│STREAM DATA SINK │
│ EXCHANGE ID: 12 │
│ UNPARTITIONED │
└────────────────────┘
│
│
┌─────────────────────────────────┐
│[7: AGGREGATE (update serialize)]│
│[Fragment: 0] │
└─────────────────────────────────┘
│
│
┌───────────────────────────────┐
│[6: HASH JOIN] │
│[Fragment: 0] │
│join op: INNER JOIN (BROADCAST)│
└───────────────────────────────┘
┌───────────┴─────────────────────────────────────┐
│ │
┌───────────────────────────────┐ ┌──────────────┐
│[4: HASH JOIN] │ │[11: EXCHANGE]│
│[Fragment: 0] │ │[Fragment: 0] │
│join op: INNER JOIN (BROADCAST)│ └──────────────┘
└───────────────────────────────┘ │
┌───────────────┴─────────────────────┐ │
│ │ ┌────────────────────┐
┌───────────────────────────────┐ ┌──────────────┐ │[11: DataStreamSink]│
│[2: HASH JOIN] │ │[10: EXCHANGE]│ │[Fragment: 3] │
│[Fragment: 0] │ │[Fragment: 0] │ │STREAM DATA SINK │
│join op: INNER JOIN (BROADCAST)│ └──────────────┘ │ EXCHANGE ID: 11 │
└───────────────────────────────┘ │ │ UNPARTITIONED │
┌─────────┴──────────┐ │ └────────────────────┘
│ │ ┌────────────────────┐ ┌┘
┌──────────────────┐ ┌─────────────┐ │[10: DataStreamSink]│ │
│[0: OlapScanNode] │ │[9: EXCHANGE]│ │[Fragment: 2] │ ┌─────────────────┐
│[Fragment: 0] │ │[Fragment: 0]│ │STREAM DATA SINK │ │[5: OlapScanNode]│
│TABLE: store_sales│ └─────────────┘ │ EXCHANGE ID: 10 │ │[Fragment: 3] │
└──────────────────┘ │ │ UNPARTITIONED │ │TABLE: store │
│ └────────────────────┘ └─────────────────┘
┌───────────────────┐ │
│[9: DataStreamSink]│ │
│[Fragment: 1] │ ┌─────────────────────────────┐
│STREAM DATA SINK │ │[3: OlapScanNode] │
│ EXCHANGE ID: 09 │ │[Fragment: 2] │
│ UNPARTITIONED │ │TABLE: household_demographics│
└───────────────────┘ └─────────────────────────────┘
│
│
┌─────────────────┐
│[1: OlapScanNode]│
│[Fragment: 1] │
│TABLE: time_dim │
└─────────────────┘
-- 非graph
PLAN FRAGMENT 0
OUTPUT EXPRS:<slot 11> <slot 10> count(*)
PARTITION: UNPARTITIONED
RESULT SINK
8:TOP-N
| order by: <slot 11> <slot 10> count(*) ASC
| offset: 0
| limit: 100
|
13:AGGREGATE (merge finalize)
| output: count(<slot 10> count(*))
| group by:
| cardinality=-1
|
12:EXCHANGE
PLAN FRAGMENT 1
OUTPUT EXPRS:
PARTITION: HASH_PARTITIONED: `default_cluster:tpcds`.`store_sales`.`ss_item_sk`, `default_cluster:tpcds`.`store_sales`.`ss_ticket_number`
STREAM DATA SINK
EXCHANGE ID: 12
UNPARTITIONED
7:AGGREGATE (update serialize)
| output: count(*)
| group by:
| cardinality=1
|
6:HASH JOIN
| join op: INNER JOIN (BROADCAST)
| hash predicates:
| colocate: false, reason: Tables are not in the same group
| equal join conjunct: `ss_store_sk` = `s_store_sk`
| runtime filters: RF000[in] <- `s_store_sk`
| cardinality=2880403
|
|----11:EXCHANGE
|
4:HASH JOIN
| join op: INNER JOIN (BROADCAST)
| hash predicates:
| colocate: false, reason: Tables are not in the same group
| equal join conjunct: `ss_hdemo_sk` = `household_demographics`.`hd_demo_sk`
| runtime filters: RF001[in] <- `household_demographics`.`hd_demo_sk`
| cardinality=2880403
|
|----10:EXCHANGE
|
2:HASH JOIN
| join op: INNER JOIN (BROADCAST)
| hash predicates:
| colocate: false, reason: Tables are not in the same group
| equal join conjunct: `ss_sold_time_sk` = `time_dim`.`t_time_sk`
| runtime filters: RF002[in] <- `time_dim`.`t_time_sk`
| cardinality=2880403
|
|----9:EXCHANGE
|
0:OlapScanNode
TABLE: store_sales
PREAGGREGATION: OFF. Reason: conjunct on `ss_sold_time_sk` which is StorageEngine value column
PREDICATES: `default_cluster:tpcds.store_sales`.`__DORIS_DELETE_SIGN__` = 0
runtime filters: RF000[in] -> `ss_store_sk`, RF001[in] -> `ss_hdemo_sk`, RF002[in] -> `ss_sold_time_sk`
partitions=1/1
rollup: store_sales
tabletRatio=3/3
tabletList=20968,20972,20976
cardinality=2880403
avgRowSize=67.95811
numNodes=3
PLAN FRAGMENT 2
OUTPUT EXPRS:
PARTITION: HASH_PARTITIONED: `default_cluster:tpcds`.`store`.`s_store_sk`
STREAM DATA SINK
EXCHANGE ID: 11
UNPARTITIONED
5:OlapScanNode
TABLE: store
PREAGGREGATION: OFF. Reason: null
PREDICATES: `store`.`s_store_name` = 'ese', `default_cluster:tpcds.store`.`__DORIS_DELETE_SIGN__` = 0
partitions=1/1
rollup: store
tabletRatio=3/3
tabletList=20773,20777,20781
cardinality=23
avgRowSize=1798.8695
numNodes=3
PLAN FRAGMENT 3
OUTPUT EXPRS:
PARTITION: HASH_PARTITIONED: `default_cluster:tpcds`.`household_demographics`.`hd_demo_sk`
STREAM DATA SINK
EXCHANGE ID: 10
UNPARTITIONED
3:OlapScanNode
TABLE: household_demographics
PREAGGREGATION: OFF. Reason: null
PREDICATES: `household_demographics`.`hd_dep_count` = 5, `default_cluster:tpcds.household_demographics`.`__DORIS_DELETE_SIGN__` = 0
partitions=1/1
rollup: household_demographics
tabletRatio=3/3
tabletList=20848,20852,20856
cardinality=14399
avgRowSize=2.8781166
numNodes=3
PLAN FRAGMENT 4
OUTPUT EXPRS:
PARTITION: HASH_PARTITIONED: `default_cluster:tpcds`.`time_dim`.`t_time_sk`
STREAM DATA SINK
EXCHANGE ID: 09
UNPARTITIONED
1:OlapScanNode
TABLE: time_dim
PREAGGREGATION: OFF. Reason: null
PREDICATES: `time_dim`.`t_hour` = 8, `time_dim`.`t_minute` >= 30, `default_cluster:tpcds.time_dim`.`__DORIS_DELETE_SIGN__` = 0
partitions=1/1
rollup: time_dim
tabletRatio=3/3
tabletList=20713,20717,20721
cardinality=172799
avgRowSize=11.671202
numNodes=3
2.2.1 Descripción de atributos comunes
Colocate Join es adecuado para escenarios donde varias tablas se dividen en depósitos según el mismo campo y se unen con frecuencia según el mismo campo. Por ejemplo, muchas aplicaciones de comercio electrónico se dividen en depósitos según el ID del comerciante y con frecuencia se unen según el Identificación del comerciante.
2.2.2 análisis del plan
-
El plan de consulta de Query96 se divide en cinco fragmentos de plan, numerados del 0 al 4
-
El plan de consulta de análisis se puede realizar de abajo hacia arriba y analizar uno por uno.
-
El fragmento del plan inferior es el análisis del fragmento 4.
- Principalmente responsable de escanear la tabla time_dim y ejecutar las condiciones de consulta relacionadas por adelantado, es decir, pushdown de predicados
- Para la tabla de agregación (Clave de agregación), Doris elegirá si habilitar PREAGGREGACIÓN de acuerdo con diferentes consultas. La preagregación de time_dim en la figura anterior está desactivada. Cuando se desactiva, se leerán todas las columnas de dimensión de time_dim. Cuando hay muchas columnas de dimensiones en la tabla, esto puede convertirse en un factor clave que afecta el rendimiento.
- Si la tabla time_dim selecciona Partición de rango para la partición de datos, las particiones en el Plan de consulta indicarán cuántas particiones alcanza la consulta y el filtrado automático de particiones irrelevantes reducirá efectivamente la cantidad de datos escaneados.
- Si hay una vista materializada, Doris seleccionará automáticamente la vista materializada de acuerdo con la consulta. Si no hay una vista materializada, la consulta llegará automáticamente a la tabla base, que es el resumen: time_dim que se muestra en la figura anterior. Puede consultar a doris para probar la vista materializada
- Cuando se complete el escaneo de datos time_dim, finalizará el proceso de ejecución del Fragmento 4. En este momento, pasará los datos escaneados a sus otros Fragmentos. ID DE INTERCAMBIO: 09 significa que los datos se pasan al nodo receptor etiquetado como 9, que se puede pasar la vista gráfica
-
Para el Plan de consulta de Query96, los Fragmentos 2, 3 y 4 tienen funciones similares, pero las tablas responsables del escaneo son diferentes; específicamente, los operadores Orden/Agregación/Unión en la consulta se realizan todos en el Fragmento 1, y el análisis del Fragmento 1 esta enfocado
- El fragmento 1 integra la ejecución de tres operadores de unión y utiliza el método BROADCAST predeterminado para la ejecución, es decir, la tabla pequeña se transmite a la tabla grande. Si las dos tablas de unión son tablas grandes, se recomienda utilizar el método SHUFFLE.
- En la actualidad, Doris solo admite HASH JOIN, es decir, el algoritmo hash se utiliza para unirse.
- Hay un campo de colocación, que se utiliza para indicar que las dos tablas de unión adoptan el mismo método de partición/depósito, de modo que el proceso de unión se pueda ejecutar directamente localmente sin movimiento de datos.
- Una vez completada la ejecución de Join, se deben ejecutar los operadores mencionados anteriormente de Agregación, Orden por y TOP-N.
3. Breve introducción de Doris-Profile
Puede ver los detalles de ejecución de la tarea a través del módulo QueryProfile en la página 8030. Lo siguiente es parte del QueryProfile realmente ejecutado por query96.sql. Para obtener detalles sobre el nombre de cada indicador, consulte: Análisis de consultas de Apache Doris
Query:
Summary:
- Query ID: 7dd4ba245012441c-b0aadbed39f80f20
- Start Time: 2022-04-15 15:52:22
- End Time: 2022-04-15 15:52:22
- Total: 611ms
- Query Type: Query
- Query State: EOF
- Doris Version: 0.15.0-rc04
- User: root
- Default Db: default_cluster:tpcds
- Sql Statement: /* ApplicationName=DBeaver Enterprise 7.0.0 - SQLEditor <20220321常用命令-doris.sql> */ select count(*)
from store_sales
,household_demographics
,time_dim
, store
where ss_sold_time_sk = time_dim.t_time_sk
and ss_hdemo_sk = household_demographics.hd_demo_sk
and ss_store_sk = s_store_sk
and time_dim.t_hour = 8
and time_dim.t_minute >= 30
and household_demographics.hd_dep_count = 5
and store.s_store_name = 'ese'
order by count(*) limit 100
- Is Cached: No
Execution Summary:
- Analysis Time: 636.648us
- Plan Time: 19.230ms
- Schedule Time: 125.121ms
- Wait and Fetch Result Time: 466.30ms
Execution Profile 7dd4ba245012441c-b0aadbed39f80f20:(Active: 611.44ms, % non-child: 100.00%)
Fragment 0:
Instance 7dd4ba245012441c-b0aadbed39f80f2d (host=TNetworkAddress(hostname:10.192.119.70, port:9060)):(Active: 586.950ms, % non-child: 0.00%)
- FragmentCpuTime: 756.962us
- MemoryLimit: 2.00 GB
- PeakMemoryUsage: 48.01 KB
- PeakReservation: 0.00
- PeakUsedReservation: 0.00
- RowsProduced: 1
BlockMgr:
- BlockWritesOutstanding: 0
- BlocksCreated: 0
- BlocksRecycled: 0
- BufferedPins: 0
- BytesWritten: 0.00
- MaxBlockSize: 8.00 MB
- TotalBufferWaitTime: 0ns
- TotalEncryptionTime: 0ns
- TotalIntegrityCheckTime: 0ns
- TotalReadBlockTime: 0ns
DataBufferSender (dst_fragment_instance_id=7dd4ba245012441c-b0aadbed39f80f2d):
- AppendBatchTime: 124.481us
- ResultSendTime: 119.257us
- TupleConvertTime: 4.217us
- NumSentRows: 1
SORT_NODE (id=8):(Active: 587.36ms, % non-child: 0.01%)
- PeakMemoryUsage: 16.00 KB
- RowsReturned: 1
- RowsReturnedRate: 1
AGGREGATION_NODE (id=13):(Active: 586.958ms, % non-child: 0.10%)
- Probe Method: HashTable Linear Probing
- BuildTime: 10.533us
- GetResultsTime: 0ns
- HTResize: 0
- HTResizeTime: 0ns
- HashBuckets: 0
- HashCollisions: 0
- HashFailedProbe: 0
- HashFilledBuckets: 0
- HashProbe: 0
- HashTravelLength: 0
- LargestPartitionPercent: 0
- MaxPartitionLevel: 0
- NumRepartitions: 0
- PartitionsCreated: 0
- PeakMemoryUsage: 28.00 KB
- RowsProcessed: 0
- RowsRepartitioned: 0
- RowsReturned: 1
- RowsReturnedRate: 1
- SpilledPartitions: 0
EXCHANGE_NODE (id=12):(Active: 586.364ms, % non-child: 95.96%)
- BytesReceived: 32.00 B
- ConvertRowBatchTime: 7.320us
- DataArrivalWaitTime: 586.282ms
- DeserializeRowBatchTimer: 22.637us
- FirstBatchArrivalWaitTime: 349.530ms
- PeakMemoryUsage: 12.01 KB
- RowsReturned: 3
- RowsReturnedRate: 5
- SendersBlockedTotalTimer(*): 0ns
Fragment 1:
Instance 7dd4ba245012441c-b0aadbed39f80f23 (host=TNetworkAddress(hostname:10.192.119.68, port:9060)):(Active: 472.511ms, % non-child: 0.10%)
- FragmentCpuTime: 5.714ms
- MemoryLimit: 2.00 GB
- PeakMemoryUsage: 610.00 KB
- PeakReservation: 0.00
- PeakUsedReservation: 0.00
- RowsProduced: 1
BlockMgr:
- BlockWritesOutstanding: 0
- BlocksCreated: 0
- BlocksRecycled: 0
- BufferedPins: 0
- BytesWritten: 0.00
- MaxBlockSize: 8.00 MB
- TotalBufferWaitTime: 0ns
- TotalEncryptionTime: 0ns
- TotalIntegrityCheckTime: 0ns
- TotalReadBlockTime: 0ns
DataStreamSender (dst_id=12, dst_fragments=[7dd4ba245012441c-b0aadbed39f80f2d]):(Active: 186.357us, % non-child: 0.03%)
- BytesSent: 16.00 B
- IgnoreRows: 0
- LocalBytesSent: 0.00
- OverallThroughput: 83.84375 KB/sec
- PeakMemoryUsage: 16.00 KB
- SerializeBatchTime: 7.0us
- UncompressedRowBatchSize: 16.00 B
AGGREGATION_NODE (id=7):(Active: 471.713ms, % non-child: 0.14%)
- Probe Method: HashTable Linear Probing
- BuildTime: 45.223us
- GetResultsTime: 0ns
- HTResize: 0
- HTResizeTime: 0ns
- HashBuckets: 0
- HashCollisions: 0
- HashFailedProbe: 0
- HashFilledBuckets: 0
- HashProbe: 0
- HashTravelLength: 0
- LargestPartitionPercent: 0
- MaxPartitionLevel: 0
- NumRepartitions: 0
- PartitionsCreated: 0
- PeakMemoryUsage: 280.00 KB
- RowsProcessed: 0
- RowsRepartitioned: 0
- RowsReturned: 1
- RowsReturnedRate: 2
- SpilledPartitions: 0
HASH_JOIN_NODE (id=6):(Active: 470.881ms, % non-child: 0.08%)
- ExecOption: Hash Table Built Asynchronously
- BuildBuckets: 1.024K (1024)
- BuildRows: 1
- BuildTime: 1.129ms
- HashTableMaxList: 1
- HashTableMinList: 1
- LoadFactor: 4562146422526312400.00
- PeakMemoryUsage: 308.00 KB
- ProbeRows: 341
- ProbeTime: 34.697us
- PushDownComputeTime: 156.171us
- PushDownTime: 4.423us
- RowsReturned: 341
- RowsReturnedRate: 724
- Activo: indica el tiempo de ejecución del nodo (incluidos todos sus nodos secundarios)
- BuildTime: el momento de escanear la tabla correcta y construir la tabla hash
- ProbeTime: el momento para obtener la tabla de la izquierda y buscar en la tabla hash coincidencias y resultados