Databend Incremental Materialized Views - Change Tracking, Incremental Refresh, and Consistent Reads
sundy-liSep 2, 2026
A Refresh May Lag; Query Results Must Not
A materialized view (MV) precomputes and persists the result of a frequently executed query. The additional storage reduces query latency, repeated computation, and the amount of source data scanned.
Persisting a result is the easy part. A production implementation must also answer harder questions:
-
How should the result be maintained while the source table continues to change?
-
Can refresh work run without delaying source-table writes?
-
What should a query return when the persisted result is behind the source?
-
How should aggregate state, physical layout, and storage maintenance evolve independently?
Databend's new materialized view design stores the precomputed result in an independent, read-only FUSE table. Change Tracking identifies source-table changes since the last refresh, while a checkpoint records the source position already consumed by the MV. The refresh path and the consistency path are deliberately separated: background maintenance may lag, but the read path selects a plan that still produces the correct result.
That plan may read the persisted MV directly, merge an unmaterialized delta at query time, or bypass the MV and execute against the source table. The central design principle is straightforward: delayed maintenance may reduce performance, but it must not silently expose an outdated answer.
Materialized View Maintenance Models and Their Trade-offs
Materialized view implementations make two related decisions. The first is when to maintain the result: synchronously in the write transaction or asynchronously in the background. The second is how much to recompute: only the affected data or the complete defining query.
These choices produce several common maintenance models. A database may support more than one of them.
| Maintenance model | Typical implementation | Freshness | Effect on writes | Cost and fit |
|---|---|---|---|---|
| Synchronous incremental maintenance | Update derived results inside the source write transaction; common in insert-triggered or write-driven MVs | Current immediately after commit | Write amplification, lock contention, and MV failures enter the write path; complex aggregation can increase write latency | Best for lighter writes, simple derivations, and workloads that prioritize minimum read latency |
| Asynchronous full refresh | Re-execute the defining query on a schedule or on demand, then atomically replace the result | May be stale between refreshes unless the system recomputes from the source at read time | Normal writes usually continue, but refresh scans and rewrites the full dataset | Simple semantics; appropriate for small or medium tables, infrequent refreshes, or aggregate workloads with many updates and deletes |
| Asynchronous incremental refresh | Consume only a change interval from a log, Stream, CDC feed, or Snapshot difference | Depends on scheduling and requires an explicit consistency strategy | Writes retain trackable changes but do not normally wait for MV computation | Well suited to append-only fact and event tables; requires reliable change semantics and checkpoints |
| Asynchronous incremental refresh with query compensation | Persist processed history asynchronously and merge the unprocessed delta during reads; fall back to the source when compensation is unsafe | Can remain current even when refresh lags | Keeps refresh work out of the write transaction, with additional read-time work when the MV is behind | Useful when both ingestion throughput and correct, current analytical results matter |
Databend uses asynchronous maintenance and chooses between incremental refresh and full rebuild according to the changes that actually occurred. At query time, it selects a Fresh, Hybrid, or Live Fallback plan according to the MV's materialization progress.
| Design question | Synchronous maintenance | Asynchronous maintenance | Databend's approach |
|---|---|---|---|
| Does MV maintenance affect writes? | Yes. Derived computation is part of the source commit path. | The source write generally does not wait for MV refresh. | A refresh pins one source Snapshot. New writes continue committing to newer Snapshots. |
| Does refresh always scan the full table? | Not necessarily, although complex changes can make maintenance expensive. | It may use either full or incremental refresh. | The initial refresh is full. Append-only changes are incremental. An aggregate MV is fully rebuilt after an |
| What happens when refresh falls behind? | Refresh lag is generally not a separate state. | The database may return an old result, compensate, or read from the source. | Fresh reads the MV, Hybrid applies read fix, and all unsafe cases fall back to the source. |
| How are complex aggregates maintained? | Maintain state during writes or restrict supported operators. | Recompute when necessary, with cost increasing with data size. | Persist mergeable aggregate states; use a full rebuild when a change cannot be safely reversed. |
An Independent Storage Model for Materialized Results
A Databend materialized view is not an index attached to source-table Blocks. It is a specialized, read-only FUSE table with
engine = MATERIALIZED_VIEW
TableMeta
This separation gives the MV an independent refresh position and allows its physical layout to follow its own query pattern rather than the ingestion pattern of the source table.
From Aggregating Indexes to Standalone Materialized Views
Databend's earlier Aggregating Index model behaves like a Block-level projection that moves with the source table. Both designs can avoid repeated computation, but they differ in storage ownership and lifecycle.
| Dimension | Aggregating Index | New materialized view |
|---|---|---|
| Storage ownership | Attached to source-table Blocks | Independent FUSE storage |
| Physical layout | Constrained by source Blocks and source-table maintenance | Independent Cluster Key, Blocks, and maintenance strategy |
| Query access | Primarily selected transparently by the optimizer | Can be queried directly or selected through query rewrite |
| Refresh progress | No natural independent consumption endpoint | Maintains a source checkpoint and its own Snapshot |
| Source compact/recluster | A changed source Block usually requires its corresponding index to be rebuilt | MV storage can be compacted and reclustered independently |
Metadata and Transaction Boundaries
The system keeps three categories of metadata to describe the physical result, the view definition, and the dependency on the source table.
| Metadata | Key contents | Purpose |
|---|---|---|
| Physical Schema, MV Snapshot, source table ID and sequence, source Snapshot location | Describes the independently stored table and its refresh endpoint |
| Original query, rewritten Physical Query, Logical Schema | Stores the larger definition separately to avoid write amplification from frequent |
| Source-to-MV binding and generation | Maintains the dependency between the source table and the MV |
Creating, replacing, or dropping an MV updates the table metadata, MV definition, and source dependency in one Meta transaction. Creation also enables Change Tracking on the source table atomically. There is therefore no intermediate state in which the MV has been published but subsequent source changes cannot yet be tracked.
From Refresh to Read: Maintaining Correct Results
Refresh progress, persisted data, and query planning form one consistency mechanism. The refresh path consumes source changes through Change Tracking and advances a checkpoint. The read path compares the checkpointed source Snapshot with the source table's current Snapshot to decide whether the stored result can be used as-is.

