<@U0A71G31CDV> what does this query mean: INSERT I...
# questions-and-troubleshooting
k
@Rocky what does this query mean: INSERT INTO blackhole()
r
In StarRocks, the query
INSERT INTO blackhole()
(specifically
INSERT INTO BLACKHOLE() SELECT ...
) is an internal mechanism used for Block Cache Warmup. What it does * Warms up the Cache: It triggers a full query execution path—fetching data from remote storage (like S3, HDFS, or a Data Lake)—which causes that data to be loaded into the local Block Cache of the Backend (BE) nodes. * Discards Results: As the name "blackhole" implies, the data is not actually written to any persistent storage or table. It is discarded immediately after being processed and cached. Primary Use Case This is the underlying implementation for the
CACHE SELECT
statement introduced in StarRocks v3.3. It is used to: 1. Accelerate First-Time Queries: Pre-load data into the cache so that subsequent user queries are sub-second instead of waiting for remote I/O. 2. Performance Testing (PoC): Assess StarRocks' compute performance without being bottlenecked by external storage latency. 3. Scheduled Warmup: Pre-warm data for critical daily tasks (e.g., loading a BI dashboard's data at 7 AM before users arrive at 8 AM). Example While usually managed via
CACHE SELECT
, the internal logic follows this pattern:
Copy code
sql
INSERT INTO BLACKHOLE() 
SELECT * FROM remote_catalog.db.large_table 
WHERE dt = '2023-10-01';
Note: If you want to warm up the cache manually, it is recommended to use the official `CACHE SELECT` syntax rather than calling
INSERT INTO BLACKHOLE()
directly, as the former is the supported user-user-facing command. References • sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md • table_design/StarRocks_table_design.md • sql-reference/System_variable.md • sql-reference/information_schema/engines.md • data_source/External_table.md • faq/Others.md • table_design/table_design.md
k
@Rocky is there a way to prioritize serving queries from specific tables?
r
Yes, you can prioritize queries from specific tables in StarRocks, but it is typically done through Resource Groups and Classifiers. While StarRocks does not currently support a "table" classifier directly, you can achieve table-level prioritization using the following methods: 1. Prioritize by Database (Most Common) If you can group your high-priority tables into a specific database, you can use the
db
classifier to map all queries hitting that database to a high-priority resource group.
Copy code
sql
CREATE RESOURCE GROUP high_priority_group
TO (db='important_db')
WITH (
    'cpu_weight' = '10',  -- Higher weight = more CPU share
    'mem_limit' = '50%'
);
2. Prioritize using Query Hints If you cannot move tables to a different database, you can use a SQL hint to manually route a specific query to a high-priority resource group at execution time. This is useful for BI dashboards or specific critical reports.
Copy code
sql
SELECT /*+ SET_VAR(resource_group = 'high_priority_group') */ * 
FROM critical_table 
JOIN other_table ON ...;
3. Prioritize by User or Role You can create a specific database user or role for applications that access the high-priority tables. Queries from that user/role will then automatically inherit the priority of their assigned resource group.
Copy code
sql
CREATE RESOURCE GROUP app_group
TO (user='reporting_service_user')
WITH ('cpu_weight' = '20');
4. Use Short Query Optimization For tables where you expect very fast, point-lookup style queries, StarRocks automatically prioritizes "Short Queries" based on cost estimation. Since v3.3.5, you can use
exclusive_cpu_cores
in a resource group to reserve dedicated hardware resources for these types of workloads, preventing them from being blocked by heavy scan-intensive queries. Summary of Resource Group Parameters When you define your priority group, use these properties to control the "strength" of the priority: *
cpu_weight
: A soft limit. If the system is busy, groups with higher weights get a larger proportional share of CPU. *
exclusive_cpu_cores
: A hard limit (introduced in v3.3.5). Reserves specific cores that only this group can use. *
concurrency_limit
: Prevents a group from being overwhelmed by too many simultaneous queries. References • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • faq/resource_isolation_faq.md • sql-reference/System_variable.md • administration/management/resource_management/resource_management.md management) References • sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
k
@Rocky what would be a cpu weight where if a query comes in that resource group db, it is done immediately
r
There is no single "magic" number for
cpu_weight
that guarantees a query will finish "immediately." In StarRocks,
cpu_weight
is a relative proportional share, not a priority ranking. If you want a specific database to have the absolute highest priority, you have two main strategies: 1. The
cpu_weight
Strategy (Relative/Soft Isolation)
cpu_weight
(formerly
cpu_core_limit
) determines what percentage of the total CPU a group gets when the system is under load. * How it works: If Group A has a weight of 100 and the
default_group
has a weight of 1, Group A will receive ~99% of the CPU cycles if both are trying to run at full capacity. * The "Immediate" Effect: To make a group feel "immediate," give it a weight significantly higher than the others (e.g., 10x or 100x the weight of your standard groups). * Limitation: This is a "soft" limit. A query might still experience a tiny sub-millisecond delay while the scheduler finishes a current time-slice for another task. 2. The
exclusive_cpu_cores
Strategy (Hard Isolation)
If "immediate" means you want the query to never wait for other workloads (even for a millisecond), you should use
exclusive_cpu_cores
(available in StarRocks v3.3.5+). * How it works: This reserves specific CPU cores solely for that resource group. These cores sit idle if no queries are running in that group, rather than being used by others. * Why use this: Because the cores are "exclusive," a query entering this group doesn't have to compete for a scheduling slot with a heavy background scan; it has its own dedicated hardware lane. * Example:
Copy code
sql
    CREATE RESOURCE GROUP immediate_group
    TO (db='critical_db')
    WITH (
        "exclusive_cpu_cores" = "4", -- Adjust based on your BE core count
        "mem_limit" = "20%"
    );
Comparison Feature |
cpu_weight
|
exclusive_cpu_cores
Type | Soft (Proportional) | Hard (Reserved) Idle Behavior | Others can use the CPU | Cores stay idle (unless borrowing is on) Best for | Prioritizing BI users over batch loads | Real-time APIs or mission-critical apps "Immediate" feel | Very high (relative to others) | Absolute (dedicated hardware) Recommendation: For most "high priority" needs, a
cpu_weight
of 10 (compared to a default of 1) is usually sufficient. If you need guaranteed sub-second response times for a specific table regardless of what else is happening on the cluster, use
exclusive_cpu_cores
. References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • sql-reference/System_variable.md • administration/management/resource_management/resource_management.md management) References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md
k
@Rocky getting this error: CREATE RESOURCE GROUP high_priority_contextual TO (db='default_catalog.starrocks') Query 1 ERROR at Line 1: : Getting analyzing error. Detail message: Specified database not exists: default_catalog.starrocks.
r
The error occurs because the
db
classifier
in StarRocks resource groups does not support the
catalog.database
(fully qualified) syntax. It expects only the database name itself. Even though your database resides in
default_catalog
, the resource group manager validates the name against the list of databases it knows. When you include the catalog prefix, StarRocks looks for a database literally named "`default_catalog.starrocks`", which does not exist. How to fix it Change your query to include only the database name:
Copy code
sql
-- Use just 'starrocks' if that is the name of your database
CREATE RESOURCE GROUP high_priority_contextual
TO (db='starrocks')
WITH (
    'cpu_weight' = '10',
    'mem_limit' = '20%'
);
Important Notes: 1. Check the Database Name: Ensure the database actually exists by running
SHOW DATABASES;
. If you are trying to prioritize the built-in system database, note that it is usually named
_statistics
or `information_schema`—there is no default database named
starrocks
unless you created one. 2. External Catalogs: If you are trying to prioritize a database from an external catalog (like Hive or Iceberg), you should still only provide the database name (e.g.,
db='sales_db'
). 3. Case Sensitivity: Database names in classifiers are generally case-sensitive depending on your StarRocks configuration. 4. Multiple Databases: You can specify multiple databases by separating them with commas:
TO (db='db1,db2,db3')
. References • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • administration/management/resource_management/resource_management.md References • data_source/catalog/iceberg/iceberg_catalog.md • sql-reference/sql-statements/Catalog/CREATE_EXTERNAL_CATALOG.md • data_source/External_table.md
k
@Rocky why am i experiencing this when mv health is reporting healthy:
Copy code
MV rewrite fail for curated_channel_enriched: stale partitions refreshType=FULL
mv status: last_refresh_duration: 21.8 last_refresh_state: SUCCESS query_rewrite_status: VALID last_refresh_error_code: 0
r
This error occurs when the StarRocks query optimizer detects that the data in your base table has changed since the Materialized View (MV) last finished its refresh. Even though your MV status is
SUCCESS
and
VALID
, the default behavior of the optimizer is to perform a version check at query time. If the base table's version is newer than the version captured in the MV, the MV is considered "stale" and the rewrite fails to ensure you don't get "old" data. Why this happens with
refreshType=FULL
The
refreshType=FULL
in the error message indicates that the MV is configured to refresh the entire dataset at once (likely because it is not partitioned or was created without a
PARTITION BY
clause). In this mode, any change to the base table—even a single row insertion—invalidates the entire MV for transparent rewrite until the next refresh completes. If your base table is being updated frequently (e.g., every few minutes), the MV will almost always be "stale" by the time you query it. How to Fix It 1. Tolerate a "Staleness" Window (Recommended) If you don't need absolute real-time consistency, you can tell StarRocks to allow rewrites as long as the MV was refreshed within a certain time window.
Copy code
sql
ALTER MATERIALIZED VIEW curated_channel_enriched 
SET ("mv_rewrite_staleness_second" = "3600"); -- Allow data up to 1 hour old
2. Relax Consistency Requirements You can change the consistency level to
loose
. In this mode, StarRocks will prioritize using the MV for query speed even if the base table has newer data.
Copy code
sql
ALTER MATERIALIZED VIEW curated_channel_enriched 
SET ("query_rewrite_consistency" = "loose");
3. Use Partitioning (For Incremental Refresh) If the table is very large, consider recreating it as a partitioned MV. This allows StarRocks to track staleness at the partition level. If only the "today" partition is stale, StarRocks can still rewrite queries hitting "yesterday's" data using the MV, while pulling only the stale portion from the base table (Partition-level Union Rewrite). 4. Check for External Table Metadata (If applicable) If your base table is in an external catalog (like Hive or Iceberg), ensure StarRocks can see the latest metadata. You may need to enable metadata caching or check the
force_external_table_query_rewrite
session variable if you are using older versions of StarRocks. Summary of Diagnostic Tools To confirm the exact version mismatch, you can run:
Copy code
sql
-- See detailed reasons for rewrite failure
TRACE REASON MV <your_query>;

-- Check the exact version recorded in the MV vs Base Table
SHOW MATERIALIZED VIEWS LIKE 'curated_channel_enriched'\G
References • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md • using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md • using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md References • using_starrocks/async_mv/use_cases/create_partitioned_materialized_view.md • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
k
@Rocky
Copy code
query_rewrite_consistency
how long will it reference the mv
r
When you set
query_rewrite_consistency
, the "how long" depends entirely on the value you choose. Here is the breakdown: 1.
LOOSE
(The "Forever" option)
If you set
query_rewrite_consistency = 'loose'
, StarRocks will reference the MV indefinitely, regardless of how much the base table data has changed. * Duration: Until the next manual or scheduled refresh successfully completes. * Behavior: The optimizer completely skips the version check. Even if the MV is days old and the base table has millions of new rows, the query will still rewrite to use the MV. 2.
CHECKED
(The Default / Strong Consistency)
This is the default mode. The MV is only referenced as long as it is perfectly in sync with the base table. * Duration: Until the next data modification (Insert, Update, Delete, or Load) happens on any base table. * Behavior: As soon as a base table's version increases, the MV is marked "stale" for rewrite purposes. 3.
mv_rewrite_staleness_second
(The "Timed" option)
This is often the better middle ground. You keep the consistency check but give it a "grace period." * Duration:
Last Refresh Time + N seconds
. * Example: If you set
mv_rewrite_staleness_second = '3600'
, the query will reference the MV for 1 hour after the last successful refresh, even if the base table data changes during that hour. * Behavior: Once the clock passes that 1-hour window since the last refresh, the MV is considered stale again until the next refresh. Summary Table Setting | How long it references the MV | Risk
checked
| Only while data is 100% identical. | Frequent rewrite failures if data loads often.
loose
| Forever (until next refresh). | High risk of returning very old/incorrect data.
staleness_second
| For the specific duration you define. | Controlled risk; good for "near real-time" BI. Recommendation: For your
curated_channel_enriched
MV, if you are seeing "stale partitions" errors, try setting
mv_rewrite_staleness_second
to a value like
300
(5 minutes) or
3600
(1 hour) instead of using
LOOSE
. This ensures the MV is used for a predictable window of time without letting the data get "forever" out of date. References • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md • using_starrocks/async_mv/Materialized_view.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md • using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md References • administration/management/FE_configuration.md • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md • using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
k
@Rocky if query_rewrite_consistency is loose but the mv is invalid, will it query the underlying external tables
r
No, if the materialized view (MV) is invalid (specifically if its
is_active
status is
false
), StarRocks will not use it for query rewrite, even if
query_rewrite_consistency
is set to
loose
. In this scenario, StarRocks will automatically fall back and query the underlying external tables directly. Here is the detailed breakdown of how StarRocks handles these two distinct states: 1. Stale vs. Inactive (Invalid) * Stale (Data Inconsistency): The MV is "healthy" structurally, but the data in the base tables has changed.
query_rewrite_consistency = 'loose'
tells the optimizer: "I don't care if the data is old, use the MV anyway." * Inactive/Invalid (Structural Issue): The MV is marked
is_active = false
. This usually happens after a schema change on a base table (e.g., a column was dropped or renamed). When an MV is inactive, it is considered "broken" and the optimizer will ignore it entirely. 2. What happens during the Query? When you run a query that could have been rewritten by that MV: 1. The optimizer checks if any MVs match the query. 2. It finds your MV but sees that it is Inactive/Invalid. 3. Because the MV is unusable, the optimizer abandons the rewrite attempt for this specific MV. 4. The query falls back to the original execution plan, which scans the base external tables (e.g., Hive, Iceberg, etc.). How to check and fix You can verify the status and the fallback behavior using these commands: * Check Status:
Copy code
sql
    -- Look for the 'is_active' column
    SHOW MATERIALIZED VIEWS LIKE 'curated_channel_enriched';
* Verify Fallback (Explain):
Copy code
sql
    -- If you see the external table name in the scan node instead of the MV name,
    -- it has fallen back to the base tables.
    EXPLAIN SELECT ...;
