跳到主要内容
最后于 更新

Changelog

Changelog 是 Doris 内表的行级变更日志。开启后,每一次写入产生的行级变化(新增、更新、删除)都会连同变更前后的值、提交时间戳一起持久化,作为 Table Stream、增量查询 和 物化视图增量维护(IVM) 的数据来源,也可以通过 Flink Doris Connector(26.4.0 及以上版本)在 Flink 中消费。

变更记录默认保留一天。请通过 changelog.ttl_seconds 设置能覆盖最长预期消费延迟的保留时长;变更记录过期不会删除基表中的数据。

Changelog 与跨集群复制(CCR)使用的 binlog 相互独立:它有自己的 changelog.* 属性和功能开关,同一张表不能同时开启两者。详见 CCR binlog。

实验性功能

该功能自 5.0.0 版本起提供,目前处于实验阶段,需要在 FE 中开启 enable_feature_changelog = true。

由 Row Binlog 更名而来

该功能在早期开发版本中名为 Row Binlog,通过 "binlog.enable" = "true" 和 "binlog.format" = "ROW" 开启,在首个正式版本发布前更名为 Changelog。这些早期版本以上述方式创建的表已无法继续使用,需要删除后用 "changelog.enable" = "true" 重新创建。

前置条件​

  • Doris 5.0.0 及以上版本。
  • FE 已在 fe.conf 中开启 enable_feature_changelog = true(非动态配置,需重启 FE)。该开关控制开启 Changelog 的表、提交时间戳(TSO)的分配以及 Table Stream 的消费。enable_feature_binlog 只控制 CCR binlog,使用 Changelog 不需要开启。
  • 如需自动清理,BE 也需在 be.conf 中开启 enable_feature_changelog = true(非动态配置,需重启 BE)。
  • 表模型为 Duplicate Key,或 Unique Key Merge-on-Write(MoW)且没有 cluster key,详见 支持范围与限制。
  • Changelog 只能在建表时开启,请在建表前完成评估。

基本概念​

概念说明
变更记录基表每提交一个事务,其中每一行的变化都会形成一条记录,包含操作类型(新增 / 更新 / 删除)、变更后的值,以及可选的变更前的值(before 镜像)
提交时间戳(TSO)每个写入事务提交时从 FE 获取的全局单调递增时间戳,由物理时间(毫秒)和逻辑计数两部分组成。同一事务内的所有变更共享同一个 TSO。Table Stream 的消费位点、@incr 的时间窗口,都以 TSO 作为标尺
LSN同一事务内变更记录的序号,与 TSO 一起决定变更记录的先后顺序

开启方式​

Changelog 只能在 CREATE TABLE 时通过表属性开启,且开启后不能关闭:

CREATE TABLE orders (
order_id BIGINT,
status VARCHAR(16),
amount DECIMAL(10, 2)
)
UNIQUE KEY(order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 8
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"changelog.enable" = "true",
"changelog.need_historical_value" = "true",
"changelog.ttl_seconds" = "86400"
);

binlog.* 属性(binlog.enable、binlog.ttl_seconds 等)用于配置 CCR binlog,而不是 Changelog,见 CCR binlog。

属性​

属性取值默认值建表后可否修改说明
changelog.enabletrue / falsefalse不可修改是否开启 Changelog。只能在 CREATE TABLE 中设置,或继承数据库的默认设置,见 数据库默认设置
changelog.need_historical_valuetrue / falsefalse不可修改是否记录变更前的值(before 镜像),需要表已开启 Changelog。仅 Unique Key MoW 表可设为 true;min_delta / detail 类型的 Table Stream、MIN_DELTA 增量查询和需要处理更新或删除的 IVM 都依赖它
changelog.ttl_seconds正整数(秒)86400(一天)可修改变更记录的保留时长,需要表已开启 Changelog。增量读取会过滤过期记录,后台 compaction 会回收过期记录的存储空间;不允许设置为 0 或负数。见 保留与清理

