数据库时间机器
v0.7 · Apache 2.0

任何一天、任何一张表,
都能原样找回来

跨库表级历史快照回溯 · 防误操作 · 单机部署,不出内网

已支持 Oracle SQL Server PostgreSQL MySQL
元库 dtm_meta.db · schema V12 · 仅本机访问

登录

账号由管理员在「用户」里创建;没有账号就找你的 DTM 管理员开一个。

口令由控制端校验(PBKDF2-SHA256,60 万次迭代),不是本地比对。 第一个管理员由 dtm-control admin init 建出,之后在「用户」里增删。 连续失败不会提示「用户名不存在」还是「口令错」——两种情况回同一句话。
监视/概览
静态原型 · 数据为模拟

概览

先看程序有没有在跑,再看表在不在保护下

实例

点一行展开该实例每天的空间增长
成功 无变更 进行中 未定 未采集 失败
实例采集端项目运行状态表占用空间 最近 7 天保留历史

受保护的表

一表一条策略(policy)。策略只由显式登记产生,采集路径不会自动建策略

重置
表实例切片方式调度 行数体积 可回溯至最近采集状态
切片方式
'' = 行级增量(主键序) rowid = 块级覆盖(ROWID 序,超大无索引表)
口径提醒
rowid 策略不允许朴素全量重建(会扫全表),界面直接禁用该入口

SYSTEM.CUSTOMERS

local-oracle · 策略 #1 · 每日 03:00 · 行级增量

快照链

点击任意时间点查看该时刻的表状态
全量快照 增量快照 空采集(跑过、无变化) 生产库当前
点某天的快照回放该时刻;深蓝块 = 当天落过全量快照 · 虚线灰「无变化」= 当天采集跑过、表没有变化(没有落文件)

该时间点的表状态

操作

一个看该时刻整表数据,一个看该时刻这张表的结构版本与列级变更。两者都只读本地 Parquet 与元库,不碰生产库。要写回生产库请到「数据直查」页的恢复入口。

数据直查

查的是本地快照里的那一个时间点,不是生产库当前数据

只读历史快照:查询走本地 DuckDB 读 Parquet,不连生产库、不产生任何写操作。行状态列取自 delta 的 DTM_OP。
命中 — 行 耗时 — 扫描 — 行 / —
每页
1 / 5009

操作审计

控制端自己做过什么:登录、改账号、改权限、改设置、动项目成员。只记写操作, 纯浏览不落 —— 审计要回答「谁改了什么」,不是操作回放

重置
时间 操作人 动作 对象 项目 详情

数据恢复日志

每次从快照恢复落一条(restore_log):谁、从哪个时间点、恢复到哪张表、带什么筛选、成没成。只增不改,快照过期清理也不删

重置
恢复时刻 操作人 实例 / 采集端 项目 源表 快照时间点 目标表 筛选条件 行数 耗时 状态
几条口径
① 只有配了恢复通道(instance.recover)的实例才会产生记录 —— 现在只有 local-oracle(ADG 备库采集 → 主库写回)和 oa-sqlserver(同机写回)能恢复,其余实例在向导里就该被拦下;
② 恢复产物永远是一张新表(*_dtm_* 前缀),工具不碰原表:临时表要不要回写、怎么回写由你们 DBA 决定,这段工具既不做也不记录 —— 它没有手段观测原表是否被改;
③ 失败记录也留(含失败原因),只增不改;行数取「该时间点按主键归并后的行数」,不是写入的最大序号。

数据查询日志

谁在哪个快照上查过什么、返回多少行、有没有导出去(query_log)。只增不改,翻页不重复记