* Fix an Inactive MV: If the MV is inactive due to a minor metadata sync issue, you can try to reactivate it:
Copy code
sql
    ALTER MATERIALIZED VIEW curated_channel_enriched ACTIVE;
Note: If the base table schema changed in a way that breaks the MV's SQL definition, the
ACTIVE
command will fail, and you must drop and recreate the MV.
Summary
query_rewrite_consistency = 'loose'
only bypasses the version/time check. It does not bypass the health/activity check. If the MV is invalid, StarRocks protects you by querying the source tables so you still get a result, albeit more slowly. References • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md • using_starrocks/async_mv/Materialized_view.md • using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md • sql-reference/sql-statements/materialized_view/ALTER_MATERIALIZED_VIEW.md References • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md • using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md • using_starrocks/async_mv/Materialized_view.md • knowledge/usage/best-practice.md References • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md
k
@Rocky how does exclusive_cpu_cores work? i have 12 r8g.4xlarge compute nodes with 16 vcpus each. i want to allocate half of cpus to a resource group exclusive_cpu_cores=96, but i get this error: Query 1 ERROR at Line 15: : Getting analyzing error. Detail message: exclusive_cpu_cores cannot exceed the minimum number of CPU cores available on the backends minus one [13]. does this setting refer to dedicated compute node instacnes instead of cpu cores on each instance?
r
The error you are seeing is because
exclusive_cpu_cores
is a per-node setting, not a cluster-wide setting. In StarRocks, a resource group definition is applied to every Backend (BE) or Compute Node (CN) in the cluster individually. Why you got the error You calculated $12\text{ nodes} \times 16\text{ vCPUs} \times 0.5 = 96$ cores. However, when you set
exclusive_cpu_cores = 96
, StarRocks tries to reserve 96 cores on every single node. Since your nodes only have 16 vCPUs each, this is physically impossible. The error message
exclusive_cpu_cores cannot exceed the minimum number of CPU cores available on the backends minus one [13]
tells us two things: 1. The Per-Node Limit: The maximum allowed value for
exclusive_cpu_cores
is
(min_be_cpu_cores - 1)
. StarRocks always requires at least one "Shared Core" to be left available for system tasks and other resource groups. 2. Detected Cores: The
[13]
in your error indicates that StarRocks has detected the minimum number of cores across your 12 nodes as 14. (Since $14 - 1 = 13$). Even though
r8g.4xlarge
has 16 vCPUs, StarRocks or the underlying OS may be reporting 14 usable cores, or one of your nodes might be slightly different. How to fix it To allocate half of your total cluster capacity (96 cores total across 12 nodes) to this resource group, you should set the value to 8 (or 7 if you want to stay within the 14-core limit detected by the system).
Copy code
sql
-- This will reserve 8 cores ON EACH of your 12 nodes.
-- Total reservation = 12 * 8 = 96 cores cluster-wide.
ALTER RESOURCE GROUP your_group_name 
SET ("exclusive_cpu_cores" = "8");
Key Differences to Remember: *
exclusive_cpu_cores
(Hard Isolation):
Reserves a specific number of cores on each node. These cores are "locked" for this group and cannot be used by others unless
enable_resource_group_cpu_borrowing
is true. *
cpu_weight
(Soft Isolation):
Defines the relative priority of a group. Unlike
exclusive_cpu_cores
, this is a weight (e.g.,
100
vs
200
), not a count. * Cluster vs Node: Resource management settings in StarRocks are almost always node-local. If you want a cluster-wide limit, you divide your desired total by the number of nodes. Recommendation: Run
SHOW BACKENDS;
and check the
CPUCores
column to see exactly how many cores StarRocks detects for each node. This will explain why the system is capping you at 13 instead of 15. References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • administration/management/BE_configuration.md • best_practices/audit_log_resource_group.md References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • administration/management/BE_configuration.md • best_practices/audit_log_resource_group.md
k
@Rocky how can i monitor resource group activity vs non resource group activity in grafanaa
r
To monitor resource group activity versus "non-resource group" activity, you need to look for the
default_wg
group in your Prometheus metrics. In StarRocks, any query or activity that is not explicitly assigned to a custom resource group is automatically funneled into the default group named
default_wg
. 1. Key Metrics to Monitor All resource group metrics in StarRocks include a
name
label. Use this label in your Grafana queries to differentiate between groups. Activity Type | Metric Name (Example) | Label Filter Non-RG Activity |
resource_group_running_queries
|
{name="default_wg"}
Specific RG Activity |
resource_group_running_queries
|
{name="your_group_name"}
All Custom RG Activity |
resource_group_running_queries
|
{name!="default_wg"}
2. Common Grafana Query Examples A. Compare CPU Usage (Cores) To see how many CPU cores your custom groups are using versus the rest of the system: * Custom Groups:
sum(resource_group_inuse_cpu_cores{name!="default_wg"})
* Non-RG (Default):
resource_group_inuse_cpu_cores{name="default_wg"}
B. Monitor Query Concurrency To see if your custom resource groups are "crowding out" the default traffic: * Custom Groups:
sum(resource_group_running_queries{name!="default_wg"})
* Non-RG (Default):
resource_group_running_queries{name="default_wg"}
C. Memory Usage (Bytes) * Custom Groups:
sum(resource_group_mem_inuse_bytes{name!="default_wg"})
* Non-RG (Default):
resource_group_mem_inuse_bytes{name="default_wg"}
3. Recommended Grafana Setup 1. Variable: Create a Grafana variable named
$resource_group
using the query
label_values(resource_group_total_queries, name)
. This allows you to filter your entire dashboard by specific groups. 2. Stacked Graph: Create a stacked area chart for
resource_group_inuse_cpu_cores
grouped by the
name
label. This visually shows the proportion of the cluster being consumed by each group versus the
default_wg
. 3. FE vs BE Metrics: * Use FE metrics (starting with
starrocks_fe_query_resource_group...
) for high-level query counts and latency percentiles. * Use BE metrics (starting with
resource_group_...
) for actual resource consumption like CPU and Memory. Important Note on Metric Visibility The
query_resource_group
metrics are lazy-initialized. If you just created a resource group and haven't run a query against it yet, that group's name will not appear in Prometheus/Grafana until the first query is processed. References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • administration/management/monitoring/metrics.md References • administration/management/resource_management/resource_group.md • administration/management/monitoring/metrics.md • faq/resource_isolation_faq.md
k
@Rocky how to set other values so this query works: Query 1 ERROR at Line 15: : Getting analyzing error. Detail message: Exactly only one of 'cpu_weight', 'cpu_weight_percent', 'exclusive_cpu_cores' or 'exclusive_cpu_percent' can be present and positive at the same time. ALTER RESOURCE GROUP high_priority_contextual WITH ( 'exclusive_cpu_percent' = '50' );
r
The error occurs because StarRocks enforces a strict rule: a resource group can only have one type of CPU allocation active at any time. Because your resource group likely already has a value for
cpu_weight
(the default for shared groups) or
exclusive_cpu_cores
, you must explicitly set those existing values to
0
in the same
ALTER
statement to "deactivate" them. The Solution To switch to
exclusive_cpu_percent
, you should set the other three CPU parameters to
0
in your query:
Copy code
sql
ALTER RESOURCE GROUP high_priority_contextual SET (
    'cpu_weight' = '0',
    'cpu_weight_percent' = '0',
    'exclusive_cpu_cores' = '0',
    'exclusive_cpu_percent' = '50'
);
Why this is required StarRocks categorizes resource groups into two modes based on these parameters: 1. Shared Mode: Uses
cpu_weight
or
cpu_weight_percent
. These groups compete for "Shared Cores" based on their relative weight. 2. Exclusive Mode: Uses
exclusive_cpu_cores
or
exclusive_cpu_percent
. These groups reserve specific physical cores that others cannot use. Since a group cannot be both "Shared" and "Exclusive" at the same time, any attempt to set an Exclusive parameter while a Shared parameter is still positive (or vice versa) results in the error you saw. Important Notes: * Version Note: Percentage-based controls (
exclusive_cpu_percent
and
cpu_weight_percent
) were introduced in StarRocks 4.1. If you are on an older version, you must use
exclusive_cpu_cores
. * Total Limit: Just like with cores, the total
exclusive_cpu_percent
across all resource groups must leave at least one core's worth of percentage available for the system/default groups. * Memory: Ensure you have also defined a
mem_limit
for the group, as it is a required parameter for resource groups to function properly. References • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • faq/resource_isolation_faq.md • sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md References • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • faq/resource_isolation_faq.md • sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md • best_practices/audit_log_resource_group.md References • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • faq/resource_isolation_faq.md
k
@Rocky if i have materialized views defined in a resource group that many many queries will be rewritten to that were originally to an external iceberg table, how will resources be allocated to the rewritten queries
r
When a query is rewritten to use a Materialized View (MV), the resource allocation is determined by the session/user context, not the properties of the MV itself. Here is the breakdown of how StarRocks handles this: 1. Foreground Queries (Rewritten Queries) Even if an MV has a
'resource_group' = 'some_group'
property, that setting only applies to the background refresh task. For a user query that gets rewritten: * Classifier Match: StarRocks assigns the query to a resource group at the very beginning of the execution based on Classifiers (e.g., the
user
,
role
, or
source_ip
of the person running the query). * The Rewrite: The optimizer rewrites the query to use the MV during the planning phase. This does not change the resource group that was already assigned to the query. * Result: The rewritten query will continue to run under the resource group of the user who initiated it (or
default_wg
if no classifier matches). 2. Background Refresh Tasks The
resource_group
property in the MV definition is strictly for Asynchronous Refresh operations. * If you set
'resource_group' = 'heavy_tasks'
, then the
INSERT OVERWRITE
job that updates the MV data will run in that group. * If you do not set this property, the refresh task runs in the system-defined
default_mv_wg
group. 3. How to target rewritten queries specifically If your goal is to ensure that queries hitting these MVs are isolated from other traffic, you have two main options: * Database Classifier: If you store all your "fast" MVs in a specific database (e.g.,
mv_db
), you can create a resource group with a classifier for that database:
Copy code
sql
    CREATE RESOURCE GROUP fast_queries
    TO (db = 'mv_db') -- All queries against tables/MVs in this DB use this RG
    WITH (...);
* Cost-based Classifier (v3.1.4+): Since rewritten queries often have a much lower estimated cost than the original Iceberg scan, you can use the
plan_cpu_cost_range
classifier to shunt low-cost queries into a specific group:
Copy code
sql
    CREATE RESOURCE GROUP small_queries
    TO (plan_cpu_cost_range = '(0, 1000]')
    WITH (...);
How to Verify You can verify which resource group a rewritten query is actually using by running:
Copy code
sql
EXPLAIN VERBOSE <your_query>;
Look for the
ResourceGroup
field in the output. It will show the group assigned by the classifier regardless of whether the plan shows a
SCAN [your_mv_name]
. References • administration/management/resource_management/resource_group.md • best_practices/audit_log_resource_group.md • faq/resource_isolation_faq.md References • best_practices/audit_log_resource_group.md • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md
k
@Rocky is concurrent queries limits configurable? im seeing peridoically during large bursts of traffic queries are taking far longer than expected due to waits 23.46s in the scan I/O queue
r
Yes, concurrent query limits are highly configurable in StarRocks. The "waits in scan I/O queue" you are seeing is a classic symptom of I/O oversubscription, where the number of concurrent scan tasks exceeds the available threads in the Back-End (BE) scan thread pool. To resolve this, you should use Query Queues to gate traffic at the gateway (FE) before it saturates the BE internal queues. 1. Configure Concurrency Limits (Resource Group Level) You can set a hard limit on how many queries a specific resource group can run simultaneously. If you are on version 3.1.4 or later, exceeding this limit will place queries into a queue rather than failing them.
Copy code
sql
ALTER RESOURCE GROUP your_group_name SET (
    'concurrency_limit' = '20' -- Adjust based on your cluster size/CPU
);
* Why this helps: Instead of 100 queries all hitting the I/O threads at once and fighting for disk/network bandwidth (causing the 23s wait), only 20 will run, and the other 80 will wait in an "orderly" queue at the FE level. 2. Enable Global Query Queuing If you want to protect the entire cluster regardless of the resource group, you can set global thresholds:
Copy code
sql
-- Set these as global variables
SET GLOBAL query_queue_concurrency_limit = 100;
SET GLOBAL query_queue_mem_used_pct_limit = 0.9; -- Queue if memory > 90%
3. Tuning the "Scan I/O Queue" (BE Level) The specific wait time you see (
IOTaskWaitTime
in profiles) refers to the BE's internal
scanner_thread_pool
. If your hardware (e.g., high-performance SSDs or large 10Gbps+ network for Iceberg) can handle more concurrency, you can increase the pool size in `be.conf`: *
scanner_thread_pool_thread_num
: (Default 48) The number of threads used for scanning data. Increase this if your CPU and I/O bandwidth have headroom during the 23s wait. *
max_hdfs_scanner_num
: (Default 50, v3.2+) Specifically limits concurrent scanners for external tables like Iceberg. If your bursts are hitting Iceberg, this is likely the bottleneck. Recommended Action Plan 1. Check FE vs BE Queuing: Check the
Query Queue
metrics in Grafana. If the FE queue is empty but
IOTaskWaitTime
is high, your BE is over-saturated. 2. Apply `concurrency_limit`: Set this on your most "bursty" resource group to a value that keeps
IOTaskWaitTime
under 1–2 seconds. 3. For Iceberg: If you are hitting the Iceberg tables mentioned earlier, check if you've reached the
max_hdfs_scanner_num
limit. Increasing this slightly (e.g., to 100) can help if your remote storage (S3/HDFS) is fast enough. References • administration/management/resource_management/resource_group.md • administration/management/resource_management/query_queues.md • administration/management/BE_configuration.md • best_practices/query_tuning/query_profile_operator_metrics.md References • administration/management/BE_configuration.md • administration/management/resource_management/query_queues.md
k
@Rocky how can i reserve scan task IO for resource groups so that we are never competing with other scan tasks for a specific RG
r
To ensure that your resource group's scan tasks never compete with other workloads, you must use Hard Isolation (Exclusive Mode). In StarRocks, when you define an Exclusive Resource Group, the system creates dedicated thread pools for that group's scan and execution tasks. This effectively "reserves" the capacity to process I/O at the thread level. How it Works Queries in a standard (Shared) resource group use a common pool called
pip_scan_com
. If a heavy batch job saturates that pool, your interactive queries will wait in the "Scan I/O Queue." When you set
exclusive_cpu_percent
(or
exclusive_cpu_cores
), StarRocks creates the following dedicated pools for that specific Resource Group (RG): *
pip_scan_<rg_id>
: Dedicated threads for internal table scans. *
pip_con_scan_<rg_id>
: Dedicated threads for external table scans (like your Iceberg tables). *
pip_exec_<rg_id>
: Dedicated threads for query execution logic. Because these pools are reserved for your RG, your scan tasks never enter the same queue as queries from other resource groups. Configuration To "reserve" these resources and prevent competition, configure your group as follows:
Copy code
sql
ALTER RESOURCE GROUP high_priority_contextual SET (
    'cpu_weight' = '0',
    'cpu_weight_percent' = '0',
    'exclusive_cpu_cores' = '0',
    'exclusive_cpu_percent' = '50' -- Reserves 50% of CPU & dedicated scan threads
);
Important Distinctions 1. Thread vs. Hardware Isolation: This reserves the threads that perform the scanning. It prevents "head-of-line blocking" where your query is stuck behind a long-running scan. However, it does not physically partition the disk's IOPS or the network's bandwidth—the underlying hardware is still shared. 2. Borrowing: By default, if your reserved threads are idle, other groups can "borrow" them to avoid wasting CPU. If you want strict isolation where others can never use your cores (even when idle), set this BE configuration:
Copy code
sql
    -- Run this to disable borrowing cluster-wide
    UPDATE information_schema.be_configs SET VALUE = "false" WHERE NAME = "enable_resource_group_cpu_borrowing";