不可变属性的修改​

已有表上的 changelog.enable 和 changelog.need_historical_value 不能修改,因此既不能为已有表开启 Changelog,也不能关闭。这类修改会直接报错:

ALTER TABLE orders SET ("changelog.enable" = "false");
-- ERROR: not support change changelog.enable from true to false

ALTER TABLE orders SET ("changelog.need_historical_value" = "false");
-- ERROR: not support change changelog.need_historical_value from true to false

ALTER TABLE t_without_changelog SET ("changelog.enable" = "true");
-- ERROR: not support change changelog.enable from false to true

对于已经存在且没有开启 Changelog 的表,需要新建一张开启 Changelog 的表并导入数据,再用 ALTER TABLE ... REPLACE WITH TABLE 原子替换。

查看属性​

SHOW CREATE TABLE 会展示开启 Changelog 的表的 changelog.* 属性:

SHOW CREATE TABLE orders\G
...
"changelog.enable" = "true",
"changelog.ttl_seconds" = "86400",
"changelog.need_historical_value" = "true",
...

数据库默认设置​

数据库可以把 Changelog 设为其中新建表的默认设置:

CREATE DATABASE changelog_db PROPERTIES ("changelog.enable" = "true");

此后在 changelog_db 中新建的表默认开启 Changelog,除非建表时设置了 "changelog.enable" = "false";表未单独设置 changelog.ttl_seconds 和 changelog.need_historical_value 时,沿用数据库的设置。在这样的数据库中,表模型不支持 Changelog 的表(如 Aggregate Key 表)必须设置 "changelog.enable" = "false"。用 ALTER DATABASE ... SET PROPERTIES 修改默认设置,只影响之后新建的表。

在默认开启 Changelog 的数据库中,对未开启 Changelog 的表执行 SHOW CREATE TABLE,输出中会包含 "changelog.enable" = "false",这样 CREATE TABLE ... LIKE 以及从该输出复制的建表语句创建的也是未开启 Changelog 的表。

支持范围与限制​

表模型​

表模型是否支持说明
Duplicate Key支持只记录新增行;不支持 changelog.need_historical_value = true,否则报 Duplicate table model don't support record historical value
Unique Key,Merge-on-Write支持记录新增、更新、删除;可开启 before 镜像。不支持带 cluster key 的表,否则报 Unique merge-on-write tables with cluster keys do not support changelog
Unique Key,Merge-on-Read不支持报 Only duplicate and mow table model support changelog
Aggregate Key不支持同上

列类型​

限制说明
auto-increment 列不支持,建表和 ADD COLUMN 都会被拒绝
VARIANT 类型的列不支持,建表和 ADD COLUMN 都会被拒绝

部署模式​

存算一体与存算分离模式均支持开启 Changelog。

写入方式​

所有写入方式(INSERT、UPDATE、DELETE、Stream Load、Broker Load、Routine Load、Flink / Spark Connector 等)产生的变更都会被记录,包括部分列更新和灵活部分列更新。

临时表​

临时表不会开启 Changelog。为临时表设置 "changelog.enable" = "true" 会报 Cannot create temporary table with changelog enable;临时表也会忽略所在数据库的 Changelog 默认设置。

CCR binlog​

Changelog 与跨集群复制(CCR)使用的 binlog 不能同时使用:

  • 同一张表不能同时设置 "binlog.enable" = "true" 和 "changelog.enable" = "true";开启 Changelog 的表也不能修改 binlog.* 属性。
  • 开启 Changelog 的表不能在开启 CCR binlog 的数据库中创建或恢复;包含开启 Changelog 的表的数据库不能开启 CCR binlog。
  • 同一个数据库不能同时开启两者。

开启 Changelog 的表也不支持备份与恢复。

变更记录模型​

操作类型与隐藏列​

每条变更记录包含基表的全部可见列(变更后的值)以及下列隐藏列:

隐藏列类型说明
__DORIS_CHANGELOG_OP__BIGINT操作类型。在 changelog() 表函数的原始记录中:0 新增,1 更新,2 删除。在 @incr 增量查询中更新会被拆成两行:0 新增,1 删除,2 更新前,3 更新后
__DORIS_CHANGELOG_TSO__BIGINT提交时间戳
__DORIS_CHANGELOG_LSN__BIGINT事务内序号
__DORIS_CHANGELOG_BEFORE_<列名>__与原列相同变更前的值,每个非 key 列对应一个。仅 changelog.need_historical_value = true 时存在

基表自身还会增加一个隐藏列 __DORIS_COMMIT_TSO_COL__,记录每行最近一次写入的提交时间戳,SET show_hidden_columns = true 后可以直接查询:

SET show_hidden_columns = true;
SELECT order_id, status, __DORIS_COMMIT_TSO_COL__ FROM orders;

Duplicate Key 表​

Duplicate Key 表只有新增,每次写入的每一行都记录为一条新增记录,即使 key 相同也不会合并。

Duplicate Key 表上执行 DELETE FROM ... WHERE ... 使用删除谓词标记删除,不会产生变更记录,下游通过 Table Stream 或 @incr 无法感知这类删除。如果需要把删除传递给下游,请使用 Unique Key MoW 表。

Unique Key MoW 表​

写入操作记录内容
写入一个不存在的 key一条新增记录
写入一个已存在的 key(INSERT 同 key、UPDATE、部分列更新)一条更新记录,携带更新后的完整行;开启 before 镜像时还携带更新前的值
删除一个已存在的 key(DELETE、带删除标记的导入)一条删除记录。开启 before 镜像时携带删除前的值,否则只有 key 列有值
删除后再次写入同一个 key一条新增记录(前一个版本是删除标记,不是有效行)
写入的 sequence 列值小于当前可见行写入被丢弃,不产生变更记录

MoW 表的 DELETE 语句默认通过写入删除标记实现,因此会产生删除记录。如果表属性 enable_mow_light_delete 设为 true,DELETE 会改用删除谓词,与 Duplicate Key 表一样不产生变更记录,因此开启了 Changelog 的表请保持该属性为默认值 false。

部分列更新会记录合并后的完整行,而不是只记录被更新的列。灵活部分列更新(unique_key_update_mode = UPDATE_FLEXIBLE_COLUMNS)中,同一批次内对同一个 key 先删除再写入,会记录为一条更新记录:before 为删除前的旧行,after 为新写入的行,其中未提供的列取默认值或 NULL,不会沿用旧值。

以下示例展示 MoW 表(开启 before 镜像)的原始变更记录:

INSERT INTO orders VALUES (1, 'created', 100.00);
INSERT INTO orders VALUES (1, 'paid', 100.00);
DELETE FROM orders WHERE order_id = 1;
INSERT INTO orders VALUES (1, 'created', 150.00);

SELECT __DORIS_CHANGELOG_OP__ AS op, order_id, status, amount,
__DORIS_CHANGELOG_BEFORE_status__ AS before_status,
__DORIS_CHANGELOG_BEFORE_amount__ AS before_amount
FROM changelog("table" = "orders")
ORDER BY __DORIS_CHANGELOG_TSO__, __DORIS_CHANGELOG_LSN__;
+------+----------+---------+--------+---------------+---------------+
| op | order_id | status | amount | before_status | before_amount |
+------+----------+---------+--------+---------------+---------------+
| 0 | 1 | created | 100.00 | NULL | NULL |
| 1 | 1 | paid | 100.00 | created | 100.00 |
| 2 | 1 | paid | 100.00 | paid | 100.00 |
| 0 | 1 | created | 150.00 | NULL | NULL |
+------+----------+---------+--------+---------------+---------------+

对表 DDL 的约束​

开启 Changelog 的表只允许下列 ALTER TABLE 操作:

允许说明
ADD COLUMN / DROP COLUMN仅支持轻量级 schema change(表需开启 light_schema_change,默认开启);新增列会同步加入变更记录
CREATE INDEX / DROP INDEX仅支持轻量级索引变更
ADD ROLLUP / DROP ROLLUP / RENAME ROLLUP
分区操作:ADD PARTITION、DROP PARTITION、MODIFY PARTITION、RENAME PARTITION、REPLACE PARTITION
RENAME 表、REPLACE WITH TABLE
修改表注释、列注释
修改分桶方式
修改表属性bloom_filter_columns / bloom_filter_fpp 除外
ENABLE FEATURE(如 SEQUENCE_LOAD)内部转换为 ADD COLUMN

其它操作会被拒绝,报 Not allowed to perform current operation on Table With changelog,典型的有:

  • MODIFY COLUMN(修改列类型、位置等)
  • RENAME COLUMN
  • ORDER BY(重排列顺序)
  • BUILD INDEX
  • 修改 bloom filter 相关属性

开启了 Changelog 的表可以正常执行 TRUNCATE TABLE,但不支持备份与恢复。对基表结构和分区的变更如何影响已创建的 Table Stream,见 Table Stream 进阶。

保留与清理​

自 Doris 5.0.0 起,changelog.ttl_seconds 控制 Changelog 的保留时长,存算一体和存算分离模式均支持。保留时长按提交 TSO 中的物理时间计算,以当前 Master FE 的 TSO 为基准。提交时间小于等于「基准时间减去 TTL」的记录视为过期。即使表没有新写入,保留窗口也会随时间推进。

配置保留时长​

TTL 默认值为 86400 秒(一天)。如果数据库设置了 changelog.ttl_seconds,新建的开启 Changelog 的表会继承该值,表属性中的显式设置优先。修改数据库配置不会改变已有表的 TTL。CREATE TABLE ... LIKE 会复制源表的 TTL。

例如,将上文创建的 orders 表的保留时长改为七天:

ALTER TABLE orders SET ("changelog.ttl_seconds" = "604800");

语句成功后,SHOW CREATE TABLE orders 的输出包含:

"changelog.ttl_seconds" = "604800"

TTL 必须大于 0,不能通过 0 或 -1 关闭过期机制。缩短 TTL 可能立即使已有记录过期;延长 TTL 可以使尚未被物理清理的记录重新可读,但无法恢复已被清理的记录。如果 ALTER TABLE 在向分区下发配置时失败,较短的 TTL 可能已经生效,请检查 SHOW CREATE TABLE 并重试同一条语句,完成配置更新。

对读取的影响​

增量读取在生成执行计划时应用保留窗口,不需要等待物理清理完成。同一条语句使用同一个基准 TSO,每条新语句都会重新计算窗口。

读取方式请求的起点已过期时的行为
未指定 startTimestamp 的 @incr仅读取保留窗口内的变更,MIN_DELTA 模式也适用
显式指定 startTimestamp 的 @incr,且 incrementType = MIN_DELTA报 Changelog offset has expired according to changelog.ttl_seconds,避免返回不完整的净变化
incrementType = DETAIL 或 APPEND_ONLY 的 @incr将实际起点推进到第一个仍保留的 TSO,仅返回请求窗口内尚未过期的部分
type = min_delta 的 Table Stream任一分区的消费位点已过期时,报相同错误
type = detail 或 append_only 的 Table Stream跳过过期变更,从第一个仍保留的 TSO 开始读取
MoW 表的快照读取(FOR TIME AS OF、FOR VERSION AS OF 或 Stream 的 @snapshot())重建需要读取 Changelog 时,已过期的目标快照报相同错误,因为所需的 before 镜像已不在保留窗口内
changelog() 表函数不应用 TTL 过滤;物理清理前仍可能读到已过期的原始记录