重置
查询时刻 操作人 实例 / 采集端 项目 表 快照时间点 查询条件 返回 / 扫描 耗时 导出 状态
几条口径
① 一次「执行」、一次「导出」各落一条;翻页、改列、切筛选不落 —— 审计要回答的是「谁取走了哪些数据」,不是操作回放;
② 被拦下的尝试也落一条(状态 = 被拒),记在进入查询之前 —— 「谁试过」和「谁成功」一样重要,失败不该只留在前端 toast 里;
③ 日志只存 SQL 文本、返回行数、扫描行数与耗时,不存任何数据值;快照超出保留期被清理后日志仍在,此时它是一条不含业务数据的线索,而不是第二份客户数据;
④ 记的是本地快照上的查询:语句里的 dtm_merge(…) 表示 base + delta 按主键归并出的当前态,不连生产库、不产生任何写操作。

结构变更

每次快照落一个结构版本,历史数据与它当时的 schema 绑定;结构一变即触发重建基线

结构时间线

SYSTEM.CUSTOMERS
选 1 个看结构 · 选 2 个对比

表结构

运行台账

一次命令调用一行(run_log)。与审计日志分工不同:这里回答「按点跑了吗、跑了多久、成没成」

—
启动时刻命令目标状态耗时备注
两个已知缺口(界面如实呈现,不粉饰)
① 进程被 kill / 机器重启时该行永远停在 running,靠 ended_at IS NULL 判读,无法自动区分「还在跑」与「已经死了」;
② 元库打不开时这次运行记不进来,只能靠 stderr。

采集端

常驻进程,配好 control_url 与 collector_key 后主动连控制台,连上就出现在这里

在线
0/ 0
—
承载实例
0个
每个实例只归一台采集端

已接入

主机、版本由采集端自报;IP 是控制端从连接上看到的,界面不可编辑
采集端主机 / IP运行环境
承载实例 实例策略容量状态
状态 / 心跳

如何接入采集端

签发即建条目,连上才算接入
① 签发长期密钥(右上「接入采集端」,只显示一次)→ ② 把 control_url / collector_key / collector_name 写进采集端的 dtm.conf → ③ 启动采集端(dtm agent),连上后状态转「在线」,主机、IP、版本也随之填入。
刚签发的显示「掉线(从未)」是正常的:一次都没连上,不是故障。
连上后心跳每 10 秒一次,超 90 秒转黄「心跳超时」、超 10 分钟转红「掉线」。 掉线有两种:网络故障(采集照常,控制台看不见),或采集端异常关闭(采集停)。

组织

公司 → 分公司 → 部门 → 组,一棵树。层级只决定审批找谁、审计怎么汇总, 不决定能看哪台库——那是「项目 + 项目内角色」的事。 直属与含下级是两个数:树里显示含下级,详情里两个都给

组织树

—

组织

项目

数据划分的最小单位:一个生产系统一个项目,一台实例只属于一个项目。也是授权的默认作用域

清单来自控制端元库;新建 / 改名 / 换所属组 / 停用都直接落库。项目不提供删除——实例与审计还引用它

项目清单

实例与采集端的对应关系是实时算的,不存在项目行里;点行看明细
项目 所属组 备注 承载实例 操作

为什么是「一个实例一个组」

多对多会让树出现重复,先把边界钉死
实例是策略的父级,「项目」如果允许多归属,按项目看树时同一个实例会出现在多个组里, 实例数、掉线数全部重复计入,恢复通道也会出现多份——“这个组到底有几个库”就再也答不准了。 所以一个实例只贴一个组。
真遇到一个库被两个项目共用:建两个实例(不同账号 / schema),各自归组。 代价是元库里有两行连接信息;换来的是一条实例只对应一条 base+delta 链,谁都不会数错。
删除保护:项目下还有实例时禁止删除,并点名是哪几个——否则那些实例会掉进「未归属」, 而「未归属」是最容易被忽略的角落。

实例

口令由采集端加密存库(AES-256-GCM),界面不回显;采集账号要求只读

每个实例一条恢复通道:采集走只读账号,写权限只配在右侧那条通道里。Oracle 在 ADG 备库采集时,恢复通道必须指向主库。