3. Verification: You can verify that your dedicated scan pools have been created by running:
Copy code
sql
    SELECT * FROM information_schema.be_threads WHERE name LIKE '%pip_scan%';
You should see threads named
pip_scan_<your_rg_id>
instead of just the common
pip_scan_com
. References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • best_practices/audit_log_resource_group.md References • administration/management/resource_management/resource_group.md • administration/management/monitoring/metrics.md • best_practices/audit_log_resource_group.md • administration/management/BE_configuration.md
k
@Rocky i have ETL workloads and applications needing data from the same MVs in the same database. I want the ETL workload queries to route to different resource group, and application resource group with dedicated cores how can i separate queries even though they are hitting the same MV
r
To separate ETL and application workloads hitting the same Materialized View (MV), you should use Classifiers based on the identity (User/Role) or the source (IP) of the connection, rather than the database object being accessed. Since both workloads hit the same MV in the same database, a database-level classifier (
db='your_db'
) would not be enough to distinguish them. Instead, follow this strategy: 1. Identify Workloads via User or Role The most robust way to separate these is to use different database users or roles for your ETL pipeline and your application. * Application User:
app_user
* ETL User:
etl_user
2. Create the Resource Groups You can then create two resource groups. Use
exclusive_cpu_cores
for the application to give it dedicated, reserved CPU and I/O threads. For the Application (Dedicated Resources) This group will have a "hard limit" reservation. These cores are removed from the shared pool and reserved only for the application.
Copy code
sql
CREATE RESOURCE GROUP app_link_group
TO (user='app_user')
WITH (
    'exclusive_cpu_percent' = '40', -- Reserves 40% of all BE cores exclusively
    'mem_limit' = '30%',
    'concurrency_limit' = '100'
);
For the ETL Workload (Shared/Background) ETL queries usually benefit from
cpu_weight
, which allows them to use idle resources but yield to other groups when the system is busy.
Copy code
sql
CREATE RESOURCE GROUP etl_group
TO (user='etl_user')
WITH (
    'cpu_weight' = '10', -- Soft limit: weight-based sharing
    'mem_limit' = '40%',
    'concurrency_limit' = '10' -- Limit ETL concurrency to protect the app
);
3. Advanced Separation (If using the same User) If you cannot change the database user, you can distinguish them using other classifier properties: * Source IP: If your ETL runs on a specific server/subnet.
Copy code
sql
    TO (user='common_user', source_ip='10.0.1.0/24') -- Routes ETL traffic by IP
* Query Cost (v3.1.4+): If ETL queries are significantly "heavier" (e.g., scanning more data), you can route them based on the optimizer's estimated cost.
Copy code
sql
    TO (plan_cpu_cost_range = '(10000.0, 99999999.0]') -- Routes heavy queries to ETL group
4. Manual Session Routing (The "Override" Method) If your application or ETL tool allows running a "startup SQL" or session command, you can bypass the classifier entirely:
Copy code
sql
-- In the ETL job session
SET resource_group = 'etl_group';
SELECT * FROM your_mv ...
This command forces the current connection into the specified group regardless of what the classifiers say. Summary of Resource Allocation * Application Group: By using
exclusive_cpu_percent
, StarRocks creates dedicated
pip_exec
and
pip_scan
thread pools for this group. Even if the ETL group initiates massive scans on the same MV, the application's scan tasks will run in their own reserved threads and won't be queued behind the ETL tasks. * ETL Group: Will compete in the shared pool (
pip_scan_com
) with any other non-exclusive workloads, ensuring they don't impact your "Exclusive" application cores. References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • administration/management/monitoring/metrics.md
k
@Rocky could starrocks support <1 sec queries for 4 billion rows with sorting on combinations of columns in shared data deployment with s3 iceberg
r
Yes, StarRocks can support sub-second queries for 4 billion rows in a shared-data (Cloud-Native) or Iceberg deployment, but achieving this requires specific physical design strategies—primarily Data Caching and Sort Key optimization. At this scale, the bottleneck is typically I/O latency from S3. Here is how to configure StarRocks to meet the <1s requirement: 1. The "Hot Data" Strategy (Data Cache) In a shared-data deployment or when using an Iceberg Catalog, StarRocks does not have to pull 4B rows from S3 every time. * Enable Data Cache: This is mandatory for sub-second performance. It caches S3 data blocks onto the local SSD/NVMe of your Compute Nodes (CN). * Performance: Benchmarks show that once data is cached, shared-data performance is comparable to (and sometimes faster than) local-disk deployments. * Iceberg Metadata: StarRocks caches Iceberg manifest files and metadata in memory and on disk. This prevents the "metadata bottleneck" where just planning the query takes >1s. 2. Sorting on Combinations of Columns To handle sorting/filtering on combinations of columns, you must use the Sort Key (the
ORDER BY
clause in your table or MV definition). * Prefix Indexing: StarRocks automatically builds a "Short Key Index" on the first ~3 columns (up to 36 bytes) of your sort key. If your query filters by the leading columns of the sort key, it can perform millisecond point-seeks across 4 billion rows. * Zone Maps (Pruning): For columns not at the front of the sort key, StarRocks maintains Min/Max indexes for every 64KB page. If your data is sorted by
(col_a, col_b, col_c)
, even a filter on
col_c
will be highly effective because the range of values in each page is minimized, allowing StarRocks to skip ("prune") the vast majority of data. * Z-Order Sorting: While StarRocks' native sort key is linear (lexicographical), you can use Materialized Views to effectively create multiple "perspectives" of the data sorted differently to satisfy different column combinations. 3. The "Gold Standard" for Iceberg: Async MVs If you are querying Iceberg directly and need <1s latency consistently, the most reliable path is creating an Asynchronous Materialized View (MV) on top of the Iceberg table. * Pre-Aggregation/Sorting: The MV can pre-calculate sums/counts or simply re-sort the 4B rows from Iceberg into StarRocks' native format. * Query Rewrite: Users continue querying the Iceberg table, but the optimizer transparently redirects the query to the MV. * Native Indexes: Once the data is in an MV, you can use Bitmap Indexes or Bloom Filter Indexes on those "combinations of columns" that aren't in the primary sort key. Recommended Configuration If you are currently seeing high wait times, check these settings: 1. Check Cache Hit Rate:
SELECT * FROM information_schema.be_metrics WHERE name LIKE '%cache_hit%';
2. Sort Key Length: Ensure your most frequent filter columns are in the
ORDER BY
clause. 3. Scan Range: If a query scans too many rows (e.g., billions of rows without filters), it will not be <1s regardless of architecture. Use
EXPLAIN ANALYZE
to see
PushDownPredicates
and
RowsRead
. Summary of Performance Expectation Method | Expected Latency (4B Rows) | Best For Iceberg (No Cache) | 10s - 120s+ | Cold/Ad-hoc analysis Iceberg + Data Cache | 1s - 5s | Frequent dashboard queries StarRocks Native/MV + Sort Key | < 100ms - 800ms | High-concurrency apps / sub-second References • deployment/shared_data/feature-support-shared-data.md • best_practices/table_clustering.md • using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md
k
@Rocky give example to configure for an mv with 4 billion rows: Zone Maps (Pruning): For columns not at the front of the sort key, StarRocks maintains Min/Max indexes for every 64KB page. If your data is sorted by
(col_a, col_b, col_c)
, even a filter on
col_c
will be highly effective because the range of values in each page is minimized, allowing StarRocks to skip ("prune") the vast majority of data. * Z-Order Sorting: While StarRocks' native sort key is linear (lexicographical), you can use Materialized Views to effectively create multiple "perspectives" of the data sorted differently to satisfy different column combinations.
r
To achieve sub-second queries on 4 billion rows with various column combinations, you should combine Materialized Views (MVs) with strategic Sort Keys and Secondary Indexes. While some systems use a single Z-order index, StarRocks' high-performance architecture relies on Multiple Perspectives: creating different MVs sorted by different column sets so the optimizer can choose the best "physical layout" for each query. Example: Optimizing 4 Billion Rows from Iceberg Suppose your base Iceberg table has columns
user_id
,
event_time
,
region
, and
category
. 1. The Primary Perspective (Sorted by User & Time) This MV is optimized for queries filtering by
user_id
or
event_time
. The
ORDER BY
clause defines the Sort Key, which creates the sparse prefix index and optimizes the 64KB page Zone Maps.
Copy code
sql
CREATE MATERIALIZED VIEW mv_user_behavior
-- Partitioning by time allows for incremental refreshes and partition pruning
PARTITION BY date_trunc('day', event_time)
DISTRIBUTED BY HASH(user_id) BUCKETS 64
REFRESH ASYNC EVERY(INTERVAL 1 HOUR)
-- The Sort Key: Mandatory for <1s range/point lookups
ORDER BY (user_id, event_time)
AS 
SELECT user_id, event_time, region, category, revenue
FROM iceberg_catalog.db.raw_events;
2. The Second Perspective (Handling Different Combinations) If you frequently filter by
region
and
category
, the first MV will be slow because those columns are at the end of the sort key. You create a second perspective (another MV) sorted differently:
Copy code
sql
CREATE MATERIALIZED VIEW mv_regional_analysis
PARTITION BY date_trunc('day', event_time)
DISTRIBUTED BY HASH(region) BUCKETS 64
REFRESH ASYNC EVERY(INTERVAL 1 HOUR)
-- New Sort Key for a different combination of columns
ORDER BY (region, category, event_time)
AS 
SELECT region, category, event_time, user_id, revenue
FROM iceberg_catalog.db.raw_events;
_The StarRocks optimizer will automatically route your query to
mv_regional_analysis
if you filter by
region
, even if your SQL queries the base Iceberg table._ How Zone Maps and Pruning Work At 4 billion rows, StarRocks divides data into 64KB pages. * For Sort Key Columns: Because data is physically ordered, the Min/Max values for
user_id
in each page will be very tight (e.g., Page 1: IDs 1–100, Page 2: 101–200). A query for
user_id = 150
skips almost all 4 billion rows instantly. * For Non-Sort Columns: StarRocks still maintains Zone Maps for
revenue
or
category
. If your data has a natural correlation (e.g., all
high_revenue
events happen in certain time blocks), the Zone Maps will still prune significantly even if they aren't the primary sort key. Adding Secondary Indexes for "Point" Combinations For columns that are highly unique (high cardinality) but aren't in your sort key, add a Bitmap Index to the MV:
Copy code
sql
-- Speeds up "category = 'electronics'" if it's not the first sort column
CREATE INDEX idx_category ON mv_user_behavior(category) USING BITMAP;

-- For extremely high-cardinality IDs, use Bloom Filter
ALTER MATERIALIZED VIEW mv_user_behavior SET ("bloom_filter_columns" = "session_id");
Summary of Best Practices for 4B Rows 1. Prefix Index: Put the most frequent filter column first in
ORDER BY
. 2. Multiple MVs: Don't try to make one MV do everything. Create 2-3 MVs for your 2-3 most common "column combinations." 3. Data Caching: Ensure
enable_storage_cache = true
so the MV data stays on the local SSDs of the StarRocks nodes, avoiding S3 latency. 4. Z-Order Note: While StarRocks supports Z-order for local table maintenance (
OPTIMIZE
), for MVs, the "Multiple Perspectives" (multiple sort keys) approach is more robust for sub-second production workloads. References • table_design/indexes/Bitmap_index.md
k
@Rocky there are 8 sort options i need to support, and having an mv to support each one is too much overhead. is there an alternative to storing multiple complete mvs to support all sort options
r
Supporting 8 different sort options with 8 separate Materialized Views (MVs) is indeed high overhead in terms of storage and ingestion. For 4 billion rows, you should use a "Multi-Dimensional Clustering" strategy instead of full replication. Here are the three best alternatives to achieve sub-second performance across various column combinations without 8 MVs: 1. The "Manual Z-Order" (using
encode_sort_key
)
StarRocks v3.x introduced
encode_sort_key
, which allows you to create a Composite Sort Key. This is the closest equivalent to a Z-order or Hilbert curve. You pack multiple columns into a single binary column and use that as your
ORDER BY
key. This allows a single MV to provide decent pruning performance for multiple columns simultaneously, rather than favoring only the first column in a list. Example:
Copy code
sql
CREATE MATERIALIZED VIEW mv_multisort
REFRESH ASYNC EVERY (INTERVAL 1 HOUR)
-- Define a composite sort key
ORDER BY (composite_key)
AS
SELECT 
    *,
    -- Encodes 4 columns into one binary sortable key
    encode_sort_key(col1, col2, col3, col4) as composite_key
