All Products
Search
Document Center

E-MapReduce:Query Profile performance diagnostics and optimization case studies

Last Updated:Sep 16, 2026

A Query Profile records the execution details of a query in your StarRocks instance. Use Query Profiles to diagnose query performance, pinpoint bottlenecks, and select the optimization technique that resolves them.

Query Profile overview

Query Profile visualization

StarRocks Manager supports visual analytics for Query Profiles. For more information, see Query Profile introduction.

Identify query bottlenecks

In the Query Profile visualization in StarRocks Manager, operators that take longer to run appear in darker colors. The three operators with the longest execution duration are highlighted, which makes query bottlenecks easy to identifyOperator execution duration in the Query Profile

Why is the CONNECTOR_SCAN time longer than the SCANTIME?

CONNECTOR_SCAN represents the cumulative active time of an operator across all of its parallel instances. This value includes SCANTIME, IOTaskExecTime, OperatorTotalTime, and other components. As a result, CONNECTOR_SCAN can be numerically larger than the SCANTIME of a single instance.

Optimization use cases

The following sections describe how to diagnose and resolve common query performance problems in StarRocks:

Optimize large-field queries that cause out-of-memory (OOM) errors

When you query a table that contains large fields such as TEXT or VARCHAR, reading or computing too much large-field data can cause an out-of-memory (OOM) error. For memory limit errors that data skew causes instead, see Optimization measures for memory limit errors (Memory of process exceed limit) caused by data skew. Apply the following measures to reduce memory consumption:

  • Avoid SELECT *. Explicitly specify only the columns that you need to reduce the data volume that is scanned and loaded into memory.

  • Keep large fields out of aggregate operations such as GROUP BY and DISTINCT to prevent oversized intermediate result sets.

  • Enable the spill to disk feature to relieve memory pressure by setting the session variable enable_spill=true.

  • Optimize the table schema by moving large text fields into a separate table, and keep only indexes and small fields in the primary table. This table-splitting approach does not take effect in aggregation scenarios.

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 gender column.

  • To check if a query hits a Bitmap index, view the BitmapIndexFilterRows field 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

Run the following command to check the progress of an index creation task:

SHOW ALTER TABLE COLUMN [FROM db_name];

View an index

Run the following command to view the indexes on a table:

SHOW {INDEX[ES] | KEY[S] } FROM [db_name.]table_name [FROM db_name];

The following output is returned:

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

Run the following command to drop an index:

DROP INDEX index_name ON [db_name.]table_name;

Test a single-column query

  1. Run a query that filters the s_gender column.

    select * from student_info where s_gender='male';
  2. View the Profile.

    Click OLAP_SCAN, then click the Node Details tab on the right. Filter the metrics for Bitmap to confirm that the Bitmap index has taken effect.Bitmap index metrics in the Query Profile

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 data volume 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 in or = predicates, such as SELECT ... WHERE ... IN () and SELECT ... WHERE column = ....

  • To check if a query hits a Bloom filter index, view the BloomFilterFilterRows field 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

Run the following command to view the indexes on a table:

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

  1. Take the customer table from TPC-H as an example. Add a Bloom filter index on a high-cardinality column that is not a sort key, such as the c_phone column.

    ALTER TABLE tpc_h_sf100.customer SET ("bloom_filter_columns" = "c_custkey, c_phone");
  2. 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"
    ); |
  3. Run a query that filters the c_phone column.

    select * from tpc_h_sf100.customer where c_phone = "10-334-921-5346";
  4. View the Profile.

    Click OLAP_SCAN, then click the Node Details tab on the right. Find the BloomFilterFilterRows metric to confirm that the Bloom filter index is active.Bloom filter index metrics in the Query Profile

Optimize for data skew

This example uses the lineitem table from TPC-H and selects a column with an uneven value distribution as the bucketing key to demonstrate a data skew problem. In this example, the l_tag column has the same value in every row, which produces extreme data skew.

  1. Create test data. Add a new column to the lineitem table 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;
  2. Run a query to perform a full table scan.

    select count(1) from lineitem_tag;
  3. 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.Data skew in the Query Profile

  4. Optimize the table schema by redefining the distribution key. Create a new table named lineitem2 and change the bucketing key from l_tag to l_orderkey to resolve the data skew. The following statement creates the table, where the critical change is DISTRIBUTED BY HASH(`l_orderkey`) BUCKETS 96.

    CREATE 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"
    );
  5. View the Profile again. Compare the SCAN time under MaxTime and MinTime. You can see that the data skew issue has been mitigated.Query Profile after the bucketing key change

