Analytics & OLAP

StarRocks 4.1: Adaptive Tablets, Range Distribution, and the Upgrade Trade-Offs

StarRocks 4.1 makes tablet layout adaptive: Range distribution, automatic splitting at 10 GB, and 100 GB tablets. When to enable it, and what it costs.

JusDB Team
Published August 3, 2026
13 min read
StarRocks 4.1: Adaptive Tablets, Range Distribution, and the Upgrade Trade-Offs

StarRocks engineers describe a shared analytics table with 400 tenants where, at launch, the largest tenant held under 5% of the rows. Nine months later one tenant had outgrown all the others, and the symptoms were the ones every multi-tenant OLAP team eventually meets: query latency degraded unevenly, compaction pressure concentrated on a handful of tablets, and p99 stopped being predictable. Nothing was broken — the DISTRIBUTED BY HASH(tenant_id) BUCKETS 64 written on day one had simply outlived the data it was designed for. StarRocks 4.1 is the release that attacks that specific failure mode, by making the physical layout something the system revises at runtime instead of something you freeze at CREATE TABLE.

TL;DR
  • Range distribution (FE config enable_range_distribution, default false) buckets data by sort-key range instead of a fixed hash, so StarRocks can reshape tablets after the table exists — no schema change, no SQL change, no re-ingestion.
  • Tablets split automatically once they exceed tablet_reshard_target_size (default 10 GB), and the tablet ceiling rises to 100 GB in 4.1. Merge ships too, but gated behind tablet_reshard_enable_tablet_merge=false.
  • It is a trade, not a free win. At 32 concurrent threads Range delivered 1.86× the QPS (7.05 vs 3.79) and cut p99 from 36.6 s to 11.5 s — but a single-user warm-cache query was ~2× slower (3.2 s vs 1.5 s) because Colocate Join is off the table.
  • Bulk-loading a Primary Key table is ~1.7× slower under Range (1,075 s vs 624 s), which matters because there is no in-place migration: existing Hash tables never auto-convert.
  • Beyond distribution, 4.1 adds native SQL DELETE on Iceberg, incremental MVs on Iceberg (7–30× faster refreshes), recursive CTEs, and Skew Join v2.
  • Upgrade traps: skip the 4.1.0 container image, know that you can only downgrade to 4.0.6 or later, and test query_queue_v2 and the FULL OUTER JOIN ... USING semantics change before production.

What Actually Changed in StarRocks 4.1

StarRocks 4.1.0 shipped in April 2026, with 4.1.1 following on 29 May 2026. The headline is not a new engine or a new table model — it is the removal of a decision. For a decade, distributed OLAP systems have asked operators to predict, at table-creation time, how data would be distributed years later. StarRocks 4.1 stops asking.

The change is scoped to shared-data mode, where storage lives in object storage and compute nodes are stateless. That separation is what makes resharding cheap enough to automate: splitting a tablet is a metadata and file-level operation, not a cluster-wide data shuffle across local disks.

Everything else in the release — larger tablets, Fast Schema Evolution v2, cache observability — exists to make adaptive layout safe to run. If you read 4.1 as a list of features you will miss the design; it is one idea with a support structure around it.

How Range Distribution and Adaptive Tablets Work

Hash Buckets vs Sort-Key Ranges

Hash distribution assigns a row to a bucket by hashing the distribution key modulo a fixed bucket count. It is excellent at one thing: guaranteeing that rows with the same key land in the same place, which is what makes Colocate Join possible. It is bad at one thing: the bucket count is a promise you made before you had evidence.

Range distribution instead treats the key columns as an ordering hint. Each tablet owns a contiguous range of the sort key, and the boundaries of those ranges are runtime state that the FE can revise. The documentation puts it plainly: "the data will be sequenced according to the data range of the key columns, and each tablet contains the data from a certain range."

The Grow → Split → Merge Lifecycle

The system deliberately does nothing while a tablet grows. That patience is the point: it lets StarRocks distinguish a Black Friday spike from a tenant that has genuinely changed shape. In StarRocks' own validation run, a single tablet absorbed 64.8 million rows (1.5 GB) before the adaptive response triggered.

When a tablet crosses tablet_reshard_target_size, the FE enqueues a SPLIT_TABLET job. In that same validation, the 1.5 GB tablet was split along the real sort-key distribution into three tablets of 228 MB, 656 MB, and 656 MB — uneven by size because it split on where the data actually was, not on an arithmetic midpoint. The job was reported FINISHED within the same second the threshold was crossed.

Merge is the inverse: adjacent ranges recombine when a tenant shrinks or rows are deleted, preventing unbounded fragmentation. Merge shipped in 4.1 but is disabled by default, which has a real operational consequence covered below.