FROM iceberg_catalog.db.large_table;
* How it works: The
encode_sort_key
ensures that the interleaved values of all four columns are used to order the data. This provides "fair" pruning for all 4 columns, which is much more efficient for the "8-option" problem than a standard linear sort. 2. Secondary Indexes (Bitmap & Bloom Filter) If your 8 sort options are primarily used for filtering before the final sort, you can use secondary indexes. In StarRocks, these are "pointers" and do not replicate the whole table. * Bitmap Indexes: Use these for columns with low to medium cardinality (e.g.,
state
,
category
,
gender
). * Bloom Filter Indexes: Use these for high cardinality columns (e.g.,
user_id
,
order_id
) where you do exact equality checks (
WHERE id = '...'
). * Inverted Indexes (v3.3+): Use these for text search or range queries on non-sorted columns. Example:
Copy code
sql
-- Add indexes to a single "Master MV" instead of creating new ones
CREATE INDEX idx_cat ON mv_master(category) USING BITMAP;
ALTER MATERIALIZED VIEW mv_master SET ("bloom_filter_columns" = "user_id, device_id");
3. Leverage "Zone Map" Pruning Every column in StarRocks has an automatic Min/Max index at the 64KB page level. If your data has any natural correlation (e.g.,
order_id
generally increases with
event_time
), the system will skip pages even if the column isn't in the sort key. To maximize this: * Sort by the highest cardinality column that appears in the most queries. * The optimizer will use the page-level Zone Maps to prune the other 7 columns. On 4 billion rows, even a 50% pruning via Zone Maps combined with the Data Cache (SSD) often results in sub-second responses. Summary Strategy Requirement | Recommended Solution | Overhead 8 sort/filter combos |
encode_sort_key
on the top 4–5 cols | Low (1 binary col) Point lookups on IDs | Bloom Filter Index | Very Low Filtering on Categories | Bitmap Index | Very Low Extreme Performance | 2 MVs (1 sorted by Time, 1 by
encode_sort_key
) | Medium Recommendation: Start by creating one MV using
encode_sort_key
for your most frequent 4 columns and add Bitmap/Bloom Filter indexes for the others. This single MV will likely handle all 8 options under 1 second once the data is cached. References • table_design/indexes/Prefix_index_sort_key.md • sql-reference/sql-functions/utility-functions/encode_sort_key.md • best_practices/table_clustering.md • table_design/indexes/Bitmap_index.md
• table_design/indexes/Bloomfilter_index.md • using_starrocks/Materialized_view-single_table.md • administration/management/resource_management/spill_to_disk.md • administration/management/monitoring/metrics.md • administration/management/resource_management/resource_group.md • data_source/catalog/iceberg/iceberg_catalog.md • data_source/feature-support-data-lake-analytics.md • faq/resource_isolation_faq.md • quick_start/iceberg.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md • table_design/indexes/indexes.md • using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md • table_design/indexes/Bitmap_index.md • using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md • table_design/indexes/Bloomfilter_index.md • best_practices/table_clustering.md • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • administration/management/monitoring/metrics.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • deployment/shared_data/feature-support-shared-data.md • best_practices/table_clustering.md • using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md • table_design/indexes/Bitmap_index.md • data_source/feature-support-data-lake-analytics.md • data_source/catalog/iceberg/iceberg_catalog.md
• quick_start/iceberg.md • data_source/catalog/iceberg/iceberg_rest_s3.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md • table_design/indexes/Prefix_index_sort_key.md • sql-reference/sql-functions/utility-functions/encode_sort_key.md • best_practices/table_clustering.md • table_design/indexes/Bitmap_index.md • table_design/indexes/Bloomfilter_index.md • using_starrocks/Materialized_view-single_table.md • administration/management/resource_management/spill_to_disk.md • administration/management/monitoring/metrics.md • administration/management/resource_management/resource_group.md • data_source/catalog/iceberg/iceberg_catalog.md • data_source/feature-support-data-lake-analytics.md • faq/resource_isolation_faq.md • quick_start/iceberg.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md • table_design/indexes/indexes.md • using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md • table_design/indexes/Bitmap_index.md • using_starrocks/async_mv/use_cases/data_lake_query_acceleration_with_materialized_views.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md • table_design/indexes/Bloomfilter_index.md • best_practices/table_clustering.md • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • administration/management/monitoring/metrics.md
k
@Rocky for async mat view, is it possible to configure multiple individual sorting columns so multiple sorting data structures are stored on disk
r
In StarRocks, a single physical table or Materialized View can have only one primary sort key (defined by the
ORDER BY
clause). This physically reorders the rows on disk to enable the high-performance "Prefix Index" and "Zone Map" pruning. However, you can configure additional data structures on disk within that same MV to support multiple columns without creating 8 separate MVs. Here is how to achieve "multi-index" performance on a single MV: 1. Inverted Indexes (The "Alternative" Structure) Starting from v3.3, StarRocks supports Inverted Indexes. This is a separate data structure stored on disk that maps column values to row IDs. * Why it works: While it doesn't "sort" the table, it allows StarRocks to instantly find the rows matching your filter across any of your 8 columns. Once the rows are found via the index, the final sort becomes extremely fast because the data volume is already reduced. * Support: It supports equality, range (
>
,
<
), and even
MATCH
(text search) queries.
Copy code
sql
-- Adding an inverted index to an existing MV
CREATE INDEX idx_col_1 ON my_mv(col_1) USING INVERTED;
CREATE INDEX idx_col_2 ON my_mv(col_2) USING INVERTED;
2. The "Z-Order" Sort Key (Multi-Column Balance) If you need multiple columns to have "equal weight" in pruning, do not use a standard
ORDER BY (a, b, c, d)
. Instead, use
encode_sort_key
. * Lexicographical Sort (Standard):
ORDER BY (time, category, user)
— Pruning is 100% efficient for
time
, but if you filter only by
user
, pruning is nearly 0%. * Multi-Dimensional Sort: Using
encode_sort_key(col1, col2, col3)
creates a interleaved (Z-order-like) key. It provides "good" pruning for all three columns simultaneously, rather than "perfect" for one and "none" for others.
Copy code
sql
CREATE MATERIALIZED VIEW mv_optimized
ORDER BY (composite_key)
AS SELECT 
  *, 
  encode_sort_key(col1, col2, col3, col4) as composite_key
FROM base_table;
3. Secondary Indexes (Bitmap & Bloom Filter) These are lightweight structures that don't replicate the data but exist on disk to skip data blocks: * Bitmap Index: Best for columns with <10,000 distinct values (e.g.,
city
,
status
). It creates a bitmask on disk. * Bloom Filter: Best for high-cardinality "ID" columns (e.g.,
order_id
,
user_id
) to speed up equality checks. Summary: The "1 MV" Configuration for 8 Options Instead of 8 MVs, use one "Master MV" configured like this: 1. Primary Sort Key: Set
ORDER BY
to the 1 or 2 most common columns (e.g.,
event_time
). 2. Inverted Indexes: Create Inverted Indexes on the other 6 columns to handle range and equality filters. 3. Bloom Filters: Enable Bloom Filters on high-cardinality IDs. 4. Data Cache: Ensure the MV's data and its indexes are cached in the CN local SSD (
enable_storage_cache = true
). Result: When a query filters by any of the 8 columns, the Inverted Index or Bloom Filter identifies the relevant data pages in milliseconds. The CN nodes pull those specific pages from the local SSD cache, and the <1s requirement is met without the storage cost of 8 MVs. References • table_design/indexes/Inverted_index.md • sql-reference/sql-functions/utility-functions/encode_sort_key.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md • table_design/indexes/Prefix_index_sort_key.md References • using_starrocks/caching/query_cache.md • best_practices/table_clustering.md
k
@Rocky
Copy code
sql
CREATE MATERIALIZED VIEW mv_optimized
ORDER BY (composite_key)
AS SELECT 
  *, 
  encode_sort_key(col1, col2, col3, col4) as composite_key
FROM base_table;
i can sort by col3 and not some special value being returned by encode_sort_key right
r
To answer your specific question: Yes, you can write
ORDER BY col3
in your SQL queries
, and you do not need to reference the "special value" (the binary blob) returned by
encode_sort_key
. StarRocks' optimizer will transparently use the Materialized View (MV) and its columns. However, there is a critical distinction regarding performance and how
encode_sort_key
actually works
: 1.
encode_sort_key
is NOT Z-Order
Despite the common misconception,
encode_sort_key
in StarRocks creates a Lexicographical (Linear) Sort, not a multi-dimensional Z-order. *
ORDER BY (encode_sort_key(col1, col2, col3))
is physically the same as
ORDER BY (col1, col2, col3)
. * Pruning Impact: If you filter by
col3
alone, you will not get prefix-index pruning. The data is sorted by
col1
first, then
col2
. * Sorting Impact: If you query
ORDER BY col3
, StarRocks will still have to perform a full sort in memory/spill, because the data on disk is not globally ordered by
col3
. 2. Why
encode_sort_key
exists
This function is a technical helper used primarily when you want to use complex expressions or multiple columns as a sort key in versions or table types where the
ORDER BY
syntax was more restrictive. In modern StarRocks (v3.x+), you can simply define:
Copy code
sql
CREATE MATERIALIZED VIEW mv_optimized
-- Native multi-column sort key
ORDER BY (col1, col2, col3, col4)
AS SELECT col1, col2, col3, col4, revenue FROM base_table;
This is preferred over
encode_sort_key
because it is more readable and the optimizer understands it better. 3. The Solution for "8 Sort Options" If you have 8 different columns that users might sort or filter by, and you want to avoid 8 MVs, do not rely on a single Sort Key. Instead, use Inverted Indexes (introduced in v3.3). Inverted Indexes are a separate data structure on disk that allows StarRocks to find matching rows for any of your 8 columns instantly, regardless of sort order. Recommended Configuration for your MV: 1. Pick the "Master" Sort Key: Choose the 1 or 2 columns used in ~80% of queries (usually
timestamp
or
tenant_id
) and put them in the
ORDER BY
clause. 2. Add Inverted Indexes for the rest: For the other 6 columns, add an inverted index.
Copy code
sql
CREATE MATERIALIZED VIEW mv_final
PARTITION BY ...
DISTRIBUTED BY ...
ORDER BY (primary_col) -- The most common filter/sort
AS SELECT col1, col2, col3, col4, col5, col6, col7, col8 FROM base_table;