过期机制不会等待消费者。即使 IS_STALE / STALE_REASON 仍显示 Stream 可用,也应在保留窗口内完成消费:当前实现不会通过这两个字段报告 TTL 过期。所需变更过期后,需要从全量快照重建下游数据。对于 Table Stream,可按 reset 流程 全量重载下游并推进位点。IVM 的内部 Stream 请通过 物化视图刷新任务 管理。

示例:变更过期后基表数据仍保留​

在已有数据库中执行以下示例。执行用户需具备建表所需的 CREATE 权限、写入所需的 LOAD 权限,以及查询所需的 SELECT 权限:

CREATE TABLE changelog_ttl_demo (id INT, value INT)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES (
"replication_num" = "1",
"changelog.enable" = "true",
"changelog.ttl_seconds" = "30"
);

INSERT INTO changelog_ttl_demo VALUES (1, 10);

SELECT COUNT(*) AS change_count
FROM changelog_ttl_demo@incr("incrementType" = "DETAIL");

写入后立即查询,结果为:

+--------------+
| change_count |
+--------------+
| 1 |
+--------------+

等待超过 30 秒后,再次执行增量查询:

SELECT COUNT(*) AS change_count
FROM changelog_ttl_demo@incr("incrementType" = "DETAIL");
+--------------+
| change_count |
+--------------+
| 0 |
+--------------+

基表中的行仍然存在:

SELECT id, value FROM changelog_ttl_demo;
+------+-------+
| id | value |
+------+-------+
| 1 | 10 |
+------+-------+

物理清理与容量规划​

后台 compaction 会回收 Changelog 中已全部过期的 Rowset,即使表没有新写入也会执行。包含未过期记录的 Rowset 会保留到其中所有记录都过期。清理会保留基表数据和版本连续性。存储空间异步回收,因此 TTL 不代表文件必须在该时刻消失,也不构成严格的存储大小上限。

自动清理要求 BE 开启 enable_feature_changelog = true,且自动 compaction 未被关闭。将 BE 配置或表属性 disable_auto_compaction 设为 true 会暂停自动清理,但增量读取仍会过滤过期记录。BE 重启后,清理会等待 Master FE 心跳下发有效的基准 TSO。

Changelog 只按时间保留,没有大小或条数上限。请根据写入量、TTL 和清理延迟预留存储空间;对开启 before 镜像的 MoW 表,每次更新还会额外记录一份旧值。TTL 应覆盖最长消费延迟、刷新间隔和预期故障停机时间,并为恢复预留余量。

写入开销​

开启 Changelog 后,每次写入需要额外生成并持久化变更记录;对 MoW 表,更新和删除还需要读取旧值以生成 before 镜像。导入吞吐会有可感知的下降,实际幅度与表结构、更新比例有关。建议:

  • 只对确实需要增量消费的表开启。
  • 上线前使用真实的写入负载评估吞吐变化。
  • 不需要变更前的值时(例如只做 append_only 消费、只关心最新值),不要开启 changelog.need_historical_value。

用 changelog() 表函数排查​

changelog() 表函数返回一张表的原始变更记录,例如用来确认某次写入是否产生了预期的变更、查看某个 key 的变更历史。

警告

changelog() 主要用于内部调试,不建议在正式数据处理流程中使用。它的输出格式和参数可能随版本变化,正式的增量消费请使用 Table Stream 或 @incr。

SELECT __DORIS_CHANGELOG_OP__, __DORIS_CHANGELOG_TSO__, __DORIS_CHANGELOG_LSN__,
order_id, status, amount
FROM changelog(
"db" = "demo",
"table" = "orders",
"partition" = "p20260914"
)
WHERE order_id = 1
ORDER BY __DORIS_CHANGELOG_TSO__, __DORIS_CHANGELOG_LSN__;
参数是否必填说明
table是表名
db否数据库名,默认为当前数据库
partition否分区名,多个用逗号分隔,默认全部分区
tablet否tablet ID,多个用逗号分隔,默认全部 tablet