Why 100 GB Tablets Matter

From v4.1 the maximum tablet size rises to 100 GB. This inverts a decade of StarRocks and Doris tuning advice, where the standard move was to over-bucket early so no single tablet became hot.

Large tablets act as a shock absorber. They tolerate short-term imbalance, avoid premature fragmentation, and reduce total tablet count — which directly lowers FE metadata pressure and scheduling overhead. Fewer, bigger tablets plus automatic splitting is a strictly better trade than many, small, permanently wrong ones.

Merge is off by default, so splitting is effectively one-way today

Tablet merge ships behind tablet_reshard_enable_tablet_merge (default false), with default-on validation deferred to a later release. On a table with heavy deletes or churning tenants, splits accumulate and nothing reclaims them automatically. Track tablet counts per partition over time, and plan to run ALTER TABLE ... MERGE TABLETS manually during maintenance windows until the flag flips.

Enabling Range Distribution

Step 1 — Turn On the FE Config

Range distribution is an FE-level configuration, not a session variable, and it is off by default for backward compatibility. Set it dynamically to test, then persist it in fe.conf so it survives a restart.

sql
-- Dynamic, cluster-wide, effective immediately
ADMIN SET FRONTEND CONFIG ("enable_range_distribution" = "true");

-- Confirm it took
ADMIN SHOW FRONTEND CONFIG LIKE 'enable_range_distribution';

-- Split threshold (bytes). 10 GB is the default.
ADMIN SET FRONTEND CONFIG ("tablet_reshard_target_size" = "10737418240");

-- Opt in to automatic merge once you have validated split behaviour
ADMIN SET FRONTEND CONFIG ("tablet_reshard_enable_tablet_merge" = "true");

Persist the same keys in fe.conf on every FE node:

ini
# fe.conf
enable_range_distribution = true
tablet_reshard_target_size = 10737418240
tablet_reshard_enable_tablet_merge = false
tablet_reshard_max_parallel_tablets = 10240

Step 2 — Create the Table Without a Bucket Count

With the config on, you stop writing a DISTRIBUTED BY clause with a hard-coded bucket count. The primary key becomes the organizing hint, and the system owns the physical shape from there.

sql
-- Before 4.1: the bucket count is a permanent guess
CREATE TABLE tenant_events (
  tenant_id   BIGINT      NOT NULL,
  event_id    BIGINT      NOT NULL,
  event_ts    DATETIME    NOT NULL,
  payload     JSON        NULL
)
PRIMARY KEY (tenant_id, event_id)
PARTITION BY date_trunc('month', event_ts)
DISTRIBUTED BY HASH (tenant_id) BUCKETS 64;

-- In 4.1 with Range distribution: no bucket count to get wrong
CREATE TABLE tenant_events (
  tenant_id   BIGINT      NOT NULL,
  event_id    BIGINT      NOT NULL,
  event_ts    DATETIME    NOT NULL,
  payload     JSON        NULL
)
PRIMARY KEY (tenant_id, event_id)
PARTITION BY date_trunc('month', event_ts);

Step 3 — Drive Resharding Manually When You Need To

Automation handles the steady state, but 4.1 exposes explicit DDL for migrations, incident response, and post-backfill cleanup. Both statements accept a partition scope, an explicit tablet list, or a per-statement target size.

sql
-- Split everything that is over the target size
ALTER TABLE tenant_events SPLIT TABLETS;

-- Scope to a hot partition after a bulk backfill
ALTER TABLE tenant_events SPLIT TABLETS PARTITION (p202608);

-- Split named tablets you found while chasing a hotspot
ALTER TABLE tenant_events SPLIT TABLETS (9588955, 9588956, 9588957);

-- Reclaim fragmentation with a 2 GB target
ALTER TABLE tenant_events MERGE TABLETS
  PROPERTIES ("tablet_reshard_target_size" = "2147483648");

-- Merge two specific adjacent pairs
ALTER TABLE tenant_events MERGE TABLETS (9588955, 9588956)(9588958, 9588959);

A tablet is eligible to split when it exceeds the target size and the number of tablets already resharding is under tablet_reshard_max_parallel_tablets (default 10,240). Merge applies the mirror condition: adjacent tablets whose combined size falls below the target.

The Benchmark Numbers, and What They Cost You

StarRocks published a like-for-like comparison on two identical AWS shared-data clusters — 1 FE on m6i.xlarge, 3 CNs on m6i.4xlarge, 500 GB gp3 per CN — against a 200 GB dataset containing a 1-billion-row event log partitioned monthly and a 215-million-row Primary Key table. The results are unusually honest, because they show where Range loses.