-- Add inverted indexes to handle the other 7 "perspectives"
CREATE INDEX idx_col2 ON mv_final(col2) USING INVERTED;
CREATE INDEX idx_col3 ON mv_final(col3) USING INVERTED;
-- ... repeat for other columns
Summary of what happens at query time: * Query:
SELECT * FROM base_table WHERE col3 = 'XYZ' ORDER BY col3
* StarRocks Action: 1. Uses the Inverted Index on
col3
to pinpoint the exact rows in the 4B-row table (skipping 99% of data). 2. Pulls only those specific blocks from disk/cache. 3. Performs a "Top-N" sort on the much smaller result set. 4. Result: Sub-second response without the storage overhead of 8 MVs. Conclusion Don't use
encode_sort_key
to solve the "8 options" problem; it won't provide the multidimensional pruning you expect. Instead, use a standard
ORDER BY
for your primary column and Inverted Indexes for the other seven. References • knowledge/usage/best-practice.md • table_design/indexes/indexes.md • table_design/indexes/vector_index.md • table_design/indexes/Bitmap_index.md
k
@Rocky how does inverted indexes in starrocks make sorting faster on those columns
r
In StarRocks, an inverted index does not physically store the data in a sorted order like the primary
ORDER BY
sort key does. Instead, it makes sorting faster by drastically reducing the input volume to the sorting process. Here is exactly how it works under the hood for your 4 billion row table: 1. Indirect Speedup: Volume Reduction If you run a query like
SELECT * FROM table WHERE col3 = 'A' ORDER BY col3
, the inverted index does the heavy lifting before the sort even begins: * Without Index: StarRocks would have to scan 4 billion rows, filter them, and then send the result to the Sort operator. * With Inverted Index: The index acts as a "map" that tells StarRocks the exact row IDs where
col3 = 'A'
. The engine then pulls only those specific data blocks from disk. * Result: Sorting 1,000 rows (the filtered result) takes a few milliseconds, whereas sorting 4 billion rows takes minutes. 2. Top-N Optimization When you use
ORDER BY ... LIMIT N
, StarRocks combines the inverted index with a Top-N sort. 1. The inverted index locates the rows matching your filter. 2. Instead of sorting everything, the system uses a Priority Queue (Heap) to keep track of only the top $N$ values as it reads the data blocks identified by the index. 3. This avoids a full "materialized" sort on disk, keeping the operation entirely in memory. 3. "Late Materialization" StarRocks uses a technique called Late Materialization with its indexes. * It first uses the inverted index to find the row IDs. * It then evaluates the
ORDER BY
column for only those row IDs. _ It only fetches the _other* requested columns (like a 100-column wide row) for the final result set after the sorting/filtering is done. This saves massive amounts of I/O. Summary: Sort Key vs. Inverted Index Feature | ***Sort Key (
ORDER BY
)*** | ***Inverted Index*** Data Order | Physically ordered on disk. | Unordered mapping (Value → Row ID). Sort Speed | Zero sort needed (if it matches the prefix). | Physical sort still needed, but on much less data. Filtering | Best for range queries on the first few columns. | Best for "needle in a haystack" filtering on any column. Storage | None (it's just the table order). | Extra disk space (approx. 10-30% of column size). The bottom line: For your 8 sort options, the inverted index makes the "filtering" phase so fast that the "sorting" phase becomes trivial. This is why you can achieve <1s response times without having 8 separate physical copies of the data. References • table_design/indexes/inverted_index.md • table_design/indexes/indexes.md • best_practices/table_clustering.md • table_design/indexes/Prefix_index_sort_key.md References • faq/Sql_faq.md • table_design/indexes/inverted_index.md • table_design/indexes/Prefix_index_sort_key.md • best_practices/table_clustering.md
k
@Rocky give query to get disk size of materialzied views
r
Because Materialized Views (MVs) are physically stored as tables in StarRocks, you can use the standard table storage commands. Option 1: Using
SHOW DATA
(Recommended)
This is the most reliable way to see the actual disk space occupied, including all replicas and indexes. * For all tables/MVs in the current database:
Copy code
sql
    SHOW DATA;
* For a specific Materialized View:
Copy code
sql
    SHOW DATA FROM database_name.mv_name;
This will return
Size
,
ReplicaCount
, and
RowCount
.
Option 2: Querying
information_schema
If you need to get the size of only Materialized Views across the entire cluster in a single result set, use this query:
Copy code
sql
SELECT 
    TABLE_SCHEMA AS database_name,
    TABLE_NAME AS mv_name,
    DATA_LENGTH AS size_bytes,
    format_bytes(DATA_LENGTH) AS human_readable_size,
    TABLE_ROWS AS row_count
FROM information_schema.tables 
WHERE TABLE_TYPE = 'VIEW' -- Note: ASYNC MVs are often listed as VIEW or BASE TABLE depending on version
   OR TABLE_NAME IN (SELECT TABLE_NAME FROM information_schema.materialized_views);
Important Notes: 1. Index Space: The size reported by
SHOW DATA
includes the storage for the Inverted Indexes, Bitmap Indexes, and the primary Sort Key. 2. Replica Overhead: If you have a replication factor of 3, the total disk consumption will be roughly 3x the logical data size.
SHOW DATA
typically shows the sum of all replicas. 3. Accuracy:
information_schema.tables
values are based on background statistics and may have a slight delay.
SHOW DATA
is generally more real-time. References • faq/Sql_faq.md • sql-reference/sql-statements/Database/SHOW_DATA.md • sql-reference/information_schema/materialized_views.md • administration/Monitor_and_Manage.md • administration/management/resource_management/resource_group.md • administration/management/resource_management/Resource_Group.md • administration/management/resource_management/Workload_Group.md • administration/management/resource_management/workload_group.md • sql-reference/sql-statements/materialized_view/SHOW_MATERIALIZED_VIEW.md References • faq/Sql_faq.md • sql-reference/information_schema/materialized_views.md • sql-reference/sys/object_dependencies.md • sql-reference/sql-statements/materialized_view/SHOW_MATERIALIZED_VIEW.md • using_starrocks/async_mv/materialized_view_task_run_details.md
k
@Rocky show data is not working for default catalog like: show data from default_catalog.starrocks or show data from default_catalog.starrocks.table_name
r
In StarRocks, the
SHOW DATA
command is a legacy diagnostic tool that does not support three-part naming (catalog.db.table). It only understands one or two levels:
[db_name.]table_name
. Because Materialized Views and internal tables always reside in the
default_catalog
, you must ensure your session is pointed there first. How to fix it Option 1: Switch Catalog first (Recommended) If your current session is pointing to an external catalog (like Hive or Iceberg),
SHOW DATA
will fail. Switch back to the internal catalog first:
Copy code
sql
SET CATALOG default_catalog;
SHOW DATA FROM starrocks_db.your_mv_name;
Option 2: Use the standard SQL way If you want to use three-part naming or query across catalogs without switching, use
information_schema
. This table tracks the same data and is much more flexible:
Copy code
sql
SELECT 
    TABLE_SCHEMA, 
    TABLE_NAME, 
    format_bytes(DATA_LENGTH) as size,
    TABLE_ROWS
FROM information_schema.tables 
WHERE TABLE_SCHEMA = 'your_db_name' 
  AND TABLE_NAME = 'your_mv_name';
Why your queries failed: 1.
SHOW DATA FROM default_catalog.starrocks
: StarRocks interprets
default_catalog
as the database name and
starrocks
as the table name. If you don't have a database literally named "default_catalog", it returns an error. 2.
SHOW DATA FROM default_catalog.starrocks.table_name
: The parser for this specific command only expects two dots at most (for
db.table
). Adding the catalog name as a third part causes a syntax error. Summary of
SHOW DATA
behavior:
* Context: It always looks inside the current catalog of your session. * Database level:
SHOW DATA;
(Shows all tables in current DB). * Table level:
SHOW DATA FROM db_name.table_name;
(Shows index/replica breakdown for one table). References • sql-reference/sql-statements/Database/SHOW_DATA.md • sql-reference/sql-statements/table_bucket_part_index/SHOW_TABLES.md • faq/Sql_faq.md • data_source/catalog/unified_catalog.md • sql-reference/sql-statements/Database/SHOW_DATABASES.md • data_source/catalog/deltalake_catalog.md • data_source/catalog/paimon_catalog.md
k
@Rocky i need to support additional sorting options on numerical columns (long, float)
r
Supporting additional sorting options for numerical columns (like
BIGINT
,
FLOAT
, or
DOUBLE
) in StarRocks requires different strategies depending on the exact data type and whether you need to optimize for filtering or physical ordering. 1. For Integer Types (
BIGINT
,
INT
,
SMALLINT
)
If your columns are integers, you have two primary secondary index options. These do not physically sort the data but make the
ORDER BY
phase faster by reducing the data volume. * Inverted Index (v3.3+): Best for high-cardinality numerical columns. By using an inverted index with
'parser'='none'
, StarRocks can instantly filter rows using
>=
,
<=
,
IN
, and
==
. This "pruning" ensures the sorting engine only handles a fraction of the data. * Bitmap Index: Best for low-to-medium cardinality columns. It supports
BIGINT
and
INT
and is highly efficient for combining multiple filters (e.g.,
WHERE age > 20 AND status = 1 ORDER BY salary
). 2. For Floating Point Types (
FLOAT
,
DOUBLE
)
Important: StarRocks Bitmap Indexes do not support
FLOAT
or
DOUBLE
types. To speed up sorting on these columns, you should use one of the following: * Materialized Views (The "Best" Solution): Since a table can only have one physical
ORDER BY
sort key, you can create a Synchronous Materialized View that uses your float column as its own sort key.
Copy code
sql
    -- Base table is sorted by id
    CREATE MATERIALIZED VIEW mv_sort_by_price AS
    SELECT price, id, user_id
    FROM base_table
    ORDER BY price; -- This creates a second physical copy sorted by price
StarRocks will automatically route your query to this MV when it sees
ORDER BY price
.
* Cast to DECIMAL: If you can afford the precision change, converting
FLOAT
to
DECIMAL
allows you to use Bitmap Indexes, which do support the
DECIMAL
type. 3. Comparison of Strategies Strategy | Best For... | Supports Float/Double? | Storage Cost Sort Key | Your #1 most frequent query. | No (only as last column) | 0 (Primary) Inverted Index | Integer range filtering. | No (varies by version) | Low (~10-15%) Materialized View | High-performance additional sorts. | Yes | High (100%) Bloom Filter | Equality checks (
=
,
IN
). | Yes | Very Low Summary Recommendation * If you have 8 different sort options on a 4B row table, you cannot practically create 8 Materialized Views (it would consume 8x the disk space). * Instead, use the Sort Key for the most common query. * For the other 7 options, use Inverted Indexes on the integer columns and Bloom Filters on the float columns to filter the data as much as possible before the sort happens. * If a specific "Sort by Price" (Float) query is still too slow, create one targeted Materialized View for just that specific high-priority column. References • knowledge/usage/best-practice.md • table_design/indexes/Bitmap_index.md • using_starrocks/distinct_values/Using_bitmap.md
k
@Rocky is there some way to create inverted index on numerical column on shared data 4.1 cluster? This doc is saying numerical types is unsupported: docs.starrocks.io/docs/table_design/indexes/inverted_index The data type of the indexed column must be CHAR, VARCHAR, or STRING.
r
While the documentation for the Full-text Inverted Index (
USING GIN
) states it only supports string types, StarRocks provides a solution for numerical columns in shared-data 4.1 clusters through Bitmap Indexes. Conceptually, a Bitmap Index is a type of inverted index, and in StarRocks v4.1 (shared-data), both the "built-in" inverted index and the bitmap index use the same underlying native implementation. The Solution: Use Bitmap Index You can create a Bitmap Index on numerical columns in a shared-data cluster. This feature was added to shared-data clusters starting in version 3.3.0. Supported Numerical Types:
TINYINT
,
SMALLINT
,
INT
,
BIGINT
,
LARGEINT
,
DECIMAL
,
BOOLEAN
. How to create it:
Copy code
sql
-- For an existing table
ALTER TABLE table_name ADD INDEX index_name (numerical_column) USING BITMAP;

-- At table creation
CREATE TABLE example (
    id BIGINT,
    data_value INT,
    INDEX idx_val (data_value) USING BITMAP
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id);
Why use Bitmap instead of GIN? 1. Type Support:
USING BITMAP
is explicitly designed for scalar types, including integers and decimals.
USING GIN
is specifically for text analysis (tokenization) and currently enforces string types in the syntax parser. 2. Optimized Queries: Bitmap indexes are highly effective for numerical: * Equality:
WHERE col = 100
* Range:
WHERE col > 10 AND col < 50
* In-list:
WHERE col IN (1, 2, 3)
3. Shared-Data Compatibility: Since v3.3, StarRocks has supported Bitmap Indexes on shared-data (compute-decoupled) clusters. Important Considerations for v4.1 * Built-in Implementation: In shared-data clusters, StarRocks automatically uses the "built-in" implementation for both Bitmap and GIN indexes because the older CLucene-based engine is not supported on shared-data. * High Cardinality: Unlike some other systems, StarRocks Bitmap Indexes are optimized for high-cardinality columns as well. * Float/Double: Note that
FLOAT
and
DOUBLE
are still not supported for Bitmap Indexes. If you need to index these, consider casting them to
DECIMAL
or using a Synchronous Materialized View to change the sort order. References • table_design/indexes/Bitmap_index.md • table_design/indexes/inverted_index.md • sql-reference/sql-statements/table_bucket_part_index/CREATE_INDEX.md • release_notes/release-3.3.md • table_design/indexes/indexes.md • administration/management/resource_management/resource_group.md • administration/management/resource_management/Resource_Group.md • administration/management/resource_management/workload_group.md • administration/management/resource_management/Workload_Group.md load_Group) References • table_design/indexes/Bitmap_index.md
k
@Rocky how could i support his multi column sorting options in a shared nothing cluster while considering different table types? something that could compete with opensearch's doc values sorting capabilities
r
To support multi-column sorting in a shared-nothing StarRocks cluster while matching or exceeding the capabilities of OpenSearch's "doc values," you should leverage StarRocks' native columnar architecture combined with specific indexing and materialized view strategies. In OpenSearch, "doc values" are essentially on-disk columnar stores that allow sorting on any field. StarRocks is a columnar database by nature, meaning *every column already functions like a "doc value"*—it is stored independently, can be scanned efficiently, and has built-in metadata (min/max) for pruning. Here is how you can implement and optimize multi-column sorting across different table types: 1. The Foundation: Physical Sort Keys (
ORDER BY
)
The most effective way to support multi-column sorting is to define the physical order of the data. * Duplicate Key Tables: Best for raw data/logs. You can specify multiple columns in the
ORDER BY
clause.
Copy code
sql
    CREATE TABLE events (
        event_time DATETIME,
        tenant_id INT,
        severity INT,
        message STRING
    ) ENGINE=OLAP
    DUPLICATE KEY(event_time)
    ORDER BY (tenant_id, event_time, severity); -- Physical multi-column sort
* Primary Key Tables (v3.0+): Unlike older versions, the Primary Key and Sort Key are now decoupled. This means you can have a UUID as the Primary Key but physically sort the data by
timestamp
and
user_id
.
Copy code
sql
    CREATE TABLE users (
        user_id BIGINT,
        city_id INT,
        last_login DATETIME
    ) PRIMARY KEY (user_id)
    ORDER BY (city_id, last_login);
2. Supporting Additional Sort Orders (Synchronous MVs) If you have a table that needs to be sorted by
(A, B)
for one query and
(C, D)
for another, you cannot do this in a single physical file. In OpenSearch, this is slow because it's a "late materialization" sort. In StarRocks, you create a Synchronous Materialized View: * It acts as a secondary "index" that is automatically kept in sync with the base table. * It stores a second physical copy of the data sorted by the new columns. * Query Rewrite: The optimizer automatically routes queries to the MV if the
ORDER BY
matches the MV's sort key.
Copy code
sql
CREATE MATERIALIZED VIEW sort_by_severity AS
SELECT severity, event_time, tenant_id
FROM events
ORDER BY severity, event_time; -- This creates a 2nd physical sort order
3. "Doc Values" Equivalent for Numerical Columns: Bitmap Indexes For numerical columns (
INT
,
BIGINT
,
DECIMAL
) that are not part of your sort key, use Bitmap Indexes. * In StarRocks, Bitmap Indexes are highly efficient even for high-cardinality data. * They allow the engine to "prune" rows before the sort happens. If you filter on 3 columns and sort by a 4th, the Bitmap Index reduces the amount of data the sorting engine has to process.
Copy code
sql
ALTER TABLE events ADD INDEX idx_severity (severity) USING BITMAP;
4. Advanced:
encode_sort_key
(Composite Sorting)
If you have complex multi-column sorting requirements (e.g., sorting by a mix of types or handling specific null behaviors) that must be calculated on the fly, use the
encode_sort_key()
function. * It creates a single
VARBINARY
string from multiple columns that preserves lexicographical order. * You can store this result in a column or MV to perform extremely fast single-column sorts that represent multi-column logic. 5. Summary Strategy by Table Type Table Type | Multi-Column Sort Strategy Duplicate Key | Use
ORDER BY (col1, col2...)
. Best for high-volume logs. Primary Key | Decouple PK from Sort Key. Use
ORDER BY
for the most common query. Aggregate/Unique | The sort key is implicitly the key columns. Use Synchronous MVs to re-order them for different query patterns. Why this competes with OpenSearch:
1. I/O Elimination: StarRocks uses "Zone Maps" (min/max) on every column and every page (64KB). It skips entire chunks of data that don't meet your filters before even trying to sort. 2. Vectorized Sorting: StarRocks uses SIMD instructions to sort columnar data, which is significantly faster than the row-based or heap-sort methods often used in text-search engines. 3. Prefix Indexing: The first 36 bytes of your sort key are indexed into a sparse "short key" index, allowing millisecond point-lookups on sorted data. References • best_practices/table_clustering.md • table_design/indexes/inverted_index.md • best_practices/query_tuning/schema_tuning.md • using_starrocks/sorted_aggregate.md • knowledge/usage/best-practice.md • sql-reference/sql-functions/utility-functions/encode_sort_key.md • table_design/table_types/duplicate_key_table.md • table_design/indexes/vector_index.md • table_design/indexes/Bitmap_index.md • using_starrocks/Materialized_view-single_table.md • faq/operation_maintenance_faq.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md • release_notes/release-3.3.md References • using_starrocks/Materialized_view-single_table.md • faq/operation_maintenance_faq.md • sql-reference/sql-statements/materialized_view/CREATE_MATERIALIZED_VIEW.md
k
@Rocky assume there will be no filtering, just storing on 4 billion rwos
r
In a shared-nothing cluster with 4 billion rows and no filtering, you are moving from a "search" problem to a "massive data movement and ordering" problem. To compete with OpenSearch's "doc values" at this scale while maintaining performance, you must shift as much work as possible from runtime to storage-time. 1. Pre-Materialize Sort Orders (Zero-Cost Sorting) At 4 billion rows, a runtime sort (
SELECT * FROM table ORDER BY A, B, C
) is extremely heavy. To match the instant response of "doc values," you should use Synchronous Materialized Views. * Strategy: Create a Synchronous MV for each secondary sort option you need to support. * Result: StarRocks stores a second physical copy of the data, sorted by those specific columns. * Competitor Edge: Unlike OpenSearch, which reads doc values and sorts at query time (late materialization), StarRocks will read a pre-sorted stream of data. The sort operation becomes a simple sequential scan.
Copy code
sql
-- Original table sorted by (id)
CREATE TABLE base_data ( ... ) 
ENGINE=OLAP DUPLICATE KEY(id) 
ORDER BY (id);