两侧权限边界

采集端与被采集库分 OS 实例
采集侧:只读账号 + 网络 ACL,只允许纯 SELECT,不加列、不建表、不开开关。
恢复侧:主库 + 受限写账号,权限仅 CREATE TABLE 与对 *_dtm_* 前缀表的 INSERT; 只写新临时表,不碰原表。

角色

角色是权限点模板,分三类:管理(改配置、改权限,跨项目生效)、数据(能碰哪些库/表,落在你参与的项目里)、审计(能看谁做了什么,按项目裁剪)。 一个项目里一个人只挂一个角色,所以要「管理员 + 审计」就建一个组合角色,不要挂两行

角色与权限点来自控制端元库;点编辑可改名字、说明与权限点,保存后对该角色的全部成员立即生效

角色

改一次角色授权,底下所有成员同时生效
角色 说明 功能授权 实例授权 审计授权 成员 操作
几条口径
① 权限分两个维度:实例授权(逐实例勾 5 个动作 + 可选的表可见范围,管的是数据面)+ 功能授权(按所属菜单分三组:配置 / 权限 / 审计,全站生效,管的是管理面);
② 5 个动作各管一段:查看备份情况=受保护的表 / 时间线 / 快照链;查看表结构=结构变更页;查看快照数据=数据直查的行数据;导出=落成 Parquet / CSV;数据恢复=恢复向导走写通道(且实例本身配了恢复通道才真的能恢复);
③ 动作有依赖:勾下位自动补上位(勾导出会把看数据、看备份一起勾上),关上位连带关下位 —— 矩阵里不会出现「能导出但不能看数据」这种自相矛盾;
④ 表可见范围是实例内的再收窄:默认全部表可见;收窄后受保护的表、时间线、数据直查、结构变更都只显示范围内的表 —— 这是显示范围,不改采集、不碰数据库;
⑤ 超级管理员是内置角色:两个维度都固定全通,不可删、不可改小,只可增减成员 —— 它仍然是一个角色,不走第二条判定路径;
⑥ 审计三项(查看恢复日志 / 查看查询日志 / 导出审计日志)单独一组:日志跨全部实例,所以不受实例授权约束,也不因「能看某个库」而自动获得;看与导出分开,导出是一次性把全量明细带走,通常只给审计岗;
⑦ 粒度到实例:要给某个部门更小的范围,用「表可见范围」或把实例拆细注册,而不是指望工具按行过滤。

用户

账号只挂一个平台角色(跨项目的那种);项目里的角色在项目里分。停用立即生效并踢掉它的登录,但不删记录—— 审计里还要能查到这个人做过什么。系统始终保留至少一个启用的超级管理员

用户

admin 是安装时生成的引导账号,首登须改密码
用户 姓名 角色 部门 参与项目 状态 最近登录 操作
几条口径
① admin 是引导账号:程序初始化时生成,默认属于超级管理员角色,初始密码随机、首次登录强制修改;
② admin 可以被停用或降级,也不要求它是某个具体的人 —— 前提是还有另一个启用的超管;
③ 不变式:至少保留一个「启用 + 超管角色」的用户。停用或降级最后一个超管会被直接拒绝,这是唯一能把所有人关在门外的情况;
④ 多人需要管理权限时按人建号、各自授予超管角色(审计才分得清谁干的);用户只停用不删除,留痕;
⑤ 一个用户一个角色 —— 要叠加权限就改角色本身或另建复合角色。

系统设置

这里只放运行时可改、改完立刻生效的策略。端口、元库路径、采集端密钥属于部署事实—— 它们只显示、不可改,因为改了要重启,而重启时这个页面本身就不在了

安全

影响所有账号的登录与会话

审计与留存

审计表是全系统唯一会无上限增长的表

采集端接入

防「一份配置被复制到第二台机器」

运行状态

只读 · 判断"我连的是哪个控制端"用这块