WorkloadHash distributionRange distributionVerdict
Single user, warm cache (COUNT DISTINCT)1.5 s3.2 sHash wins ~2×
8 concurrent threads3.60 QPS3.53 QPSTie (<2%)
32 concurrent threads3.79 QPS7.05 QPSRange wins 1.86×
32 threads, p99 latency36.6 s11.5 sRange wins 3.2×
Mixed query + ingest, QPS2.865.31Range wins 1.86×
Mixed load, query p999.7 s4.8 sRange wins 2×
Bulk load, 1B-row duplicate-key table625 s591 sRange slightly ahead
Bulk load, 215M-row Primary Key table624 s1,075 sHash wins 1.7×

Read the Inflection Point, Not the Averages

The interesting detail is what the Hash cluster was doing at 32 threads: sitting over 90% CPU idle while p99 sat at 36.6 seconds. That is not a compute shortage, it is contention — threads queuing on the same hot buckets while most of the cluster does nothing. Range spreads shuffle work across independent tablets, so the same hardware actually gets used.

The single-user regression has the same root cause in reverse. Range gives up Colocate Join, so aggregations that were local become shuffles. If your workload is a handful of analysts running sequential queries, Range distribution will make your dashboards slower and buy you nothing.

A decision rule that fits on one line

Enable Range distribution when concurrency is high and the key distribution is unpredictable — customer-facing multi-tenant analytics, embedded dashboards, per-account APIs. Stay on Hash when concurrency is low, the distribution key is naturally even, or you depend on Colocate Join for large fact-to-fact joins. If you cannot name the tenant that will be 40% of your data in a year, that uncertainty is the argument for Range.

There Is No In-Place Migration Path

This is the operational fact the announcement posts underplay. Existing Hash-distributed tables do not auto-upgrade when you flip enable_range_distribution. Adopting Range means creating new tables and moving data into them — and the benchmark says a Primary Key backfill runs about 1.7× slower under Range because of post-commit compaction overhead.

Budget for that. A 215-million-row PK table that reloads in 10 minutes today should be planned as closer to 18. On a real estate of dozens of tables, this is a migration project with a cutover plan, not a config flag you flip on a Friday.

The Rest of 4.1 Worth Your Attention

Iceberg Gets Native DELETE

StarRocks 4.1 can run distributed SQL DELETE directly against Iceberg tables, producing standard V2 position delete files with atomic snapshot commits. For teams that have been shelling out to a Spark job every time GDPR erasure or a bad CDC batch needed correcting, that removes an entire piece of infrastructure from the loop.

sql
-- Correct a bad CDC batch without spinning up Spark
DELETE FROM iceberg_catalog.analytics.orders
WHERE order_date = '2026-07-14'
  AND source_system = 'legacy_pos';

Alongside it: incremental materialized views on Iceberg shift from partition-level recomputation to version-range delta processing, which StarRocks measured at 7–30× faster on subsequent refreshes against a 100 GB dataset. Refresh cost now tracks how much changed, not how big the table is. Iceberg v3 VARIANT support also lands, using offset-based binary field lookup so nested field access skips repeated JSON parsing.

Table maintenance improved too: 4.1 adds a rewrite_manifests procedure and extends expire_snapshots and remove_orphan_files with finer-grained arguments, plus TRUNCATE support for Hive and Iceberg tables.

Query Engine and Observability

  • Recursive CTEs — hierarchical rollups and graph traversal in plain SQL, no more application-side loops.
  • Skew Join v2 — statistics-driven detection with histogram support and NULL-skew awareness, replacing manual hints.
  • Fast Schema Evolution v2 — second-level DDL in shared-data mode, decoupled from tablet metadata rewrites.
  • Cache observability — per-query cache hit ratio in the audit log, cluster-wide cache metrics in Prometheus BE metrics, and I/O counters in the SQL profile.
  • Inverted index (beta) on shared-data, with a builtin parser and more analyzers planned.
  • Window functions accept ARRAY types, and COUNT DISTINCT now works over framed windows.

Upgrade Traps to Test Before Production

Do not deploy 4.1.0 in containers, and check your downgrade floor

StarRocks flagged a container image issue in v4.1.0 where BE processes fail reliably in containerised environments — Kubernetes and Docker users should go straight to 4.1.1. Separately, because of the tablet distribution changes, once you are on 4.1 you can only downgrade to v4.0.6 or later. If your cluster is on 4.0.3, upgrade to 4.0.6+ first so you keep a rollback path.