-- Support "Sort by Metric A, Metric B" instantly
CREATE MATERIALIZED VIEW mv_sort_metrics AS
SELECT metric_a, metric_b, id, user_id
FROM base_data
ORDER BY metric_a, metric_b;
2. Handling Complex Multi-Column Logic (
encode_sort_key
)
If you need to sort by many columns (e.g., 5+ columns) or a mix of types, the overhead of the sorting engine increases. You can use a generated column with the
encode_sort_key()
function. * This function collapses multiple columns (Long, Float, String, etc.) into a single
VARBINARY
string that preserves the exact lexicographical order of the original components. * By making this the primary Sort Key, you reduce the 4-billion-row sort complexity from a multi-column comparison to a single-column binary comparison.
Copy code
sql
CREATE TABLE high_scale_data (
    col1 BIGINT,
    col2 FLOAT,
    composite_key VARBINARY(128) AS (encode_sort_key(col1, col2))
) ENGINE=OLAP 
ORDER BY (composite_key);
3. Stability at Scale: "Spill to Disk" (v3.0.1+) Sorting 4 billion rows without filtering will likely exceed the
query_mem_limit
of your BE nodes. In a shared-nothing cluster, StarRocks manages this via intermediate result spilling. * How it works: When the sort operator hits memory limits, it spills chunks of the sorted data to local disk and performs a merge sort. * Setup: Ensure you have high-performance local NVMe drives for the
spill_local_storage_dir
.
Copy code
sql
    SET enable_spill = true;
    SET spill_mode = 'auto';
4. Optimize the Merge Phase In a shared-nothing architecture, data is spread across many tablets. * Local vs. Global Sort: Each BE node sorts its own tablets locally. The FE then oversees a global merge. * Top-N Optimization: If you are sorting 4B rows but only need the top 1,000 (e.g.,
ORDER BY ... LIMIT 1000
), StarRocks applies a Top-N Pushdown. Each BE node only sends its top 1,000 rows to the merger, reducing network traffic from 4 billion rows to just
(BE_count * 1000)
rows. 5. Why this beats "Doc Values" for 4B Rows 1. I/O Efficiency: OpenSearch "doc values" are columnar but require a random-access look-up or a heavy heap-sort on the values. StarRocks Sort Keys are part of the file format; the data is physically contiguous. 2. Vectorized Execution: StarRocks uses SIMD to compare multiple rows simultaneously during the merge-sort process. 3. Streaming Aggregate: If your sort is followed by an aggregation, StarRocks can use Sorted Streaming Aggregation (
set enable_sort_aggregate=true;
). It processes the 4B rows in their pre-sorted order and emits results without ever building a hash table, which is significantly faster and more memory-efficient than OpenSearch. Summary Recommendation For 4 billion rows with no filtering: 1. Primary Sort: Use the
ORDER BY
clause in your table definition.
2. Additional Sorts: Use Synchronous Materialized Views to avoid runtime sorting. 3. Memory Safety: Enable Spill to Disk to ensure the query finishes even if memory is tight. 4. Complex Orders: Use
encode_sort_key
to flatten multi-column sorts into a single binary key. References • sql-reference/sql-functions/utility-functions/encode_sort_key.md • best_practices/table_clustering.md • administration/management/resource_management/spill_to_disk.md • using_starrocks/sorted_aggregate.md • faq/Sql_faq.md References • knowledge/usage/best-practice.md • administration/management/resource_management/spill_to_disk.md • using_starrocks/sorted_aggregate.md • administration/management/resource_management/Memory_management.md • faq/Sql_faq.md
k
@Rocky for filtering on string token, should we use inverted index or bloom filter
r
The choice depends entirely on whether your "token" is the entire value of the column or a word inside a larger string. 1. Inverted Index (
USING GIN
)
Use this if: You are doing Full-Text Search or looking for a keyword within a sentence/log. * Query Pattern:
WHERE col MATCH 'token'
or
WHERE col MATCH_ANY 'token1 token2'
. * How it works: It breaks the string into individual words (tokenization). It stores a mapping of
word -> row_id
. * Pros: Extremely fast for finding rows containing a specific word. Supports complex logic like "contains word A AND word B". * Cons: Higher storage overhead than a Bloom filter. 2. Standard Bloom Filter Use this if: Your "token" is a unique identifier (like a UUID or Session Token) and you are filtering for the exact, whole value. * Query Pattern:
WHERE col = 'exact_token_value'
or
WHERE col IN ('token1', 'token2')
. * How it works: It hashes the entire column value into a bitset. It can quickly tell if a data block definitely does not contain that exact string. * Pros: Very low storage overhead. Excellent for high-cardinality ID columns that are not part of the sort key. * Cons: Cannot look "inside" a string. If the column value is
"error_token_404"
and you search for
WHERE col = 'token'
, the Bloom filter will say it doesn't exist. 3. N-Gram Bloom Filter (Alternative) Use this if: You are doing fuzzy matching or substring searches without full-text tokenization. * Query Pattern:
WHERE col LIKE '%token%'
. * How it works: It breaks the string into small chunks of $N$ characters (e.g., 3-character "grams") and puts them in a Bloom filter. * Pros: Accelerates
LIKE
queries which usually require a full table scan. ────────── Summary Recommendation If your query is... | Use this Index | Syntax Example "Find rows where the text contains the word..." | Inverted Index |
INDEX idx (col) USING GIN
"Find rows where the ID is exactly..." | Bloom Filter |
PROPERTIES("bloom_filter_columns"="col")
"Find rows where the string matches the pattern `%abc%`" | N-Gram Bloom Filter |
INDEX idx (col) USING NGRAMBF
Note for StarRocks 4.1 (Shared-Data): The Inverted Index is the most powerful option here. In shared-data clusters, the Inverted Index uses a native "built-in" format that is highly optimized for the compute-decoupled architecture. If you are unsure, the Inverted Index provides the most flexibility for string-performance-wise for string-based filtering. References • table_design/indexes/Ngram_Bloom_Filter_Index.md • table_design/indexes/Bloomfilter_index.md • knowledge/usage/best-practice.md
k
@Rocky an entire word like a category (e.g. "Movies"), low cardinality
r
For an entire word like a category (e.g., "Movies") with low cardinality, you should generally avoid a Bloom Filter and instead rely on StarRocks' native storage optimizations or use a Bitmap Index. Here is the breakdown for your specific scenario: 1. Why NOT to use a Bloom Filter Bloom Filters are designed for high-cardinality columns (like
user_id
or
order_id
). * The Problem: For a low-cardinality category like "Movies," every data block (tablet) will likely contain at least one row with that value. * Result: The Bloom Filter will return "True" for almost every block, meaning StarRocks will still have to read every block from disk. It provides zero "pruning" power and just adds storage overhead. 2. The Best Choice: Native Dictionary Encoding (Default) In StarRocks, low-cardinality string columns are automatically dictionary-encoded. * Instead of storing the string "Movies" 4 billion times, it stores an integer (e.g.,
5
) and a small global dictionary. * Performance: When you filter
WHERE category = 'Movies'
, StarRocks converts the filter to
WHERE category_id = 5
and scans the integer column, which is extremely fast and hardware-efficient. * Recommendation: For 4 billion rows, if this column is used frequently in filters, try making it part of your Sort Key (
ORDER BY
) first. This allows "Zone Map" pruning, which is faster than any secondary index. 3. When to use a Bitmap Index If the category column is not part of your Sort Key and you often filter by multiple categories at once (e.g.,
WHERE category = 'Movies' AND status = 'Released'
), use a Bitmap Index. * Why: Bitmap indexes are the traditional choice for low-cardinality data. They store a bitset for each unique value. * Efficiency: StarRocks can perform bitwise AND/OR operations on these indexes before even touching the actual data, which is highly efficient for multi-column filtering on 4 billion rows. 4. When to use an Inverted Index (GIN) In StarRocks 3.3+, the new Inverted Index can also handle exact matches, but it is typically optimized for text search (finding a word inside a sentence). * If your "category" column is strictly single words like "Movies," "Action," or "Drama," an Inverted Index is technically functional but is usually "heavier" than a Bitmap Index. * Use this ONLY if: You might occasionally want to do "contains" searches or if you are using a shared-data (Cloud Native) architecture where the modern Inverted Index is more tightly integrated into the storage layer. Final Summary Feature | Recommendation Cardinality | Low (few unique values) Best Strategy | No Index (Dictionary Encoding) + Sort Key (if possible) Secondary Index | Bitmap Index (Great for combining multiple low-card filters) Avoid | Bloom Filter (Will not prune data effectively at low cardinality) For 4 Billion Rows: The absolute best performance will come from including the "Category" column as the first or second column in your
ORDER BY
clause
. This will allow StarRocks to skip massive amounts of data at the storage level without needing to load index files. References • table_design/indexes/Bitmap_index.md • table_design/indexes/Bloomfilter_index.md • best_practices/query_tuning/schema_tuning.md • knowledge/usage/best-practice.md References • table_design/indexes/Bitmap_index.md
k
@Rocky the query for this explain is taking 2 minutes, far too long:
Copy code
0:OlapScanNode
     TABLE: mv_2
     PREAGGREGATION: ON
"     PREDICATES: 149: category = 'Technology & Computing'"
     partitions=1/1
     rollup: mv_2
     tabletRatio=2048/2048
     tabletList=3402037,3402038,3402039,3402040,3402041,3402042,3402043,3402044,3402045,3402046 ...
     cardinality=1684737
     avgRowSize=211.50655
     MaterializedView: true
