Vertica Epoch 机制详解¶
Epoch 概述¶
Epoch 是一个 64 位数字,代表 Vertica 中数据的逻辑时间戳。每一行都有一个隐式存储的列,记录该行被提交时的 epoch。
=> CREATE TABLE testdata (a INT, b INT);
=> INSERT INTO testdata VALUES (1,2);
=> INSERT INTO testdata VALUES (3,4);
=> COMMIT;
=> INSERT INTO testdata VALUES (5,6);
COMMIT;
=> SELECT a,b,epoch FROM testdata;
a | b | epoch
---+---+-------
1 | 2 | 1898
3 | 4 | 1898
5 | 6 | 1899
(3 rows)
当系统的逻辑状态发生变化,或者数据通过 DML 操作(INSERT、UPDATE、MERGE、COPY、DELETE)提交时,epoch 会推进。
EPOCHS 系统表包含每个已关闭 epoch 的日期时间和对应的 epoch 编号,可用于确定哪些时间段对应哪些 epoch:
=> SELECT * FROM epochs;
epoch_close_time | epoch_number
-------------------------------+--------------
2015-11-09 17:47:21.013641+00 | 1350
2015-11-09 18:03:30.454707+00 | 1351
2015-11-09 18:04:48.329494+00 | 1371
2015-11-09 18:18:42.796702+00 | 1372
2015-11-09 18:19:52.738018+00 | 1392
2015-11-09 18:26:43.655577+00 | 1393
2015-11-11 15:21:47.918074+00 | 1394
2015-11-11 15:23:16.80952+00 | 1897
2015-11-20 18:13:20.324436+00 | 1898
2015-11-20 18:13:20.372756+00 | 1899
(10 rows)
Epoch 类型¶
Epoch Map 是 AHM 到 LE 之间的 epoch 列表,存储在数据库 catalog 中。