Optimization measures for memory limit errors (Memory of process exceed limit) caused by data skew

Data skew can also cause queries to fail with a Memory of process exceed limit error. For OOM errors that large fields cause instead, see Optimize large-field queries that cause out-of-memory (OOM) errors. If you encounter memory limit errors caused by data skew, try the following optimizations in addition to changing the bucketing key:

  • Optimize the bucketing strategy: Use a high-cardinality column as the bucketing key, combine multiple columns for bucketing, or apply salting or random bucketing to hot values.

  • Adjust the JOIN execution strategy: Use Broadcast Join when joining a small table with a large table. When joining two large tables, make sure the join keys are evenly distributed, or use DISTRIBUTE BY to redistribute the data.

  • Dynamically adjust and optimize resource parameters: Change the bucketing method of the existing table, increase BE concurrent execution parameters such as parallel_fragment_exec_instance_num, or add more BE nodes or replicas to spread the load.

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

  1. Take the lineitem table from TPC-H as an example and run a query.

    select l_returnflag,l_linestatus,l_shipmode,sum(l_extendedprice) from lineitem group by l_returnflag,l_linestatus,l_shipmode;
  2. The initial query takes 1115 ms to complete because no materialized view has been created.Query duration before the materialized view is created

  3. View the Profile.

    Click OLAP_SCAN and go to the Node tab on the right. You can see that the Rollup scanned the lineitem table itself.Rollup in the Query Profile

Create a materialized view

Run the following command to create a single-table 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

  1. Use the EXPLAIN command to check whether 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_test indicates that the query hit the materialized view named material_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
  2. 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.Materialized view hit in the Query Profile

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.

  1. 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;
  2. 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.JoinRuntimeFilter metrics in the Query Profile

Colocate Join

To use Colocate Join in StarRocks, assign tables to a Colocation Group (CG) when you create them. Tables in the same CG must follow the same Colocation Group Schema (CGS) so that 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, which reduces data transfer time between nodes. Unlike a Shuffle Join or a Broadcast Join, a Colocate Join avoids network data transfer and improves query performance.

Create a colocated table

Note

StarRocks supports Colocate Join operations only on tables within the same database.

Run the following statement to create a table in a Colocation Group:

CREATE TABLE tbl (k1 int, v1 int sum)
DISTRIBUTED BY HASH(k1)
BUCKETS 8
PROPERTIES(
    "colocate_with" = "group1"
);

Automatic deletion of 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. After you delete a table by using the DROP TABLE command, the table remains in the Recycle Bin for one day by default before it is permanently deleted.

View group information

For example, run 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 run 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

Run the following statement to move a table to a different Colocation Group:

ALTER TABLE tbl SET ("colocate_with" = "group_name");

Example

  1. Assign the orders and lineitem tables in the TPC-H dataset to the same Colocation Group.

    use tpc_h_sf100;
    ALTER TABLE orders SET ("colocate_with" = "cg_tpc_orders");
    ALTER TABLE lineitem SET ("colocate_with" = "cg_tpc_orders");
  2. Run the following query.

    select count(1) from orders as o join lineitem as l on o.o_orderkey = l.l_orderkey;
  3. Check whether the Colocate Join is active on the Query Plan tab for the query in the StarRocks Manager UI.

    When colocate is true, Colocate Join has taken effect. In the execution plan, this appears as colocate: true. The corresponding query 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 or the tabletRatio parameter to verify if partition pruning or bucket pruning is active.

How do I troubleshoot continuously rising FE memory that triggers a memory alert?

When FE memory keeps rising and triggers a memory alert, troubleshoot the issue in the following order:

  1. Check for metadata bloat — Run SHOW PROC '/statistic' and check the total TabletNum. A total TabletNum above 1,000,000 indicates metadata bloat.

  2. Locate resource-intensive queries — If the TabletNum is normal, query the audit log table _starrocks_audit_db_.starrocks_audit_tbl, sort the records by CPU or memory consumption, and then optimize the high-load SQL statements.

  3. Resolve unbalanced load — Configure SLB and a client-side connection pool to enable load balancing across FE nodes. This prevents requests from concentrating on a single FE Leader, which causes memory spikes and frequent GC.

  4. Optimize the write method — Replace large VALUES inserts with Stream Load to eliminate FE parsing hotspots at the source.