Four behaviour changes deserve a staging replay before you promote:

  1. query_queue_v2 is enabled by default in 4.1.0. Admission control now behaves differently under load. Replay a peak-hour workload and watch for queries queuing that previously ran immediately.
  2. FULL OUTER JOIN ... USING follows SQL-standard semantics. The USING column now appears once in the output instead of twice. Any downstream consumer doing positional column access or SELECT * into a fixed schema can break silently.
  3. ETL mode is on by default for batch operations, changing the execution profile of large INSERT and load jobs.
  4. 4.1.1 disabled query rewrite over INCREMENTAL/AUTO materialized views, and now rejects FORCE and partition refresh on them. If you built incremental MVs on 4.1.0 expecting transparent rewrite, verify your queries still hit them.

Also note that SQL transactions are now gated behind the enable_sql_transaction session variable, and lag/lead accept column references rather than constants only — the latter is additive, but it changes what the planner will accept from generated SQL.

What to Watch After You Enable Range Distribution

sql
-- Partition-level shape: bucket count, size, rows, compaction score, balance
SELECT PARTITION_NAME,
       BUCKETS,
       DATA_SIZE,
       ROW_COUNT,
       MAX_CS,
       TABLET_BALANCED
FROM information_schema.partitions_meta
WHERE DB_NAME = 'analytics_db'
  AND TABLE_NAME = 'tenant_events'
ORDER BY ROW_COUNT DESC;

-- Tablet-level detail for a partition you suspect is skewed
SHOW TABLET FROM analytics_db.tenant_events PARTITION (p202608);

The signal to track is not average tablet size — it is the ratio between your largest tablet and your median, which SHOW TABLET gives you directly via DataSize and RowCount. If that ratio climbs steadily while BUCKETS stays flat, splits are not firing: either tablet_reshard_target_size is set too high, or the tablet has not yet crossed it. If BUCKETS climbs monotonically and never falls, that is merge being disabled, and it is your cue to schedule a manual MERGE TABLETS pass. Watch MAX_CS alongside it — a rising compaction score on one partition is the earliest signal that resharding is not keeping up with ingestion.

Key Takeaways
  • Treat Range distribution as a workload decision, not an upgrade step. Enable it for high-concurrency multi-tenant tables with unpredictable key distribution; keep Hash where you rely on Colocate Join or run low-concurrency sequential queries.
  • Plan a migration, not a flag flip. Existing Hash tables never auto-convert, and Primary Key backfills run roughly 1.7× slower under Range — size the cutover window accordingly.
  • Skip 4.1.0 if you run containers and confirm you are on 4.0.6 or later before upgrading, so you keep a working downgrade path.
  • Enable merge deliberately. With tablet_reshard_enable_tablet_merge off, fragmentation only accumulates — monitor tablet counts and run ALTER TABLE ... MERGE TABLETS until the default changes.
  • Replay peak load in staging against query_queue_v2 and the FULL OUTER JOIN ... USING change before promoting 4.1 to production.
  • Use the new Iceberg DELETE to retire Spark jobs that exist purely for data correction, and switch eligible Iceberg MVs to incremental refresh for 7–30× faster refresh cycles.

Working with JusDB on StarRocks 4.1

JusDB runs StarRocks in production for teams that need real-time analytics without staffing a dedicated OLAP team. Our DBAs handle version upgrades and rollback planning, distribution and partition design, compaction and tablet health, Kafka and CDC ingestion pipelines, and 24/7 incident response — so a release like 4.1 becomes a scheduled change instead of a weekend.

If you are weighing Range distribution, we will benchmark it against your actual query mix and concurrency rather than a synthetic dataset, then build the migration and cutover plan around what the numbers say.

StarRocks Consulting →  |  StarRocks Performance Tuning  |  Talk to a DBA

Related reading:

Sources: StarRocks 4.1: Built for Production, Designed to Simplify, When Your Table Design Outlives Its Assumptions, and the StarRocks 4.1 release notes.

Share this article

JusDB Team

Official JusDB content team

Keep reading

TimescaleDB Hypertables, Continuous Aggregates & Compression (2026 Production Guide)

Use TimescaleDB for time-series data in PostgreSQL. Covers hypertable creation, continuous aggregates with refresh policies, retention policies, and 10-20x compression.

PostgreSQL1 minMay 13, 2026
Read

ClickHouse Explained (2026): MergeTree, Distributed Engine & Real-Time OLAP

Build blazing-fast analytics with ClickHouse columnar database. Learn MergeTree engine, data modeling, query optimization, and cluster deployment strategies.

ClickHouse5 minMay 13, 2026
Read

StarRocks vs ClickHouse: Architecture, Table Models & When to Choose Each (2026)

StarRocks is an open-source MPP analytics database. This 2026 guide covers FE/BE/CN architecture, the four table models, vectorized execution, and when to choose StarRocks over ClickHouse.

ClickHouse5 minMay 9, 2026
Read