r
The 2-minute response time and the
tabletRatio=2048/2048
in your EXPLAIN plan are the "smoking guns."
tabletRatio=2048/2048
means your query is scanning 100% of the tablets in that Materialized View. Even though you have a predicate (
category = '...'
), StarRocks has to open and read every single tablet to find the relevant rows because it has no metadata to tell it where those rows are. For 4 billion rows, you want that ratio to be as small as possible (e.g.,
1/2048
). Here is how to fix this: 1. The Best Fix: Add
category
to the Sort Key
In your Materialized View definition, the
ORDER BY
(Sort Key) determines which columns StarRocks uses for Zone Map pruning (skipping data based on Min/Max values). * Problem: If
category
is not in the Sort Key (or is very late in the list), StarRocks must perform a full scan. * Solution: Recreate the Materialized View with
category
as the first column in the
ORDER BY
clause.
Copy code
sql
CREATE MATERIALIZED VIEW mv_2 
REFRESH ASYNC
DISTRIBUTED BY HASH(...) -- keep your existing distribution
ORDER BY (category, ...) -- PUT CATEGORY FIRST
AS 
SELECT category, ... 
FROM base_table;
* Result: The
tabletRatio
will still show
2048/2048
(because it's not partitioned/distributed by category), but the ScanTime will drop from minutes to seconds because StarRocks will use "Zone Maps" to skip ~99% of the data blocks within those tablets. 2. The "Instant" Fix: Add a Bitmap Index Since "Category" is low cardinality (as you mentioned earlier), a Bitmap Index is perfect. It allows StarRocks to skip reading data blocks entirely by checking a small index bitset first. * Why it helps: It fixes the "2 minute" scan by providing a shortcut to the specific rows you need without requiring a full rewrite of the Sort Key.
Copy code
sql
-- Run this on the Materialized View (or the base table if it's a sync MV)
ALTER TABLE mv_2 ADD INDEX idx_category (category) USING BITMAP;
3. Change Distribution (If category is your primary filter) If 90% of your queries filter by
category
, you should consider distributing the data by that column. * Current:
DISTRIBUTED BY HASH(some_other_id) BUCKETS 2048
(Likely why ratio is 2048/2048). * Change to:
DISTRIBUTED BY HASH(category) BUCKETS 2048
. * Result: Your
tabletRatio
will change from
2048/2048
to
1/2048
. The query will only talk to one tablet instead of 2,048. _ _Warning:* Only do this if
category
has enough unique values to spread across your BE nodes. If you only have 5 categories, you'll get massive data skew. Summary Checklist 1. Check `ORDER BY`: Is
category
the first column? If not, change it. 2. Add Bitmap Index: This is the easiest "bolt-on" performance boost for low-cardinality strings. 3. Check Tablet Count: 2,048 tablets is quite high unless you have a very large cluster (e.g., 50+ nodes). If your cluster is small, 2,048 tablets create high metadata overhead; consider reducing this to
NodeCount * 10-20
. To see exactly where the time is going, run:
SET enable_profile = true;
Execute the query, then run
SHOW PROFILE FOR <query_id>;
and look for
PushdownPredicates
and
RowsRead
vs
RawRowsRead
. If
RawRowsRead
is 4 billion, you are doing a full table scan. References • using_starrocks/async_mv/materialized_view_task_run_details.md • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md • knowledge/usage/best-practice.md • using_starrocks/async_mv/use_cases/query_rewrite_with_materialized_views.md
k
@Rocky query to view indexes on materialized views
r
To view the indexes (Bitmap, Bloom Filter, or Inverted Index) on a Materialized View in StarRocks, you can use the same commands you would use for a standard table. Since an Asynchronous Materialized View (like your
mv_2
) is stored physically as a table, use one of the following: 1. The Quickest Way:
SHOW INDEX
This will list all manually created indexes (Bitmap, GIN, N-Gram, etc.).
Copy code
sql
SHOW INDEX FROM mv_2;
2. View the Original Definition:
SHOW CREATE TABLE
This is the most reliable way to see both the Sort Keys (which are "built-in" indexes) and any secondary indexes.
Copy code
sql
SHOW CREATE TABLE mv_2;
* Look for
ORDER BY
: These columns are your "Prefix Index" and are used for Zone Map pruning. * Look for
INDEX
: Any lines starting with
INDEX
or
PROPERTIES("bloom_filter_columns"...)
are your secondary indexes. 3. For Synchronous MVs:
DESC ALL
If you are using a Synchronous (Rollup) MV, you must check it through the base table:
Copy code
sql
DESC base_table_name ALL;
────────── What to look for in your specific case: In your previous
EXPLAIN
plan, the
tabletRatio=2048/2048
confirms that you do not have an index that allows for "Partition Pruning" or "Distribution Pruning." If
SHOW INDEX
returns empty for your
category
column: 1. Check if it's in the
ORDER BY
clause
(via
SHOW CREATE TABLE
). 2. If it's not, you are suffering from a full scan. 3. To fix it immediately, add a Bitmap index:
Copy code
sql
    ALTER TABLE mv_2 ADD INDEX idx_category (category) USING BITMAP;
Note: If you just created the index, you may need to wait for a compaction or a refresh for it to fully apply to all 4 billion rows. References • table_design/indexes/Bitmap_index.md • sql-reference/sql-statements/table_bucket_part_index/SHOW_INDEX.md • using_starrocks/async_mv/Materialized_view.md • table_design/indexes/Bloomfilter_index.md • sql-reference/sql-statements/table_bucket_part_index/SHOW_CREATE_TABLE.md References • using_starrocks/Materialized_view-single_table.md • using_starrocks/async_mv/Materialized_view.md • using_starrocks/async_mv/troubleshooting_asynchronous_materialized_views.md • sql-reference/information_schema/statistics.md • table_design/indexes/indexes.md
k
@Rocky for my shared data cluster, how could i load all iceberg tables onto disk
r
To load your Iceberg tables onto the local disks of your shared-data cluster (effectively "warming up" the cache), you should use the Data Cache Warmup feature introduced in StarRocks v3.3.0. In a shared-data architecture, StarRocks uses a Data Cache (Block Cache) to store remote data on local NVMe/SSD disks to provide shared-nothing-like performance. 1. Proactive Loading:
CACHE SELECT
The most direct way to force Iceberg data from remote storage (S3/HDFS) onto your local disks is the
CACHE SELECT
command. This statement explicitly fetches data and populates the local block cache. * To load an entire table:
Copy code
sql
    CACHE SELECT * FROM iceberg_catalog.your_db.your_table;
* To load only specific columns or recent data (to save disk space):
Copy code
sql
    CACHE SELECT column1, column2
    FROM iceberg_catalog.your_db.your_table
    WHERE event_date >= '2024-01-01';
2. Automating the Load (Scheduled Warmup) If you want to ensure your Iceberg tables are always "hot" on disk (e.g., before morning BI reports), you can create a scheduled task:
Copy code
sql
SUBMIT TASK warmup_iceberg_task 
SCHEDULE EVERY (INTERVAL 1 DAY)
AS 
CACHE SELECT * FROM iceberg_catalog.your_db.your_table;
3. Ensure Data Cache is Enabled Before running the above, verify that your Compute Nodes (CN) or Backend nodes (BE) have the Data Cache enabled in their configuration (
be.conf
): *
starlet_use_star_cache = true
(Enabled by default in v3.2.3+) * `starlet_star_cache_disk_size_percent`: Check this value to ensure you have enough disk space to hold the tables you are loading. 4. Metadata Caching (Important for Iceberg) For Iceberg specifically, the "planning" phase can be slow if metadata is not local. You can also enable Metadata Disk Caching in your FE configuration: * Set
enable_iceberg_metadata_disk_cache = true
in
fe.conf
. * This ensures that Iceberg manifest files and snapshots are also stored on the FE's local disk, speeding up the time it takes to even start the query. Summary of Performance Metrics When you run
CACHE SELECT
, StarRocks will return a summary: * WRITE_CACHE_SIZE: How much data was actually written to your local disks. * TOTAL_CACHE_USAGE: The percentage of your total allocated cache disk space currently in use. Note: Since the cache uses an LRU (Least Recently Used) policy, if your Iceberg tables are larger than your total local disk cache capacity, older data will be evicted to make room for the data you are currently loading. References • data_source/block_cache_warmup.md • deployment/shared_data/feature-support-shared-data.md • data_source/catalog/iceberg/iceberg_catalog.md • data_source/data_cache.md ) • source/data_cache) References • data_source/block_cache_warmup.md • data_source/catalog/iceberg/iceberg_catalog.md
k
@Rocky will caching an iceberg table be as performant as a cached MV
r
No, a Materialized View (MV) will almost always be more performant than a Cached Iceberg Table, even if the Iceberg data is 100% "warm" on local disk. While both features use local NVMe/SSD disks to speed up queries, they optimize different parts of the query lifecycle. 1. I/O vs. Computation (The Biggest Difference) * Cached Iceberg (
CACHE SELECT
):
This only optimizes I/O. It brings the raw Parquet/ORC files from S3 to local disk. When you run a query, StarRocks still has to perform all the computation: it must decompress the Parquet blocks, filter the rows, perform joins, and calculate aggregations every single time. * Materialized View: This optimizes I/O and Computation. An MV stores the result of the query. If your MV aggregates 4 billion rows into 1 million rows, StarRocks only reads the 1 million pre-calculated rows. It skips the heavy lifting of processing those 4 billion rows entirely. 2. Storage Format & Indexing * Iceberg (Open Format): Even when cached, the data is in Parquet/ORC. StarRocks must use the Iceberg reader to parse it. * MV (StarRocks Native Format): MVs are stored in StarRocks' internal
Segment V2
format. This format is deeply integrated with the execution engine and supports: * Primary/Sort Keys: Much faster data skipping (Zone Maps) than Parquet. * Native Indexes: You can add Bitmap or Inverted indexes to an MV; you cannot add StarRocks-native indexes directly onto a remote Iceberg table. 3. Metadata Overhead * Iceberg: Querying Iceberg requires parsing manifest files and snapshots to find which files to read. Even with metadata caching, this adds "planning" time. * MV: Since the MV is a native StarRocks table, the FE (Frontend) knows exactly where the data is instantly. Metadata operations are significantly faster. Comparison Summary Feature | Cached Iceberg | Materialized View Primary Benefit | Eliminates S3/HDFS latency | Eliminates computation + I/O Data Format | Parquet/ORC (Remote Format) | Segment V2 (Native Format) Computation | Full processing on every query | Pre-computed / Minimal processing Optimization | Local Disk Cache (LRU) | Sort Keys, Bitmap Indexes, Pre-aggregation Query Rewrite | No (You must query the table) | Yes (Optimizer automatically uses MV) When to use which? * Use Data Caching (Iceberg) when you have "ad-hoc" queries where users might filter by any column, and you want to ensure the raw data access isn't throttled by remote storage speeds. * Use Materialized Views for your *"Golden Queries"*—the heavy, repetitive dashboards or reports where 2-minute wait times are unacceptable. Pro-tip: You can actually create an Asynchronous MV on top of an Iceberg table. This gives you the best of both worlds: your "source of truth" stays in the open Iceberg format, but your high-performance queries hit the pre-computed StarRocks-native MV. References • data_source/data_cache.md • using_starrocks/async_mv/Materialized_view.md • data_source/catalog/iceberg/iceberg_catalog.md • table_design/indexing.md
k
@Rocky for shared data cluster with less joins occurring with high qps, would more smaller instances (r8g.4xlarge) be more performant than less larger instances (r8g.8xlarge)
r
For a shared-data cluster facing high QPS (Queries Per Second) with few joins, using more smaller instances (r8g.4xlarge) is generally more performant and cost-effective than using fewer larger instances (r8g.8xlarge). Here is the breakdown of why horizontal scaling (more small nodes) is the preferred approach for this specific workload in StarRocks: 1. Higher Aggregate Network Bandwidth On AWS, network throughput is capped at the instance level. * An
r8g.8xlarge
typically provides up to 25 Gbps. * Two
r8g.4xlarge
instances often provide 15 Gbps each, giving you an aggregate of 30 Gbps. In a shared-data architecture, where nodes are constantly pulling data from S3 to populate the Data Cache, having more aggregate bandwidth prevents network saturation during high-concurrency peaks. 2. Distributed Data Cache I/O In StarRocks shared-data mode, each Compute Node (CN) maintains its own local Data Cache (on NVMe/SSD). * Parallelism: High QPS means many concurrent small reads. More instances mean more separate disk controllers and I/O queues working in parallel. * Reduced Contention: Fewer threads on a smaller node will compete for the same local disk I/O, reducing "tail latency" (P99) for your queries. 3. More Efficient Concurrency Handling For simple queries (point lookups or small aggregations), the bottleneck is often the overhead of thread scheduling and connection management. * StarRocks is an MPP (Massively Parallel Processing) engine. It is designed to slice queries into fragments and distribute them. * Distributing a high volume of small queries across a larger number of nodes reduces the CPU context-switching and lock contention that can occur when a single large node tries to handle too many concurrent tasks. 4. Better Elasticity and Availability * Stateless Scaling: In a shared-data cluster, CN nodes are stateless. Adding or removing
r8g.4xlarge
nodes is instant and requires no data rebalancing. It is much easier to "fine-tune" your cluster capacity with smaller increments. * Blast Radius: If an
r8g.8xlarge
node fails, you lose 50% of your capacity (assuming a 2-node setup). If one of four
r8g.4xlarge
nodes fails, you only lose 25%, making the cluster more resilient to high-traffic spikes. When to reconsider (Larger Instances): You should only move toward larger instances (
r8g.8xlarge
or
16xlarge
) if: 1. Memory-Intensive Joins: You start running very large "Shuffle Joins" that require massive hash tables to fit in a single node's memory. 2. FE Overhead: You have so many small nodes (hundreds) that the Frontend (FE) struggles to manage the RPC heartbeats and query dispatching (this usually only happens at a very massive scale). Recommendation For high QPS with simple queries on
r8g
instances: * Start with
r8g.4xlarge
. * Ensure your Data Cache is configured to use the local NVMe storage of these instances. * Monitor your *FE (Frontend) CPU*; if it spikes, it means the overhead of dispatching to many small nodes is becoming a factor (though unlikely at this instance size). References • deployment/shared_data/feature-support-shared-data.md • introduction/Architecture.md • administration/management/resource_management/Compute_node.md • faq/shared_data_faq.md References • introduction/Architecture.md • deployment/shared_data/feature-support-shared-data.md
k
@Rocky what is a resource gruop config to reserve 20% of cpu
r
To reserve CPU resources for a specific workload in StarRocks, you have two primary options depending on whether you want a Soft Limit (proportional sharing) or a Hard Limit (exclusive reservation). 1. Hard Reservation (Exclusive Isolation) If you want to guarantee that 20% of the CPU cores are physically reserved for a group and cannot be used by any other queries (even if this group is idle), use the
exclusive_cpu_cores
property. Since this property requires a specific number of cores rather than a percentage, you must calculate 20% of your minimum BE core count. For example, if your BE nodes have 16 cores, 20% would be 3 cores (rounding to the nearest integer):
Copy code
sql
CREATE RESOURCE GROUP reserved_20_group
PROPERTIES (
    "exclusive_cpu_cores" = "3", -- 20% of a 16-core node
    "mem_limit" = "20%"          -- Memory is usually set as a %
);
* Best for: Critical production workloads that need absolute performance stability. 2. Soft Reservation (Weight-based) If you want the group to get 20% of the CPU time when the system is busy, but allow other groups to use that CPU if the group is idle, use
cpu_weight
(formerly
cpu_core_limit
before v3.3.5). In this mode, the share is calculated as:
group_weight / total_weights_of_all_active_groups
. If you want this group to have 20% and the remaining groups to share 80%, you could set the weights like this:
Copy code
sql
CREATE RESOURCE GROUP flexible_20_group
PROPERTIES (
    "cpu_weight" = "20", 
    "mem_limit" = "20%"
);

-- Ensure other groups have weights that sum to 80
-- e.g., Altering the default group
ALTER RESOURCE GROUP default_rg 
PROPERTIES (
    "cpu_weight" = "80"
);
* Best for: Maximizing cluster utilization while ensuring fair access during peak hours. Key Configuration Differences Parameter | Type | Behavior
exclusive_cpu_cores
| Hard Limit | Physically pins cores. Unavailable to others even if idle.
cpu_weight
| Soft Limit | Proportional share. Idle resources can be borrowed by other groups. Important Note on Memory: Unlike CPU, the
mem_limit
is almost always defined as a percentage (e.g.,
"0.2"
or
"20%"
) of the total queryable memory on the BE. References • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • faq/resource_isolation_faq.md References • faq/resource_isolation_faq.md • administration/management/resource_management/resource_group.md • administration/management/monitoring/metrics.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
k
@Rocky getting this error: Query 1 ERROR at Line 16: : Getting analyzing error. Detail message: cpu_weight should range from 0 to 14. ALTER RESOURCE GROUP rg with ( 'cpu_weight' = '20', 'mem_limit' = '20%' );
r
The error occurs because the
cpu_weight
parameter is restricted to a range between 0 and the average number of CPU cores available on your BE (Backend) nodes. Based on the error message
cpu_weight should range from 0 to 14
, your cluster nodes have 14 CPU cores. Since 20 is greater than 14, the command failed. How to fix it In StarRocks,
cpu_weight
defines a relative share of the total CPU. To reserve 20% of the CPU resources, you should choose a value that represents 20% relative to the weights of your other resource groups. Option 1: Using Weighted Shares (Recommended) If you want this group to have 20% of the CPU and all other queries (the
default_rg
) to have 80%, you can use simple numbers that fit the 0–14 range: * Set your group to 2. * Set the
default_rg
to 8. * Total weight = 10. Your group gets $2/10 = 20\%$.
Copy code
sql
-- 1. Update your group to a valid weight (e.g., 2)
ALTER RESOURCE GROUP rg PROPERTIES (
    "cpu_weight" = "2",
    "mem_limit" = "20%"
);