Change Tracking and Checkpoints
Databend reuses the change-table semantics already provided by Streams instead of implementing a separate CDC mechanism for materialized views. A refresh reads the changes between two consumption endpoints. Each change includes internal columns such as:
| Internal column | Meaning |
|---|---|
| The change operation, such as |
| Whether the record is part of an update |
| The stable identity of the source row |
After a successful refresh, the materialized result and its checkpoint are committed in the same transaction:
materialized_view_source_table_seq
materialized_view_source_snapshot_location
Atomic commit prevents two invalid states: persisted MV data without a corresponding checkpoint advance, and an advanced checkpoint without the data it claims has been materialized.
The checkpoint still advances when all rows in a change batch are filtered out by the MV's
WHERE
Refresh Strategy Follows the Actual Change Set
At the beginning of a refresh, Databend locks the MV, reloads its definition, and pins the source Snapshot for that run. Writes that arrive during the refresh continue committing to newer source Snapshots and are left for a later refresh.
The changes between the previous checkpoint and the pinned Snapshot determine the maintenance strategy.
| Source endpoint state | Refresh operation | Rationale |
|---|---|---|
| Initial refresh | Execute the complete query through | No starting checkpoint exists, so Databend must establish the initial result. |
| Run the Physical MV Query on the delta, then use | Append-only changes can safely add result rows or mergeable aggregate states. |
| Internally apply | Stable source-row identity maps a deletion or update to the correct materialized row. |
| Rebuild the complete result with | States such as |
| Unchanged Snapshot | Process checkpoint semantics only | There is no new data to materialize. |
| Empty source table | Use an empty endpoint | Avoid unnecessary scanning. |
For an aggregate MV, an append-only refresh writes aggregate states, not finalized scalar results. A Group Key may temporarily have states in multiple Blocks. Functions such as
sum_merge
count_merge
min_merge
Three Read Paths: Fresh, Hybrid, and Live Fallback
When a query accesses an MV, the Binder compares the source Snapshot recorded in the MV checkpoint with the current source Snapshot.
| Read path | Condition | Query plan | Correctness and cost |
|---|---|---|---|
| Fresh | The two Snapshots match. | Scan the MV's Physical Storage; merge states for an aggregate MV. | Lowest cost, with a current result. |
| Hybrid, or read fix | The MV is behind, and all changes after its checkpoint are append-only. | Persisted MV Storage UNION ALL Physical MV Query(Source Delta), followed by state merging when required. | Completes the result at query time without changing the MV or waiting for refresh. |
| Live Fallback | The initial refresh has not completed; the delta contains an update or deletion; a required historical Snapshot has been garbage-collected; or the source binding is invalid. | Ignore persisted MV storage and execute the original logical query against the source table. | More expensive, but never returns a stale or incorrect result. |
Read fix is query-plan compensation, not an automatic background refresh. This distinction matters operationally: a delayed refresh can increase query work, but it does not change the answer returned to the user.
Mergeable Aggregate State and Independent Storage Maintenance
Incremental materialization must do more than append each new result. It must preserve mergeable state across batches and prevent state rows and small files from accumulating without bound.
Logical Schema Versus Physical Schema
The columns visible to a user do not always match the columns stored physically. Consider
avg(amount)
| Layer | | Role |
|---|---|---|
| Logical Schema | | The result column exposed to the user |
| Physical Schema | | Mergeable state persisted across incremental batches and Blocks |
| Read projection | | Converts physical state into the logical result |
Initial support covers common aggregates including
sum
min
max
avg
count
approx_count_distinct
avg
sum_state + count_state
_mv_source_row_id
Reaggregate, Compact, and Recluster
Repeated append-only refreshes can create multiple state rows for the same Group Key. The query result remains correct because the read path merges those states, but the amount of merge input and the degree of file fragmentation increase over time.
Before compact or recluster writes a new Block, Databend performs reaggregation: states with the same Group Key inside that output Block are combined.
| Operation | What it does | What it does not do |
|---|---|---|
| Reaggregate | Groups by business columns and merges duplicate Aggregate State within the newly written Block | Does not guarantee one global convergence pass across every Block in the MV |
| Reorganizes MV Blocks and triggers reaggregation on output Blocks | Does not allow users to change the MV's logical data |
| Reorganizes data according to the MV's own Cluster Key | Does not inherit the clustering layout of the source table |
An MV that contains
GROUP BY
_mv_source_row_id
Query Rewrite and a Complete Usage Example
Applications may query an MV directly or continue querying the source table. For source queries, the optimizer compares output expressions, filters, Group Keys, aggregate functions, aggregation granularity, and required columns. When the query matches, the optimizer rewrites it to an MV read plan. That rewritten plan still chooses Fresh, Hybrid, or Live Fallback according to the current data state.
The following MV summarizes paid orders by customer:
CREATE MATERIALIZED VIEW paid_orders_by_customer
(customer_id, total_amount, order_count, average_amount)
CLUSTER BY (customer_id)
AS
SELECT
customer_id,
sum(amount),
count(*),
avg(amount)
FROM orders
WHERE paid
GROUP BY customer_id;
Creation publishes the definition but does not populate the MV immediately. Before the first refresh, a direct query uses Live Fallback. The answer is correct, but the persisted MV provides no acceleration yet.
After the initial refresh, append-only changes can be consumed incrementally. Storage maintenance can be scheduled as the number of incremental batches grows.
-- Build the initial materialized result.
REFRESH MATERIALIZED VIEW paid_orders_by_customer;
-- If subsequent changes are inserts only, consume the delta.
REFRESH MATERIALIZED VIEW paid_orders_by_customer;
-- Reorganize storage as incremental batches accumulate.
OPTIMIZE TABLE paid_orders_by_customer COMPACT;
ALTER MATERIALIZED VIEW paid_orders_by_customer RECLUSTER FINAL;
Where Incremental Materialized Views Fit
The value of an incremental MV depends on the source change pattern, the defining query, and the workload's refresh and consistency requirements. The current Databend implementation is strongest for single-table analytical workloads whose changes can usually be consumed as append-only deltas. It is not intended to replace every continuous data transformation pipeline.
Recommended Workloads
Logs, events, behavioral data, and append-only fact tables. Historical records in these datasets are rarely updated or deleted. Each refresh can process only the unconsumed interval instead of rerunning the full source query.
Frequent
GROUP BY
Write-throughput-sensitive systems that cannot accept stale answers. Asynchronous refresh keeps derived computation out of the source write transaction. If the MV falls behind and the delta is safe to merge, read fix supplies the missing rows during the query. If it is not safe, Databend reads from the source.
Workloads in which ingestion and analytics need different physical layouts. The source may be organized around ingestion time, while the MV uses a customer or business dimension as its Cluster Key. Compact and recluster operations can then maintain the two layouts independently.
What to Consider Before Adoption
Definitions currently focus on a single persistent FUSE table. An MV can currently be created from one persistent FUSE table in the
default
SELECT ... FROM ... [WHERE ...] [GROUP BY ...]
JOIN
Updates and deletes can make aggregate refresh expensive. A non-aggregate MV can project an
UPDATE
DELETE
_mv_source_row_id
Refresh scheduling must be configured explicitly. A Databend Task can run
REFRESH MATERIALIZED VIEW
Incremental processing depends on continuous Change Tracking history. If a historical Snapshot required for incremental refresh or read compensation has already been garbage-collected, Databend can no longer reconstruct that delta and uses Live Fallback. Snapshot retention, refresh frequency, ingestion rate, and query requirements therefore need to be planned together.
Materialized views are read-only objects. Ordinary
INSERT
UPDATE
DELETE
Correctness Determines the Value of Incremental Maintenance
Databend's new materialized view is not merely a query-result cache. It forms an end-to-end incremental computation path: Change Tracking identifies source changes, a checkpoint records the consumption position, the Physical Schema persists mergeable state, refresh advances data and position atomically, and read fix preserves consistent reads while maintenance is asynchronous.
This model is particularly effective for continuously ingested, append-heavy, single-table workloads that require stable analytical answers. Multi-table dependencies, frequent updates or deletes, and workloads that expect fully automatic scheduling require a different pipeline today—or a careful assessment of full-rebuild cost and future Dynamic Table capabilities.
The relevant engineering question is therefore not only whether an MV avoids computation. It is whether the refresh model, mutation pattern, and storage-maintenance cost remain favorable over the full lifecycle of the workload.
Incremental materialized views were introduced and updated in Databend Enterprise Edition after v1.2.934.
Subscribe to our newsletter
Stay informed on feature releases, product roadmap, support, and cloud offerings!