changelog() 直接读取存储的原始记录,不做变更折叠或 TTL 过滤,返回的 __DORIS_CHANGELOG_OP__ 使用原始编码(0 新增、1 更新、2 删除)。物理清理完成前,这里仍可能看到过期记录。对开启 changelog.need_historical_value = true 的表,SELECT * 读取 changelog() 时还会返回 before 镜像列,这些列名以保留前缀 __DORIS_ 开头;在 changelog() 之上定义视图、CTAS 表或物化视图时,需要列出所需的列,并为隐藏列和 before 镜像列指定别名。完整语法见 CHANGELOG 表函数。

常见错误对照​

错误信息原因处理
Property binlog.format is no longer supported, use "changelog.enable" = "true" to enable changelog, or remove it for CCR binlog语句中使用了早期开发版本用来开启该功能的 binlog.format用 "changelog.enable" = "true" 开启 Changelog;如果是 CCR binlog,删除该属性
Property binlog.need_historical_value is no longer supported, use changelog.need_historical_value instead语句中使用了早期开发版本的属性名改用 changelog.need_historical_value
Table <db>.<table> was created with "binlog.format" = "ROW", which is no longer supported; drop the table and create it again with "changelog.enable" = "true"该表由早期开发版本创建删除该表,再用 "changelog.enable" = "true" 重新创建
Cannot create table <table> with changelog when enable_feature_changelog=falseFE 未开启 Changelog在 fe.conf 中设置 enable_feature_changelog = true 并重启 FE
changelog.ttl_seconds must be greater than 0TTL 为 0 或负数设置以秒为单位的正整数,默认值为 86400
Changelog offset has expired according to changelog.ttl_secondsMIN_DELTA 起点或 MoW 快照超出保留窗口从全量快照重建下游数据并重新对齐消费位点,见 对读取的影响
not support change changelog.enable from false to true对已有表用 ALTER TABLE 开启 ChangelogChangelog 只能在建表时开启。新建开启 Changelog 的表并导入数据,再用 ALTER TABLE ... REPLACE WITH TABLE 原子替换
not support change changelog.enable from true to false尝试在已开启 Changelog 的表上设置 changelog.enable = falseChangelog 开启后不可关闭
not support change changelog.need_historical_value from true to false尝试修改 changelog.need_historical_value该属性建表后不可修改
Property <property> requires "changelog.enable" = "true"建表时为未开启 Changelog 的表设置了 changelog.ttl_seconds 或 changelog.need_historical_value为该表开启 Changelog,或去掉该属性
changelog.ttl_seconds can not be set because changelog is not enabled for table: <table>用 ALTER TABLE 为未开启 Changelog 的表设置 changelog.ttl_seconds只有开启 Changelog 的表才有变更记录保留时长
Duplicate table model don't support record historical valueDuplicate Key 表设置了 changelog.need_historical_value = trueDuplicate Key 表不支持 before 镜像,去掉该属性;需要 before 镜像时改用 Unique Key MoW 表
Unique merge-on-write tables with cluster keys do not support changelogMoW 表带有 cluster key建表时不要指定 cluster key
Only duplicate and mow table model support changelog表模型为 Aggregate Key 或 Unique Key Merge-on-Read改用 Duplicate Key 或 Unique Key MoW 表。在默认开启 Changelog 的数据库中,为这类表设置 "changelog.enable" = "false"
Table <table> can not enable both binlog and changelogCREATE TABLE 同时把 binlog.enable 和 changelog.enable 设为 true二者只能选一个,见 CCR binlog
Can not create table <table> with changelog in database <db> because the database enables binlog所在数据库开启了 CCR binlog在未开启 CCR binlog 的数据库中建表
Cannot create temporary table with changelog enable为临时表设置了 "changelog.enable" = "true"临时表不能开启 Changelog
Not allowed to perform current operation on Table With changelog执行了 MODIFY COLUMN、RENAME COLUMN、ORDER BY、BUILD INDEX 或修改 bloom filter 属性等不允许的 DDL见 对表 DDL 的约束 中的允许清单