Current Epoch (CE)¶
当前 epoch 是一个开放的 epoch,在 COMMIT 操作后成为 Latest Epoch (LE)。COMMIT 时的 current_epoch 即为该 DML 的 epoch。
=> SELECT CURRENT_EPOCH FROM SYSTEM;
CURRENT_EPOCH
---------------
629415
(1 row)
=> INSERT INTO test VALUES (10,20); COMMIT;
=> SELECT current_epoch FROM SYSTEM;
current_epoch
---------------
629416
(1 row)
Latest Epoch (LE)¶
最新关闭的 epoch。COMMIT 操作后的 current epoch 即成为 latest epoch。
Checkpoint Epoch (CPE)¶
每个 projection 的 checkpoint epoch 是 WOS 中无数据的最新 epoch,即 projection 可以恢复到的时间点。Tuple Mover 的 moveout 操作将数据从 WOS 移到 ROS 时推进 CPE。
=> SELECT node_name, projection_schema schema, projection_name p_name,
is_up_to_date UTD, checkpoint_epoch CPE,
would_recover WR, is_behind_ahm BAHM
FROM projection_checkpoint_epochs;
node_name | schema | p_name | UTD | CPE | WR | BAHM
------------------------+---------+--------------------------+-------+-----------+-------+-----
v_test_israel_node0001 | public | product_dimension_b1 | t | 1347 | f | f
v_test_israel_node0003 | public | product_dimension_b1 | t | 1347 | f | f
v_test_israel_node0002 | public | product_dimension_b1 | t | 1347 | f | f
v_test_israel_node0002 | store | store_dimension_b1 | t | 1347 | f | f
v_test_israel_node0003 | store | store_dimension_b1 | t | 1347 | f | f
v_test_israel_node0001 | store | store_dimension_b1 | t | 1347 | f | f
v_test_israel_node0002 | public | promotion_dimension_b1 | t | 1347 | f | f
v_test_israel_node0001 | public | promotion_dimension_b1 | t | 1347 | f | f
v_test_israel_node0003 | public | promotion_dimension_b1 | t | 1347 | f | f
v_test_israel_node0001 | public | warehouse_dimension_b1 | t | 1347 | f | f
v_test_israel_node0002 | public | warehouse_dimension_b1 | t | 1347 | f | f
v_test_israel_node0003 | public | warehouse_dimension_b1 | t | 1347 | f | f
Last Good Epoch (LGE)¶
所有节点中最小的 checkpoint epoch。LGE 是手动恢复可以恢复到的最新 epoch。LGE 由磁盘上所有数据的快照组成。集群异常关闭时,LGE 之后的数据会丢失。
- Tuple Mover 推进 CPE 并设置新的 LGE
- 如果 Tuple Mover 失败,数据不会从 WOS 移到 ROS,CPE 和 LGE 都不会推进
- 每个节点有一个 LGE(该节点上 projection 的最小 CPE)
- Vertica 从各节点 LGE 计算集群 LGE,利用 K-safety 提供尽可能高的 LGE
-- 查看集群 LGE
=> SELECT GET_LAST_GOOD_EPOCH();
GET_LAST_GOOD_EPOCH
---------------------
1347
(1 row)
-- 查看各节点 LGE(含恢复计算过程)
=> SELECT GET_EXPECTED_RECOVERY_EPOCH();
INFO 4544: Recovery Epoch Computation:
Node Dependencies:
011 - cnt: 253
101 - cnt: 253
110 - cnt: 253
111 - cnt: 239
Nodes certainly in the cluster:
Node 0(v_test_israel_node0001), epoch 1347
Node 1(v_test_israel_node0002), epoch 1347
Filling more nodes to satisfy node dependencies:
Data dependencies fulfilled, remaining nodes LGEs don't matter:
Node 2(v_test_israel_node0003), epoch 1347
--
GET_EXPECTED_RECOVERY_EPOCH
-----------------------------
1347
(1 row)
Ancient History Mark (AHM)¶
AHM 之前的 epoch 的历史数据可以从物理存储中 purge。任何早于 AHM 的历史查询都无法执行。
- 默认情况下,Vertica 每 3 分钟将 AHM 推进到与 LGE 相等
- 当集群中有节点 DOWN 或存在未刷新的 projection 时,AHM 不会推进
- AHM 永远不会超过 LGE
Epoch 推进过程(完整演示)¶
DML 事务(INSERT、UPDATE、MERGE、COPY、DELETE)的 COMMIT 同时推进 CE 和 LE。CE 移动 1,LE 也移动 1,current epoch 变为 latest epoch。
=> CREATE TABLE test_epochs (c1 int);
CREATE TABLE
=> SELECT CURRENT_EPOCH, AHM_EPOCH, LAST_GOOD_EPOCH FROM SYSTEM;
CURRENT_EPOCH | AHM_EPOCH | LAST_GOOD_EPOCH
---------------+-----------+-----------------
1348 | 1347 | 1347
(1 row)
-- 插入新数据
=> INSERT INTO test_epochs VALUES (1);
OUTPUT
--------
1
(1 row)
-- INSERT 未提交,epoch 列为空
=> SELECT epoch, * FROM test_epochs;
epoch | c1
-------+----
| 1
(1 row)
=> COMMIT;
COMMIT
-- COMMIT 后,该行记录当前 epoch 值
=> SELECT epoch, * FROM test_epochs;
epoch | c1
-------+----
1348 | 1
(1 row)
-- CE 因 COMMIT 而推进
=> SELECT CURRENT_EPOCH, AHM_EPOCH, LAST_GOOD_EPOCH FROM SYSTEM;
CURRENT_EPOCH | AHM_EPOCH | LAST_GOOD_EPOCH
---------------+-----------+-----------------
1349 | 1347 | 1347
(1 row)
-- Vertica 中 UPDATE = DELETE + INSERT
=> UPDATE test_epochs SET c1 = 2 WHERE c1 = 1;
OUTPUT
--------
1
(1 row)
-- 未提交,epoch 列为空
=> SELECT epoch, * FROM test_epochs;
epoch | c1
-------+----
| 2
(1 row)
=> COMMIT;
COMMIT
-- COMMIT 后 epoch 变为当前值
=> SELECT epoch, * FROM test_epochs;
epoch | c1
-------+----
1349 | 2
(1 row)
=> SELECT CURRENT_EPOCH, AHM_EPOCH, LAST_GOOD_EPOCH FROM SYSTEM;
CURRENT_EPOCH | AHM_EPOCH | LAST_GOOD_EPOCH
---------------+-----------+-----------------
1350 | 1348 | 1348
(1 row)
要点:
- 执行 SELECT 时,Vertica 在 latest epoch 获取数据,同时也会检索同一事务中未提交的数据
- INSERT/COPY 加载数据时,每行都有一个表示提交时间的 epoch 值
- DELETE 时,Vertica 创建存储 epoch 的 delete vector,记录 ROS 容器中标记为删除的位置
- 早于 AHM 的被删除数据可以被物理清除
故障排查¶
LGE 不推进¶
LGE 推进的正常状态:Tuple Mover 将数据从 WOS 移到 ROS 时,LGE 推进。
=> SELECT CURRENT_EPOCH, LAST_GOOD_EPOCH FROM SYSTEM;
CURRENT_EPOCH | LAST_GOOD_EPOCH
---------------+-----------------
630384 | 620380
如果 LGE 不推进,按以下步骤排查:
1. 检查 WOS 中是否有数据:
2. 如果有 WOS 数据,强制 moveout:
3. Tuple Mover moveout 失败的常见原因:
| 原因 | 说明 |
|---|---|
| 无法获取 T-lock | Tuple Mover 无法获取表的 T 锁 |
| ROS pushback | ROS 容器数达到最大值,moveout 无法创建新容器 |
| WOS 分区数过多 | WOS 中有超过 1024 个分区 |
查看 vertica.log 排查 Tuple Mover 失败原因:
4. Replay delete 会拖慢 Tuple Mover:
AHM 不推进¶
场景 1:存在未刷新的 Projection
找到未刷新的 projection:
此时尝试手动推进 AHM 会报错:
=> SELECT MAKE_AHM_NOW();
ERROR 2154: AHM must lag behind the create epoch of unrefreshed
projection public.test_2_b0 (Create epoch: 1372)
解决方案:
- 常规做法:导出 DDL → 刷新 projection 或
REFRESH - 如果是大表 projection 刷新太慢(会使 AHM 长期被卡住),可采用变通方案:先 drop projection,推进 AHM,再重建:
-- 导出 DDL 以便后续重建
=> SELECT export_objects('','test_epochs');
...
-- 删除 projection
=> DROP PROJECTION test_2;
DROP PROJECTION
-- 推进 AHM
=> SELECT MAKE_AHM_NOW();
MAKE_AHM_NOW
-------------------------------
AHM set (New AHM Epoch: 1393)
(1 row)
场景 2:集群中有节点不在 UP 状态
⚠️ 警告: 在有 DOWN 节点的情况下,手动执行
MAKE_AHM_NOW(true)、SET_AHM_EPOCH或SET_AHM_TIME将 AHM 强行推进超过该节点的 LGE,将导致该节点重启后必须从头恢复。
Epoch 管理函数¶
| 函数 | 说明 |
|---|---|
GET_AHM_EPOCH() |
返回 AHM 所在 epoch 编号 |
GET_AHM_TIME() |
返回 AHM 对应的时间戳 |
GET_CURRENT_EPOCH() |
返回当前 epoch 编号(COPY/INSERT/UPDATE/DELETE 写入数据的 epoch) |
GET_LAST_GOOD_EPOCH() |
返回 LGE 编号(手动恢复可恢复到的最新 epoch) |
GET_EXPECTED_RECOVERY_EPOCH() |
返回各节点的恢复 epoch 及集群 LGE 计算过程 |
SET_AHM_EPOCH(epoch) |
将 AHM 设为指定 epoch(通常仅用于测试) |
SET_AHM_TIME(time) |
将 AHM 设为指定时间点 |
MAKE_AHM_NOW([force]) |
将 AHM 设为最大允许值(force=true 忽略 DOWN 节点警告) |
ADVANCE_EPOCH(n) |
手动将 epoch 推进 n 个(向后兼容用) |
Epoch 配置参数¶
| 参数 | 默认值 | 说明 |
|---|---|---|
AdvanceAHMInterval |
180 秒 |
AHM 推进检查间隔 |
EpochMapInterval |
180 秒 |
epoch→时间映射精度,影响历史查询和 LGE 报告。必须 ≥ AdvanceAHMInterval |
HistoryRetentionTime |
0 |
已删除数据保留时长(秒)。推荐用于控制 purge 策略 |
HistoryRetentionEpochs |
-1 |
保留的历史 epoch 数量;-1 = 禁用 |
AHMBackupManagement |
0 |
1 时 AHM 不超过最近一次全量备份的 epoch |
扩展阅读¶
- K-Safety 最佳实践 — K-safety 与节点依赖
- Vertica 维护前准备 Checklist — 推进 AHM 是维护前的重要步骤
- Vertica 数据库 Replay Delete 算法 — replay delete 与 AHM 的关系