Query Profile performance diagnostics and optimization case studies
This topic explains how to interpret a Query Profile to pinpoint performance bottlenecks in your StarRocks instance and provides optimization techniques to improve query efficiency.
Query Profile overview
Visualize a Query Profile
You can use StarRocks Manager to visually analyze a Query Profile. For more information, see Query Profile introduction.
Identify query bottlenecks
The more time an operator consumes, the darker its color. The top three operators by execution time are highlighted, making it easy to identify query bottlenecks.
Optimization use cases
Bitmap index
A Bitmap index is a special type of database index that uses a bit array. Each bit in the array corresponds to a single row in the data table. The value of the bit, either 0 or 1, is determined by the value of the corresponding row.
-
Use a Bitmap index to improve query performance for columns that have low cardinality and a high number of repeating values, such as a
gendercolumn. -
To check if a query hits a Bitmap index, view the
BitmapIndexFilterRowsfield in the Query Profile.
Create an index
-
Create a Bitmap index when you create a table.
CREATE TABLE `student_info` ( `s_stukey` bigint(20) NULL COMMENT "", `s_name` varchar(65533) NULL COMMENT "", `s_gender` varchar(65533) NULL COMMENT "", INDEX index1 (s_gender) USING BITMAP COMMENT 'index1' ) ENGINE=OLAP DUPLICATE KEY(`s_stukey`) COMMENT "OLAP" DISTRIBUTED BY HASH(`s_stukey`); INSERT INTO student_info VALUES (001,'student#000000019','male'), (002,'student#000000020','male'), (003,'student#000000021','male'), (004,'student#000000022','female'); -
Create a Bitmap index on an existing table by using
CREATE INDEX.CREATE INDEX index_name ON table_name (column_name) [USING BITMAP] [COMMENT ''];
Check the index creation progress
SHOW ALTER TABLE COLUMN [FROM db_name];
View an index
SHOW {INDEX[ES] | KEY[S] } FROM [db_name.]table_name [FROM db_name];
MySQL [db_test]> show index from student_info;
+------------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+------------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| db_test.student_info | | index1 | | s_gender | | | | | | BITMAP | index1 |
+------------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
1 row in set (0.01 sec)
Drop an index
DROP INDEX index_name ON [db_name.]table_name;
Test a single-column query
-
Execute a query that filters the
s_gendercolumn.select * from student_info where s_gender='male'; -
View the Profile.
Click OLAP_SCAN, then click the Node Details tab on the right. Filter the metrics for
Bitmapto confirm that the Bitmap index has taken effect.
Bloom filter index
A Bloom filter index quickly determines whether a data file contains the target data. If it does not, the file is skipped, which reduces the amount of data scanned. A Bloom filter is space-efficient and well-suited for high-cardinality columns, such as ID columns.
-
In the Primary Key model and Duplicate model, you can create a Bloom filter index on any column. In the Aggregate model and Update model, you can create a Bloom filter index only on key columns.
-
Bloom filter indexes are not supported for columns of the TINYINT, FLOAT, DOUBLE, or DECIMAL data types.
-
A Bloom filter index only improves performance for queries that contain
inor=predicates, such asSELECT ... WHERE ... IN ()andSELECT ... WHERE column = .... -
To check if a query hits a Bloom filter index, view the
BloomFilterFilterRowsfield in the Query Profile.
Create an index
When you create a table, you can create a Bloom filter index by specifying bloom_filter_columns in the PROPERTIES clause. The following example shows how to do this.
CREATE TABLE table1
(
k1 BIGINT,
k2 LARGEINT,
v1 VARCHAR(2048) REPLACE,
v2 SMALLINT DEFAULT "10"
)
ENGINE = olap
PRIMARY KEY(k1, k2)
DISTRIBUTED BY HASH (k1, k2) BUCKETS 10
PROPERTIES("bloom_filter_columns" = "k1,k2"); -- Separate multiple index columns with commas (,).
View an index
SHOW CREATE TABLE table1;
Modify an index
For example:
-
Add a Bloom filter index for column
v1.ALTER TABLE table1 SET ("bloom_filter_columns" = "k1,k2,v1"); -
Remove the Bloom filter index for column
k2.ALTER TABLE table1 SET ("bloom_filter_columns" = "k1"); -
Delete all Bloom filter indexes from
table1.ALTER TABLE table1 SET ("bloom_filter_columns" = "");
Example
-
Take the
customertable from TPC-H as an example. Add a Bloom filter index on a high-cardinality column that is not a sort key, such as thec_phonecolumn.ALTER TABLE tpc_h_sf100.customer SET ("bloom_filter_columns" = "c_custkey, c_phone"); -
View the index to confirm that the Bloom filter index has been added.
SHOW CREATE TABLE tpc_h_sf100.customer;| customer | CREATE TABLE `customer` ( `c_custkey` bigint(20) NULL COMMENT "", `c_name` varchar(65533) NULL COMMENT "", `c_address` varchar(65533) NULL COMMENT "", `c_nationkey` bigint(20) NULL COMMENT "", `c_phone` varchar(65533) NULL COMMENT "", `c_acctbal` double NULL COMMENT "", `c_mktsegment` varchar(65533) NULL COMMENT "", `c_comment` varchar(65533) NULL COMMENT "", `Gender` varchar(65533) NULL DEFAULT "default_value" COMMENT "" ) ENGINE=OLAP DUPLICATE KEY(`c_custkey`) COMMENT "OLAP" DISTRIBUTED BY HASH(`c_custkey`) BUCKETS 24 PROPERTIES ( "replication_num" = "1", "bloom_filter_columns" = "c_custkey, c_phone", "in_memory" = "false", "storage_format" = "DEFAULT", "enable_persistent_index" = "false", "compression" = "LZ4" ); | -
Execute a query that filters the
c_phonecolumn.select * from tpc_h_sf100.customer where c_phone = "10-334-921-5346"; -
View the Profile.
Click OLAP_SCAN, then click the Node Details tab on the right. Find the
BloomFilterFilterRowsmetric to confirm that the Bloom filter index is active.
Optimize for data skew
This example uses the lineitem table from TPC-H and selects a column with a non-uniform distribution as the bucketing key to demonstrate a data skew problem. In this case, the l_tag column has a non-uniform value distribution.
-
Create test data. Add a new column to the
lineitemtable from TPC-H to use as the distribution key.CREATE TABLE `lineitem_tag` ( `l_orderkey` bigint(20) NULL COMMENT "", `l_partkey` bigint(20) NULL COMMENT "", `l_suppkey` bigint(20) NULL COMMENT "", `l_linenumber` int(11) NULL COMMENT "", `l_quantity` double NULL COMMENT "", `l_extendedprice` double NULL COMMENT "", `l_discount` double NULL COMMENT "", `l_tax` double NULL COMMENT "", `l_returnflag` varchar(65533) NULL COMMENT "", `l_linestatus` varchar(65533) NULL COMMENT "", `l_shipdate` date NULL COMMENT "", `l_commitdate` date NULL COMMENT "", `l_receiptdate` date NULL COMMENT "", `l_shipinstruct` varchar(65533) NULL COMMENT "", `l_shipmode` varchar(65533) NULL COMMENT "", `l_comment` varchar(65533) NULL COMMENT "", `l_tag` varchar(65533) default 'false' COMMENT "" ) ENGINE=OLAP DUPLICATE KEY(`l_orderkey`) COMMENT "OLAP" DISTRIBUTED BY HASH(`l_tag`) BUCKETS 96 PROPERTIES ( "replication_num" = "1", "in_memory" = "false", "storage_format" = "DEFAULT", "enable_persistent_index" = "false" ); insert into lineitem_tag select *, 'false' as l_tag from tpc_h_sf100.lineitem; -
Execute a query to perform a full table scan.
select count(1) from lineitem_tag; -
View the Profile.
Click OLAP_SCAN and go to the Node tab on the right. Compare the SCAN time under MaxTime and MinTime. If the times differ by several orders of magnitude, data skew might have occurred.
-
Optimize the table structure by redefining the distribution key. Create a new table named
lineitem2and change the bucketing key froml_tagtol_orderkeyto resolve the data skew. The following statement creates the table, where the critical change isDISTRIBUTED BY HASH(.l_orderkey) BUCKETS 96CREATE TABLE `lineitem2` ( `l_orderkey` bigint(20) NULL COMMENT "", `l_partkey` bigint(20) NULL COMMENT "", `l_suppkey` bigint(20) NULL COMMENT "", `l_linenumber` int(11) NULL COMMENT "", `l_quantity` double NULL COMMENT "", `l_extendedprice` double NULL COMMENT "", `l_discount` double NULL COMMENT "", `l_tax` double NULL COMMENT "", `l_returnflag` varchar(65533) NULL COMMENT "", `l_linestatus` varchar(65533) NULL COMMENT "", `l_shipdate` date NULL COMMENT "", `l_commitdate` date NULL COMMENT "", `l_receiptdate` date NULL COMMENT "", `l_shipinstruct` varchar(65533) NULL COMMENT "", `l_shipmode` varchar(65533) NULL COMMENT "", `l_comment` varchar(65533) NULL COMMENT "", `l_tag` varchar(65533) NULL DEFAULT "default_value" COMMENT "" ) ENGINE=OLAP DUPLICATE KEY(`l_orderkey`) COMMENT "OLAP" DISTRIBUTED BY HASH(`l_orderkey`) BUCKETS 96 PROPERTIES ( "replication_num" = "1", "bloom_filter_columns" = "l_orderkey", "in_memory" = "false", "storage_format" = "DEFAULT", "enable_persistent_index" = "false", "compression" = "LZ4" ); -
View the Profile again. Compare the SCAN time under MaxTime and MinTime. You can see that the data skew issue has been mitigated.
Single-table materialized view
A single-table materialized view (also known as a Rollup) in StarRocks is a special type of index that cannot be queried directly. If your data warehouse contains a large number of complex or repetitive queries, you can create a single-table materialized view to accelerate them.
Test a query
-
Take the
lineitemtable from TPC-H as an example and execute a query.select l_returnflag,l_linestatus,l_shipmode,sum(l_extendedprice) from lineitem group by l_returnflag,l_linestatus,l_shipmode; -
The initial query takes 1115 ms to complete because no materialized view has been created.
-
View the Profile.
Click OLAP_SCAN and go to the Node tab on the right. You can see that the Rollup scanned the
lineitemtable itself.
Create a materialized view
CREATE MATERIALIZED VIEW material_test AS select l_returnflag,l_linestatus ,l_shipmode,sum(l_extendedprice) from lineitem group by l_returnflag,l_linestatus,l_shipmode;
Verify materialized view hit
-
You can use the EXPLAIN command to check if the query hits the single-table materialized view.
explain select l_returnflag,l_linestatus ,l_shipmode,sum(l_extendedprice) from lineitem group by l_returnflag,l_linestatus,l_shipmode;In the returned result,
rollup: material_testindicates that the query hit the materialized view namedmaterial_test.0:OlapScanNode TABLE: lineitem PREAGGREGATION: OFF. Reason: null partitions=1/1 rollup: material_test tabletRatio=96/96 tabletList=15646,15648,15650,15652,15654,15656,15658,15660,15662,15664 ... cardinality=600037902 avgRowSize=14.285666 numNodes=0 -
View the Profile.
Click OLAP_SCAN. On the Node tab on the right, you can see that the query hits the materialized view and the query time is reduced to 0.05 ms.
Verify if JoinRuntimeFilter is active
When the right table in a JOIN operation builds a hash table, a Runtime Filter is created. This filter is sent to the left side of the query tree and pushed down to the scan operator whenever possible. You can view metrics related to JoinRuntimeFilter on the Node Details tab of the scan operator.
-
Take query72.sql from TPC-DS as an example.
select i_item_desc, w_warehouse_name, d1.d_week_seq, sum(case when p_promo_sk is null then 1 else 0 end) no_promo, sum(case when p_promo_sk is not null then 1 else 0 end) promo, count(*) total_cnt from inventory join catalog_sales on (cs_item_sk = inv_item_sk) join warehouse on (w_warehouse_sk=inv_warehouse_sk) join item on (i_item_sk = cs_item_sk) join customer_demographics on (cs_bill_cdemo_sk = cd_demo_sk) join household_demographics on (cs_bill_hdemo_sk = hd_demo_sk) join date_dim d1 on (cs_sold_date_sk = d1.d_date_sk) join date_dim d2 on (inv_date_sk = d2.d_date_sk) join date_dim d3 on (cs_ship_date_sk = d3.d_date_sk) left outer join promotion on (cs_promo_sk=p_promo_sk) left outer join catalog_returns on (cr_item_sk = cs_item_sk and cr_order_number = cs_order_number) where d1.d_week_seq = d2.d_week_seq and inv_quantity_on_hand < cs_quantity and d3.d_date > (cast(d1.d_date AS DATE) + interval '5' day) and hd_buy_potential = '>10000' and d1.d_year = 1999 and cd_marital_status = 'D' group by i_item_desc,w_warehouse_name,d1.d_week_seq order by total_cnt desc, i_item_desc, w_warehouse_name, d1.d_week_seq limit 100; -
View the Profile.
Click OLAP_SCAN, then click the Node Details tab on the right. You can see that the JoinRuntimeFilter was triggered when the scan operator scanned the inventory table.
Colocate Join
In StarRocks, to use the Colocate Join feature, you must assign tables to a Colocation Group (CG) when they are created. Tables within the same CG must follow the same Colocation Group Schema (CGS), which ensures their data is distributed across the same set of BE nodes. When the join column is also the bucketing key, compute nodes only need to perform a local join. This reduces data transfer time between nodes and improves query performance. Unlike Shuffle or Broadcast Joins, a Colocate Join avoids network data transfer, which significantly improves query performance.
Create a colocated table
StarRocks supports Colocate Join operations only on tables within the same database.
CREATE TABLE tbl (k1 int, v1 int sum)
DISTRIBUTED BY HASH(k1)
BUCKETS 8
PROPERTIES(
"colocate_with" = "group1"
);
Delete a Colocation Group
When the last table in a Group is permanently deleted, the Group is also automatically deleted. Permanent deletion means that the table is removed from the Recycle Bin. Typically, after a table is deleted by using the DROP TABLE command, it remains in the Recycle Bin for one day by default before it is permanently deleted.
View group information
For example, execute the following command to view group information.
SHOW PROC '/colocation_group';
The command returns the following information.
+-------------+--------------+----------+------------+----------------+----------+----------+
| GroupId | GroupName | TableIds | BucketsNum | ReplicationNum | DistCols | IsStable |
+-------------+--------------+----------+------------+----------------+----------+----------+
| 11912.11916 | 11912_group1 | 11914 | 8 | 3 | int(11) | true |
+-------------+--------------+----------+------------+----------------+----------+----------+
The following table describes the columns.
|
Column |
Description |
|
GroupId |
The unique identifier of a group across the entire cluster. The first part is the database ID, and the second part is the group ID. |
|
GroupName |
The full name of the group. |
|
TableIds |
A list of table IDs included in this group. |
|
BucketsNum |
The number of buckets. |
|
ReplicationNum |
The number of replicas. |
|
DistCols |
The data type of the distribution columns. |
|
IsStable |
Indicates whether the group is stable. |
You can execute the following command to view the data distribution of a specific group.
SHOW PROC '/colocation_group/GroupId';
SHOW PROC '/colocation_group/11912.11916';
The command returns the following information.
+-------------+---------------------+
| BucketIndex | BackendIds |
+-------------+---------------------+
| 0 | 10002, 10004, 10003 |
| 1 | 10002, 10004, 10003 |
| 2 | 10002, 10004, 10003 |
| 3 | 10002, 10004, 10003 |
| 4 | 10002, 10004, 10003 |
| 5 | 10002, 10004, 10003 |
| 6 | 10002, 10004, 10003 |
| 7 | 10002, 10004, 10003 |
+-------------+---------------------+
8 rows in set (0.00 sec)
Modify group properties
ALTER TABLE tbl SET ("colocate_with" = "group_name");
-
Using the TPC-H data as an example, perform the following operations.
use tpc_h_sf100; ALTER TABLE orders SET ("colocate_with" = "cg_tpc_orders"); ALTER TABLE lineitem SET ("colocate_with" = "cg_tpc_orders"); -
Execute the following query.
select count(1) from orders as o join lineitem as l on o.o_orderkey = l.l_orderkey; -
You can check whether the Colocate Join is active on the Query Plan tab for the query in the StarRocks Manager UI.
When
colocateis true, it indicates that Colocate Join has taken effect. This is indicated bycolocate: truein the execution plan. The corresponding execution plan fragment is:2:HASH JOIN | join op: INNER JOIN (COLOCATE) | colocate: true | equal join conjunct: 10: l_orderkey = 1: o_orderkey | |----1:OlapScanNode | TABLE: orders | PREAGGREGATION: ON | PREDICATES: 1: o_orderkey IS NOT NULL | partitions=1/1 | rollup: orders | tabletRatio=96/96 | tabletList=12403,12405,12407,12409,12411,12413,12415,12417,12419,12421 ... | cardinality=135000000 | avgRowSize=1.0 | numNodes=0
Verify bucket and partition pruning
On the Query Plan tab for your query in the StarRocks Manager UI, you can view the partition and tabletRatio parameters to verify if partition pruning or bucket pruning is active.