等待数据变更完成
ALTER TABLE mutation 返回时,数据变更可能还未完成。应用修改事件名称后立即查询,可能仍读到旧值。
操作示例
CREATE TABLE event_log (id String, event_name String)
ENGINE = MergeTree ORDER BY id;
INSERT INTO event_log VALUES ('E1', 'login');
jdbcTemplate.executeUpdate(
"ALTER TABLE event_log UPDATE event_name = ? WHERE id = ? SETTINGS mutations_sync = 0",
new Object[] { "archived", "E1" });
这里明确使用异步执行。调用成功说明命令已接受,不表示匹配的数据已经全部修改。
等待完成后再读取
如果下一步需要读取当前服务节点上的新值,在提交时使用 mutations_sync = 1:
jdbcTemplate.executeUpdate(
"ALTER TABLE event_log UPDATE event_name = ? WHERE id = ? SETTINGS mutations_sync = 1",
new Object[] { "archived", "E1" });
String name = jdbcTemplate.queryForObject(
"SELECT event_name FROM event_log WHERE id = ?",
new Object[] { "E1" }, String.class);
成功返回后,name 为 archived。此例使用单节点 MergeTree 表;副本等待设置见mutation 同步方式。
修改与删除的返回值
修改或删除成功,不代表 JDBC 返回值就是实际命中条数。例如,将上述条件改为 WHERE event_name = 'login',即使命中多条记录,也不能把 executeUpdate() 的返回值当作修改数量;返回值同样不能可靠判断是否没有匹配记录。
构造器 API 和 BaseMapper 也沿用驱动的返回值。需要确认数据是否改变时,应等待操作完成后查询目标记录;不要用 返回值 == 1 作为单条修改成功的业务判断。
查看进度与失败原因
异步执行时,通过 mutation 记录检查进度,不要用更新计数判断是否完成:
List<Map<String, Object>> tasks = jdbcTemplate.queryForList("""
SELECT mutation_id, command, is_done, parts_to_do, latest_fail_reason
FROM system.mutations
WHERE database = currentDatabase() AND table = ?
ORDER BY create_time DESC, mutation_id DESC
""", new Object[] { "event_log" });
is_done 表示是否完成,parts_to_do 表示剩余分片,latest_fail_reason 给出最近失败原因。按命令和 mutation ID 确认目标任务,不要把其它写入者提交的最新记录当成本次操作。见 system.mutations。
等待完成不等于把多条 mutation 变成一个事务。调用超时后先检查任务状态,不要直接重复提交。
命令异常
无效命令会向调用方报告异常,但 MergeTree 的排序键和主键不强制唯一,重复 ID 不会因此产生主键冲突错误。批量调用同样不能依赖重复主键异常判断是否出现重复记录,处理方式见重复记录处理。
事务支持
事务调用方式、传播及隔离级别的限制见事务支持。