-- 2. Update the default group to 8 to ensure the 20/80 ratio
ALTER RESOURCE GROUP default_rg PROPERTIES (
    "cpu_weight" = "8"
);
Option 2: Using Core-Equivalent Weights You can also set the weights to sum up to your total core count (14) to make the math easier to visualize: * 20% of 14 cores = 2.8 * 80% of 14 cores = 11.2
Copy code
sql
ALTER RESOURCE GROUP rg PROPERTIES (
    "cpu_weight" = "2.8",
    "mem_limit" = "20%"
);
Key Differences to Remember 1. Soft Limit (
cpu_weight
):
This is a share. If the cluster is idle, this group can use more than 20%. If the cluster is under heavy load, it is guaranteed at least 20%. 2. Hard Limit (
exclusive_cpu_cores
):
If you want a strict reservation where those cores are only for this group and cannot be shared even if idle, use
exclusive_cpu_cores
. The value would be an integer (e.g.,
3
for ~20% of 14 cores). Note that the maximum value for
exclusive_cpu_cores
is
min_be_cpu_cores - 1
. Correct Syntax Note Ensure you use the
PROPERTIES
keyword and double quotes for the keys:
Copy code
sql
ALTER RESOURCE GROUP rg PROPERTIES (
    "cpu_weight" = "2",
    "mem_limit" = "20%"
);
References • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md • faq/resource_isolation_faq.md References • faq/resource_isolation_faq.md • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • administration/management/monitoring/metrics.md
k
@Rocky how to update an existing resource group to target multiple users
r
To update an existing resource group to target multiple users, you add new classifiers to the group. Each classifier represents a rule (like a specific user) that maps queries to that group. You can add multiple users in a single command by separating the classifier blocks with commas. 1. Adding Multiple Users Use the
ALTER RESOURCE GROUP ... ADD
syntax. Note that each user needs their own set of parentheses:
Copy code
sql
ALTER RESOURCE GROUP your_group_name 
ADD 
    (user='alice'),
    (user='bob'),
    (user='charlie');
2. Using Roles (More Scalable) If you have many users, it is often easier to assign them all to a specific Role and target that role instead of individual usernames. This way, any new user added to the role automatically inherits the resource limits.
Copy code
sql
ALTER RESOURCE GROUP your_group_name 
ADD (role='analytics_team');
3. Verification To see which users and classifiers are currently associated with your resource group, use:
Copy code
sql
SHOW RESOURCE GROUP your_group_name;
4. Removing Users If you need to remove a specific user later, you must find the Classifier ID from the
SHOW
command above and then drop it:
Copy code
sql
-- Replace 12345 with the actual Classifier ID found in SHOW command
ALTER RESOURCE GROUP your_group_name DROP (12345);
Key Rules * Unique Classifiers: Each user-group mapping is a "classifier." * Precedence: If a user matches multiple resource groups, StarRocks uses a weight-based system to pick the best match (more specific rules generally win). * Session Variable: A user can always override their assigned group for the current session using
SET resource_group = 'group_name';
if they have the necessary permissions. References • sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md • sql-reference/sql-statements/cluster-management/resource_group/SHOW_RESOURCE_GROUP.md References • sql-reference/sql-statements/cluster-management/resource_group/ALTER_RESOURCE_GROUP.md • administration/management/resource_management/resource_group.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
k
@Rocky how much worse would an ec2 r6g.4xlarge vs r8g.4xlarge perform against a shared data cluster with heavy ebs disk cache
r
The r6g.4xlarge (Graviton2) will perform significantly worse than the r8g.4xlarge (Graviton4). In a StarRocks Shared Data environment with heavy disk caching, you can expect the r6g to be roughly 45% to 55% slower (or conversely, the r8g to be roughly 80% to 100% faster) for the following reasons: 1. Architectural Generational Gap The r6g uses Graviton2 (Neoverse N1), while the r8g uses Graviton4 (Neoverse V2). * Performance Scaling: AWS benchmarks for database workloads show that Graviton3 was ~30% faster than Graviton2, and Graviton4 is ~40% faster than Graviton3. Compounded, the r8g is roughly 1.8x faster in raw compute power than the r6g. * Instruction Set: StarRocks (especially versions 3.3+) includes specific optimizations for ARM NEON and SIMD. While both generations support these, Graviton4 has significantly more efficient execution pipelines and larger caches (L2/L3) that StarRocks’ vectorized engine can exploit. 2. Memory Bandwidth (The "Cache" Bottleneck) In a Shared Data architecture, StarRocks relies heavily on the local disk cache (EBS-backed). * DDR4 vs. DDR5: The r6g uses DDR4 memory, while the r8g uses DDR5-5600. * Impact: When StarRocks pulls data from the local EBS cache into memory for processing, memory bandwidth is often the primary bottleneck. The r8g provides over 50% more memory bandwidth, meaning cache-hit queries will finish much faster on the newer instance. 3. EBS & I/O Throughput StarRocks Shared Data clusters are I/O intensive because they are constantly warming the cache from S3 or reading from EBS. * Throughput: r8g instances have significantly higher baseline and peak EBS throughput and IOPS limits compared to r6g. * Latency: The Nitro system in r8g instances has lower overhead, reducing the "tail latency" (P99) of disk reads, which prevents slow-node issues in an MPP (Massively Parallel Processing) environment. 4. Price-Performance Reality While the r8g.4xlarge is roughly 15–20% more expensive per hour than the r6g.4xlarge, it delivers nearly double the performance. * Conclusion: Choosing r6g actually results in a worse total cost of ownership (TCO). You would likely need twice as many r6g nodes to match the throughput of an r8g cluster. Summary Comparison (Estimated) Metric | r6g.4xlarge (Graviton2) | r8g.4xlarge (Graviton4) | Difference Compute Speed | Baseline | ~1.8x | +80% Memory Tech | DDR4 | DDR5-5600 | Major Bandwidth Gain StarRocks Performance | Baseline | ~1.9x | ~90% faster Relative Price | 100% | ~118% | +18% Cost Recommendation: Avoid r6g for StarRocks 3.x. The r8g (or at least r7g) is required to take full advantage of StarRocks' ARM-specific vectorization and SIMD optimizations. References • administration/management/resource_management/resource_group.md • faq/resource_isolation_faq.md • administration/management/monitoring/metrics.md • sql-reference/sql-statements/cluster-management/resource_group/CREATE_RESOURCE_GROUP.md
k
@Rocky if more compute nodes are added, does an async meterialized view get redistributed?
r
In a StarRocks Shared Data cluster (where data is in S3/Object Storage and nodes use EBS for caching), adding more compute nodes does trigger a redistribution of the asynchronous materialized view's workload. Here is how it works and what you should expect: 1. Tablet Redistribution An asynchronous materialized view (MV) is physically stored as a table. Like any other table in StarRocks, it is divided into Tablets. * In a Shared Data cluster, the FE (Frontend) maintains a mapping of which Compute Node (CN) is responsible for which tablet. * When you add new nodes, the FE automatically rebalances this mapping to ensure the workload is spread evenly across the expanded cluster. * Result: Some tablets of your MV will be reassigned from existing nodes to the new nodes. 2. The "Cold Cache" Effect Since your cluster uses a heavy EBS disk cache, the redistribution has a specific performance impact: * The data (the S3 files) does not move, but the local EBS cache does not follow the tablet. * When a new node takes over a tablet of the MV, its local EBS cache is initially "cold" for that data. * The first few queries against that MV after scaling will experience higher latency because the new nodes must pull data from S3 to warm their local EBS disks. 3. How to Mitigate Performance Jitter To prevent the performance drop during redistribution, you can use two StarRocks features: * Cache Sharing (Recommended): If enabled, when a new node needs data for a reassigned tablet, it will first check if the "old" node still has it in its cache and pull it over the internal network (which is usually much faster than S3). * Set
enable_datacache_sharing = true
in your session/global variables. * Set the FE config
enable_trace_historical_node = true
. * Data Cache Warmup: After scaling, you can proactively warm the cache for the MV so users don't hit the "cold" disks.
Copy code
sql
    -- Manually trigger a cache warmup for the MV
    CACHE SELECT * FROM your_mv_name;
4. Automatic Scaling Note If you are on StarRocks v3.3.4 or later, the parameter
lake_enable_balance_tablets_between_workers
is set to
true
by default, which ensures this balancing happens automatically. If you are on an older version, the balancing might only occur when the next refresh task or query is triggered. Summary * Does it redistribute? Yes, the responsibility for the MV's tablets is spread to the new nodes. * Is data copied? No, the master data stays in S3. Only the local EBS cache needs to be "re-warmed" on the new nodes. References • administration/management/resource_management/Replica.md • data_source/data_cache.md • faq/shared_data_faq.md • using_starrocks/async_mv/Materialized_view.md References • data_source/data_cache.md • using_starrocks/caching/block_cache.md • deployment/shared_data/feature-support-shared-data.md • data_source/data_cache_troubleshooting.md
k
@Rocky what happens if im using spot instances and compute nodes serving an mv are down? how will those tablets be served?
r
In a StarRocks Shared Data (Compute-Storage Separation) cluster, using spot instances is a common and supported practice. Because data is stored durably in S3 (or other object storage), the loss of a Compute Node (CN) does not result in data loss. Here is exactly what happens when a CN serving an MV (or any table) goes down suddenly: 1. Detection and Tablet Reassignment The Frontend (FE) monitors the health of all Compute Nodes via heartbeats. * Detection: If a spot instance is reclaimed suddenly, the FE will detect the missing heartbeat (typically within a few seconds). * Reassignment: Because it is a Shared Data architecture, tablets do not have fixed physical replicas on specific nodes. The FE simply updates its internal routing metadata to assign the "ownership" of those tablets to the remaining healthy CNs. * Availability: The tablets remain "available" almost immediately because any other CN in the cluster can pull the required data from S3 to serve them. 2. Impact on In-Flight Queries If a node goes down while a query is currently running: * Query Failure: In most versions of StarRocks, if a CN executing a query fragment disappears (SIGKILL/Sudden termination), the query will fail with a connection or RPC error. * Retry: StarRocks does not currently support transparent "mid-query" failover for a single fragment. The client (or the application layer) must catch the error and retry the query. Once retried, the FE will route the request to the new healthy nodes. 3. The "Cold Cache" Penalty While the tablets are served immediately by other nodes, you will notice a temporary performance dip: * The newly assigned nodes do not have the data for those tablets in their local EBS disk cache. * The first few queries against that MV will be slower because the new nodes must fetch the data from S3 to re-populate their local caches. 4. Handling Spot 2-Minute Warnings (Graceful Exit) If your spot instance orchestration (like AWS Node Termination Handler) can catch the 2-minute termination warning, you should trigger a Graceful Exit: * Command: Run
stop_cn.sh -g
on the node receiving the warning. * Behavior: The node will enter an "Exiting" state. It will finish its current query fragments but will reject new ones. * Benefit: This allows the FE to proactively stop routing new queries to that node before it actually disappears, significantly reducing the number of failed queries compared to a sudden shutdown. Summary of Behavior Scenario | Impact on Availability | Impact on Performance | Client Action Sudden Loss (Spot) | Tablets reassigned in < 10s | Slow (Cold Cache) | Must retry failed query Graceful Exit (-g) | Seamless | Slow (Cold Cache) | No action needed Recommendation For a cluster using spot instances, it is highly recommended to: 1. Use a Load Balancer: Ensure your application connects via a load balancer or the FE list so it can easily reconnect. 2. Enable Graceful Shutdown: Use scripts to catch the spot termination signal and run
stop_cn.sh -g
. 3. Tablet Count: Ensure you have enough tablets (buckets) in your MV so that when one node fails, the tablets can be distributed evenly among the remaining nodes rather than overloading just one. References • introduction/Architecture.md • administration/management/graceful_exit.md • administration/management/BE_blacklist.md • faq/shared_data_faq.md References • administration/management/resource_management/Replica.md • administration/management/BE_blacklist.md
k
@Rocky for important tables, how can i have every compute node keep a copy of cached mv on its own local ebs disk
r
In a StarRocks Shared-Data (Compute-Storage Separation) cluster, it is not natively possible to force every Compute Node (CN) to keep a full copy of a table's cache on its local EBS disk. The architecture is designed specifically to avoid this redundancy to save costs and simplify scaling. Here is how the system handles this and the best workarounds for "important" tables: 1. The 1-to-1 Tablet Mapping In StarRocks Shared-Data mode, each table is split into Tablets. The Frontend (FE) assigns each tablet to exactly one CN at any given time. * Result: Only the CN responsible for a specific tablet will read its data from S3 and store it in its local EBS cache. * Documentation: StarRocks explicitly states that it "does not support multiple replicas for cached data" in shared-data clusters. 2. Best Workaround: Broadcast Joins If your "important table" is a dimension table used in joins, StarRocks achieves the performance benefit of local data through Broadcast Joins. * How it works: When you join a large fact table with a smaller important table (or MV), StarRocks broadcasts the smaller table into the memory of every CN involved in the query. * Performance: This provides the same speed as having a local copy on every node, as the join happens locally in memory without further network shuffle. 3. Mitigating Scaling Jitter (Cache Sharing) The main reason users want every node to have a copy is to prevent "cold cache" misses when nodes are added or removed (spot instances). StarRocks solves this with Data Cache Sharing: * If a node needs data that was previously on another node (due to scaling), it can fetch the blocks from the peer node’s EBS cache over the internal network instead of going to S3. * Enable it by setting:
Copy code
sql
    SET GLOBAL enable_datacache_sharing = true;
    -- Also requires FE config: enable_trace_historical_node = true
4. Proactive Cache Warmup If you have a critical table and want to ensure it is always ready on the nodes that do own its tablets, you can use the Data Cache Warmup feature introduced in v3.3:
Copy code
sql
-- This forces the responsible nodes to pull the data from S3 into their EBS cache
CACHE SELECT * FROM your_important_mv;
5. If "Shared-Nothing" is a better fit If you have a table that is so critical it must reside on the local disk of every node for absolute maximum performance and redundancy, you would need to use a Shared-Nothing deployment (BEs instead of CNs) and set the
replication_num
to match the number of nodes in your cluster. This is generally not recommended if you have already committed to a Shared-Data architecture for its elasticity. Summary * Can you force a full copy on every node's EBS? No. * Does it matter for performance? No, because Broadcast Joins handle this in-memory for queries. * How to handle node failures/spot termination? Use Data Cache Sharing to pull "warm" data from survivors rather than S3. References • deployment/shared_data/feature-support-shared-data.md • data_source/data_cache.md • faq/shared_data_faq.md • administration/management/resource_management/Replica.md References • deployment/shared_data/feature-support-shared-data.md • deployment/shared_data/s3.md • table_design/table_design.md • knowledge/admin_and_operation/shared_data_compaction.md