syncdb

单文件 MySQL / MongoDB 数据同步工作台。Go 服务内嵌 React + Ant Design 控制台,运行时无需 Node.js、独立前端服务或 CDN;界面支持中文、英文、日文。

启动

Windows 安装 Go 1.26 或以上版本后,在项目根目录双击 start-dev.cmd,或运行:

.\start-dev.cmd

脚本先编译 dist/syncdb.exe,成功后启动;编译失败不会启动旧程序。build-and-run.cmd 也可用于打包启动。再次编译前请停止旧实例,脚本不强行结束已有进程。

直接双击 dist/syncdb.exe 也能运行。所有启动入口在未设置 SYNCDB_ADDR、未通过 YAML 覆盖地址时都监听所有网络接口的 :0,由系统动态分配可用端口。Windows 自动打开实际端口的浏览器并完成登录;控制台打印访问地址,按 Ctrl+C 停止服务。

启动时控制台先打印 SYNCDB 字符 Logo,再输出日志;图标以双数据库和双向同步箭头组成,使用纯 ASCII,兼容 Windows 控制台。

控制台日志带时间戳和 [syncdb] 前缀,展示启动阶段、程序与工作目录、实际数据目录、恢复记录数量、并发配置、监听地址、启动耗时、浏览器启动结果,以及关闭和数据保存结果。HTTP 请求日志已关闭,不逐条打印请求信息。日志只显示令牌文件位置,不输出令牌或数据库密码。

Linux/macOS 构建和运行:

go build -trimpath -ldflags="-s -w" -o dist/syncdb ./cmd/syncdb
./dist/syncdb

登录与局域网访问

  • 首次启动会在数据目录生成 session-token。其他设备使用运行机器的局域网 IP 和控制台显示的端口访问,输入这个文件中的令牌。
  • Windows 自动打开页面时通过 URL fragment 传递令牌,前端立即移除 fragment 并换取 HttpOnly 会话 Cookie;令牌不写入 localStorage。
  • 本机通过 127.0.0.1、localhost 或 [::1] 访问无需令牌,直接进入工作台;同时检查连接来源与访问地址。局域网访问的业务 API、SSE 和导出下载仍需认证。API 客户端可发送 Authorization: Bearer <token>;健康检查公开。
  • Cookie 有效期 12 小时。退出登录会清除当前浏览器 Cookie,不会撤销已复制的 Cookie;更换访问令牌并重启可使旧 Cookie 失效。
  • HTTP 不加密浏览器传输的令牌、密码和数据。局域网也需可信,远程部署请配置 HTTPS。数据库 TLS 不保护浏览器到 Web 服务的连接。

配置

.\start-dev.cmd --config config.example.yaml
# 或运行已编译程序
.\dist\syncdb.exe --config config.example.yaml

start-dev.cmd 未收到 SYNCDB_ADDR 时会设置为 :0;若要使用 YAML 中指定的地址,可直接运行 EXE 或 build-and-run.cmd。环境变量优先于 YAML。下表的“默认值”区分直接运行 EXE 与开发脚本:

环境变量 默认值 作用
SYNCDB_ADDR :0 Web 监听地址,0 表示系统分配端口
SYNCDB_DATA_DIR 用户目录下的 .syncdb 元数据、密钥、令牌和导出目录;显式配置相对路径时相对可执行文件所在目录
SYNCDB_WORKERS 2 同步/导出共享并发上限,1–16
SYNCDB_EXPORT_RETENTION_HOURS 24 导出文件保留时长,1–8760 小时
SYNCDB_ENABLE_HIGH_RISK_OPERATIONS false 是否开放复制初始化和计划内切换;修改后需重启
SYNCDB_TOKEN 自动生成 自定义访问令牌,至少 24 字符;设置后不使用令牌文件
SYNCDB_OPEN_BROWSER true 设为 false 禁止 Windows 自动打开浏览器

YAML 支持 server.addr、server.data_dir、server.workers、export.retention_hours,未知字段会报错。参考 config.example.yaml;示例中的 server.addr: ":0" 是显式选择动态端口,不代表直接运行 EXE 的默认值。Docker 或桥接端口映射部署时应显式指定 SYNCDB_ADDR 和对应的发布端口。

显式指定数据目录时,相对路径(包括 YAML 与环境变量配置)统一相对可执行文件目录解析,不随启动工作目录变化;绝对路径按指定位置使用。未指定时使用用户目录下的 .syncdb。启动日志显示实际数据目录。--config 文件路径仍按命令行工作目录解析。

默认数据目录固定为用户目录下的 .syncdb,Windows 示例为 C:\Users\zoe\.syncdb,移动程序或使用 go run 不会改变默认数据位置。升级前若使用旧数据目录,请停止旧实例,将整个旧目录复制到用户目录下的 .syncdb,同时保留 store.json、store.key、session-token、exports 和 SQLite 文件,不要覆盖另一套已有数据。仍可使用 SYNCDB_DATA_DIR 或 YAML 显式指定其他目录。

Docker 镜像显式设置 SYNCDB_DATA_DIR=/data,可将持久卷挂载到 /data。

功能与操作

数据源及对象浏览

  • MySQL / MongoDB 均支持创建、编辑、删除、真实连接测试、对象列表及结构检查。
  • 输入主机或 主机:端口;数据库端口默认 MySQL 3306、MongoDB 27017,与 Web 服务端口无关。带端口的 IPv6 写作 [地址]:端口。
  • 用户名、密码分别填写,不在地址中嵌入凭据。默认数据库可留空,保存和测试连接无需选库;一个数据源可连接并浏览多个数据库。MongoDB 可配置认证库;UI 默认 admin,API 未指定时使用数据源默认库,无默认库时使用 admin。操作中选择其他数据库不会更改认证库。不启用认证的 MongoDB 可留空用户名和密码。
  • 同步任务、SQL 导出和结构对比各自指定操作数据库,已有默认库会自动填入;任务及导出保存具体库名,后续修改数据源默认库不会改变其范围。对象浏览可切换数据库,Binlog、慢 SQL 和原生主从复制继续使用各自的范围设置。
  • 支持关闭 TLS 或开启并验证证书;不跳过证书校验,不自动降级。TLS 关闭仅用于可信开发网络。
  • 密码从不随创建、编辑、列表、详情或 SSE 返回;编辑时密码留空保留原值。API 更新省略 password 保留原值,显式传空字符串可清空。
  • MySQL 展示列、主键、索引、外键及建表语句;MongoDB 展示集合选项、索引和采样推断字段。MongoDB 推断结构不是强制 Schema。
  • MySQL 数据库/表名下拉、对象列表、结构检查和写入结构缓存在 information_schema 元数据不可访问时回退到 SHOW 命令。名称下拉最多返回 512 项并报告截断,表名查询明确限定所选数据库且只返回普通表。账号仍需相应 SHOW/读取权限;索引信息不可读取时明确失败,避免误判唯一键。回退检查保留完整建表 SQL,外键关系无法单独列出时页面会提示查看 SQL。
  • 被任务/导出引用的数据源不能删除,正在使用的数据源不能编辑。

测试连接、加载库表名称或启动 Binlog 采集返回 502 且错误码为 mysqlAuthFailed 时,表示 MySQL 拒绝数据源账号登录。请编辑对应数据源,核对用户名、密码及 MySQL 账号允许的客户端 host;如需修正密码,必须重新填写后保存,编辑时留空会保留旧密码。Binlog 的库表筛选不会更换连接账号。mysqlDatabaseDenied 表示数据库授权不足,binlogPermissionDenied 表示日志读取权限不足,需分别处理。页面登录返回 invalidToken 才需要检查 syncdb 访问令牌。

同步任务

  1. 添加源和目标数据库,测试连接。
  2. 浏览源/目标对象,必要时先使用结构对比和安全结构计划创建目标表/集合。目标对象必须存在。
  3. 创建任务,选择对象、冲突策略、过滤条件等。默认 dry-run,先预演检查。
  4. 确认配置后关闭 dry-run,在运行对话框明确确认写入。
  5. 执行记录通过 SSE 实时更新,连接失败时退回轮询;可取消运行中的任务。遇到耗时大表时,可在执行记录点击“跳过当前表”,中断该表读写并继续下一张表;已经写入的数据不回滚,跳过表名保存在执行记录中。跳过增量表不会推进水位,下次运行仍会读取该表。

支持 MySQL→MySQL、MongoDB→MongoDB,以及通过字段映射/转换实现的跨类型同步。支持:

  • 全量、带条件范围读取,以及用户配置水位的字段增量读取。
  • 同步分为表结构和数据两个阶段。结构并发数 schema_workers 默认为 4,可设置为 1–16;所有结构处理结束后才进入数据阶段,数据按任务对象顺序执行。外键表在普通表之后按依赖尝试创建,结构失败的表保留重试入口。表同步状态页显示两个阶段的逐表结果、结构并发进度和失败数量。
  • 网页实时更新仅在状态变化时推送完整快照,不变时每 15 秒发送轻量心跳;快照列表顺序固定。前端忽略不变快照,不重复渲染列表,心跳不触发页面刷新。隐藏标签页暂停实时连接和全局轮询,重新显示时恢复并读取最新状态,减少空闲浏览器 CPU 占用。
  • MySQL→MySQL 在结构阶段核对映射、默认值和忽略规则所需的目标字段。缺字段的表标记结构失败并跳过数据读取,其他表继续;修复目标结构后可单独重试。自动克隆源表结构也会先检查是否能接收转换后的字段。字段值的类型转换及数据库约束仍在写入时验证。
  • 插入、upsert 和 replace 策略还会检查目标非空、无默认值且非自增/生成的必填列是否有对应源字段、映射或任务默认值;缺失时在结构阶段失败,避免先读取大表。update 只更新已有记录,因此允许省略目标额外必填列。对象结构页展示“插入必填”标记。
  • 源列名含点号时,转换或脱敏可能保留原列并产生嵌套根字段。结构校验按“映射、默认值、转换、忽略、脱敏”的实际顺序检查这些目标字段;忽略嵌套根字段之后执行脱敏,仍可能重新产生该字段。
  • insert/fail:插入,冲突失败;skip:插入,重复键跳过;update:只更新已有记录;upsert:更新或插入;replace:替换匹配记录内容,不使用删除再插入。
  • MySQL 主键匹配的 upsert 可批量写入;存在其他唯一索引时,整数主键批次在短事务内锁定主键,再分别更新已有记录和插入缺失记录,避免通过其他唯一索引更新错行。其他情况仍使用逐行写入。兼容未转发 CLIENT_FOUND_ROWS 的代理,无变化的更新会核实匹配记录后计为成功。
  • MySQL 全量同步可对单列整数键使用 JSON 聚合批量读取,并用键范围分页保留整数、二进制及时间值;只读代理拒绝首批 JSON 查询时自动回退到普通 SELECT。聚合结果超限时缩小尚未交付的页,核对返回行数后才交付,不重放此前批次。普通同步不持有跨表一致性快照,源库持续新增、修改或删除数据时,结束后的源目标差异需结合读取时间核验;upsert 保留目标独有记录。
  • 批次 1–1000 条、任务总上限 1–1,000,000 条;max_rows: -1 表示读取全部数据。超时 1–86400 秒分别应用于每张表,数据阶段读取前的表结构检查也计入该表超时,并支持“跳过当前表”;单表超时记录失败并继续后续表。不会用一个长事务包裹整个任务。
  • 执行记录的“读取”统计源端已交付的记录,包含转换失败及部分写入的批次;“写入”和“跳过”统计目标处理结果。失败批次同样消耗任务读取上限,后续表仅使用剩余额度,避免失败后超额扫描。重试继续累计原记录的读取和写入数量。
  • 表同步状态页显示每张表最近一次实际执行的读取、写入和跳过数量,支持按数量排序;未执行的重试表保留之前的数量,重启后仍可查看。旧记录未保存的逐表数量显示为“—”,不推算为零。长表名省略显示,可悬停查看和复制完整名称。
  • 原生列表排序直接比较十进制文本,避免大整数或高精度小数转为浮点数后被判为相等;此规则保留输入文本的精度,不恢复接口或调用方已舍入的数值。实时刷新保留用户选定的排序。
  • 最多 3 次有限重试,仅用于允许重试的策略及尚未尝试写入的临时连接失败。部分写入或结果不确定时停止,不盲目重放整批。
  • 配置至少 60 秒的运行间隔可定时执行;同一目标数据库的同步任务不并发写入。
  • 任务编辑、删除及取消;运行中不允许编辑/删除。生产环境数据源禁止作为同步目标。
  • 表同步状态支持单表和批量重试,结果更新原执行记录;批量中单表失败后继续处理其他表,重试不推进增量水位。执行结束回调重复触发时不会重复累计重试数量。
  • 重试入口按当前任务对象顺序执行所选表,已从任务中移除的历史失败表仍可重试,并按提交顺序追加到末尾;不再按表大小排序。提交前重新校验实际重试对象和目标名称,增量模式不接受多表重试,校验失败不改变执行状态。
  • 后端不会因旧跳过列表而接受已成功、运行中或重试中的表;等待中的执行/任务及同一任务的活动执行也会阻止重试。重复或并发提交只会启动一次重试,不重复写入该表。
  • 重试需先完成原生界面的确认;确认组件不可用时显示错误,不直接提交。请求失败后保留所选表并解除按钮加载状态,便于修复问题后再次提交。
  • 单表和批量重试携带确认时的任务 revision,后端在启动前原子校验版本。配置、运行状态或增量水位已改变时拒绝旧请求,不修改原执行结果;不携带版本的旧 API 调用仍兼容。退出登录期间不提交重试,切换页面或会话后忽略迟到响应。
  • 批量重试期间,表状态、成功/失败数量和状态筛选实时显示当前重试的逐表结果;等待或正在处理的表显示重试中。失败详情查询当前尝试的目标表和 SQL 审计,不使用此前尝试的旧错误。整批结束后仍更新原执行记录,不新增可见记录。
  • 执行记录及概览中的累计读取、写入、跳过数量实时包含当前重试,已成功的重试表从待补同步数量中移除。重试未结束时不显示历史结束时间;结束后显示合并后的原记录数量,不重复累加内部重试记录。
  • 重试进入数据阶段后,当前表行和原执行记录行均可跳过该表,操作绑定重试执行编号。确认期间表名或执行状态改变时不提交;结构阶段和等待状态不显示跳过入口。
  • 顶部与表行的跳过入口共享当前执行的提交状态,确认或请求未结束时不重复提交;取消和请求失败后允许再次操作。页面或登录会话改变后,旧请求的返回结果不覆盖新页面提示。
  • 任务启动、编辑、删除和重试使用相同的活动检查:任务或关联执行处于运行中、等待中时拒绝修改,即使任务摘要状态尚未更新。界面同步禁用对应操作,只有实际运行中的任务才提供停止操作。
  • 数据源编辑和删除同样识别等待任务及关联活动执行;界面提示占用或引用原因,已打开的编辑窗口保存前会重新检查。已结束任务或恢复记录的目标数据源仍属于引用,不能直接删除。
  • 批量选择跨搜索、状态筛选和翻页保留,筛选外的已选数量单独提示;“选择筛选内可重试表”只增加当前筛选内的表。实时更新后,成功或正在重试的表会自动移出选择,不会在再次失败时意外恢复选中。
  • 普通执行和重试后的原记录按全部表结果汇总:全部成功为“完成”,全部表实际失败为“失败”,成功与失败混合、主动跳过或达到行数上限为“部分完成”;服务重启恢复使用相同规则,并保留未完成表的失败原因及重试入口。
  • 重试结束后任务状态与原执行记录的全部表汇总一致,不会因所选表成功而掩盖其他失败表;内部重试记录仍保留本次尝试的独立结果。重启时恢复中断任务的执行结果,已编辑的草稿和已结束任务不受历史记录影响。
  • 执行记录保存当次使用的源表到目标表名称映射。失败详情按实际目标表定位写入 SQL;后续编辑任务或重启不会改变历史执行的目标名称。重试仅更新已尝试表的映射;旧记录缺少映射时使用当前任务配置兼容查询。
  • 原执行记录同时保存每张表最近一次实际重试的执行编号;内部重试历史被清理后,失败详情仍按该编号查询独立 SQL 审计,避免误显示更早的失败 SQL。未执行的表保留此前编号,旧记录仍使用历史重试信息兼容查询。
  • 结构阶段自动创建 MySQL 表或 MongoDB 集合会生成 SCHEMA 审计,记录实际建表 SQL/创建命令、目标对象、执行结果及安全错误码,并关联任务和执行记录。仅检查失败、结构不兼容或 dry-run 未执行创建时不会虚构写入审计。
  • 表失败详情只展示失败的 SQL 审计。长语句若被审计存储截断,会明确显示截断提示及“复制 SQL 片段”;完整语句需要从数据库日志或原始结构核对,页面不会把 dry-run 语句当作失败 SQL。
  • 已打开的失败详情会随新失败结果刷新;表成功或记录被删除后关闭并取消查询,旧请求返回不会重新打开详情。失败信息未变化和切换语言时不重复查询 SQL。
  • 取消或提前失败时,正在处理的结构/数据状态会收尾为失败;尚未执行的表保留“未执行”状态并立即开放重试,无需重启服务。取消重试时同样更新原记录,保留此前成功表及增量水位。
  • 表同步状态页可直接停止结构同步或数据同步,包括当前重试。停止确认固定点击时的执行编号,并在提交前重新检查;接口也校验编号,避免确认期间结束的旧执行误取消新执行。切换页面或登录会话后,停止请求的迟到结果不会触发提示或页面刷新。等待中的任务或同一任务的活动执行会禁用重试入口。
  • 数据阶段每张表结束后立即释放取消上下文和跳过句柄;任务只保留当前表的异常清理,不积累已完成表的延迟清理函数。适配器异常退出时同样清理当前表,保留此前完成的写入与状态。
  • 结构阶段在源端检查和目标端准备返回后再次检查超时/取消,即使适配器迟到返回成功也不会将过期操作记为成功。源端检查已过期时不再调用目标端;超时表跳过数据阶段,其他表继续。
  • 删除已结束的执行记录会同时清理其内部重试记录,保留独立 SQL 审计。存在运行中或等待中的关联重试时禁止删除;历史清理保留活动重试所需的原执行记录。
  • 已删除记录的表状态页显示明确提示,并提供返回执行记录列表的入口;浏览器清理该记录的批量选择、分页、排序及状态筛选缓存。
  • 服务重启后,中断的重试结果会合并回原记录,包括结构状态、失败原因、逐表时间和已持久化的数量;连续重启不会重复累计。缺失内部记录的旧“重试中”状态会恢复为可单独重试的失败状态,增量水位不变。

重要边界:不能将所有批次都视为跨记录原子事务;符合条件的 MySQL 批量 upsert 使用单条语句或短事务,其他写入仍逐行执行。取消、失败或超时不会回滚此前已提交的写入;记录保留已尝试、已写入和已跳过数量。dry-run 不写目标,但不能模拟所有服务器约束或并发竞争。MySQL 确定性读取及匹配更新要求主键或完整、非空唯一索引;MongoDB 匹配键需可靠唯一索引,不使用稀疏、部分或自定义排序规则的唯一索引。

字段增量不是实时 CDC:仅支持单对象、有初始值且可比较的水位字段,使用包含边界的 gte 读取,配合 upsert/update/skip 安全重读边界。仅完整扫描且未达到行数上限的成功写入会推进水位;达到上限时保留原水位,需缩小过滤范围或提高上限。无法保证捕获水位之前迟到的数据或源记录删除。

时间水位按实际时间比较,保留小数秒精度,并识别 RFC3339 时区偏移;MongoDB 日期和 Go 时间使用同一比较规则。成功执行保存的水位在重启后继续作为包含边界的读取条件;空结果不会重置水位。大整数和十进制数比较不经过浮点转换。

初始水位必须为有效、非空的标量,不能是数组或非有限数值。MySQL 在结构阶段检查源增量字段是否存在;每批数据在写入前检查水位字段,字段缺失、空值、文档或数组均标记失败并保留重试入口。当前失败批次不写入,此前已完成的写入保留,增量水位不推进。

任务编辑页在保存前检查增量单表限制、写入策略和水位格式,错误定位到对应字段。任务接口与实时快照提供 incremental_value_json 精确文本;编辑页保留并回传原始 JSON 文本,后端精确解析后只保存实际水位,避免浏览器舍入大整数或小数后改变位点。

任务接口还提供 revision 版本标识。编辑页回传打开窗口时的版本;配置、运行状态或水位已更新时,界面或后端拒绝旧表单,避免覆盖新位点或另一窗口的修改。旧版不携带版本的 API 调用仍兼容原行为。

手动启动任务同样回传运行窗口打开时的 revision,前后端拒绝配置、运行状态或水位已变化的旧确认。后端在创建执行记录前完成版本检查,拒绝时不修改任务状态或创建执行;版本校验不替代实际写入确认。定时任务及旧版不携带版本的调用保持原启动行为。

任务编辑支持同一数据源连接下不同数据库之间的同步;同一连接、同一数据库仍禁止,生产目标仍禁止写入。范围同步须填写筛选条件;自动重试只适用于 upsert、update、skip,其他策略须将重试次数设为 0。界面会在对应字段提示这些配置错误,筛选错误会展开高级配置。

任务接口通过 filters_json 提供可编辑的精确筛选文本,任务编辑和 SQL 导出会回传原始筛选 JSON。后端优先解析该文本并保留大整数、小数的准确值,持久化实际筛选条件;不会保存第二份条件。普通 filters 字段仍兼容现有 API。任务详情同样使用精确文本显示筛选条件。

任务筛选、增量水位及 SQL 导出的 $date 在提交前检查 RFC3339 格式、实际日历日期、闰年、时间及偏移范围,不将不存在的日期自动调整为其他日期。合法值保留原始时区和小数秒文本。

$numberDecimal 在前端检查有限 Decimal128 的精度和指数范围,保留可精确表示的尾随零及科学计数法,不通过浮点数转换。前后端使用同一组边界用例验证,超出范围的输入定位到筛选或增量水位字段。

后端筛选接口同样拒绝 Decimal128 的 NaN 和无穷值,包括列表筛选。MySQL 的类型化筛选参数在绑定前规范化,Decimal128 和 JSON 大整数使用精确文本,避免结构体参数被驱动拒绝;MongoDB 仍使用原数值类型。

默认值同样通过 defaults_json 保留精确 JSON 文本,包含嵌套对象和数组;后端保存实际默认值,原始文本不单独持久化。MySQL JSON 列可接收对象、数组默认值及 JSON 转换结果,写入前序列化并保留 JSON 数值精度;结构化值不能写入其他列类型。

字段的 json 转换支持 JSON 文本数组、原生数组及 MongoDB BSON 数组。数组保留嵌套对象、空值和大整数,空数组保持为 [];可用于 MySQL JSON 列或 MongoDB 数组字段。

MySQL JSON 列中的布尔值以 true / false JSON 文本传给驱动,单行写入和批量更新均保留布尔类型;普通布尔列仍使用驱动布尔参数。

json 转换得到的字符串在 MySQL JSON 列写入时保留 JSON 引号,例如 "true" 不变成布尔值 true,空字符串保持为 ""。写入文本列或 MongoDB 时仍是普通字符串;未经转换的源端 JSON 文本按原文写入,不重复编码。

文本 null 经 json 转换后,在 MySQL JSON 列中写为 JSON null,与源端空值写成 SQL NULL 区分。普通非 JSON 列和 MongoDB 仍写为空值;脱敏保持转换后的空值,不将其散列成字符串。

重试结果回写原执行记录时,已尝试的表使用最新错误和时间信息;本次跳过或达到行数上限不会残留上一次失败原因,缺失的新时间信息不会沿用旧时间。等待中的表保留原错误、时间和数量,界面不会因排队重试暂时显示为零条。

数据源列表的同步状态优先显示关联任务的重试中、运行中或等待状态,否则显示最近一次同步任务的结果;没有关联任务时显示“未配置同步”。来源和目标均会更新,状态支持搜索、排序,点击有执行记录的状态可进入表同步状态页。该状态表示一项活动任务或最近任务,不汇总数据源全部历史结果,也不代表导出任务状态。任务列表同样优先显示活动执行记录,避免旧任务摘要仍显示成功。

列表刷新使用一次扫描得到任务活动、数据源占用和引用关系;执行列表也一次索引活动重试,避免逐行重复扫描历史记录。概览未搜索时只准备最近 8 条执行记录,搜索仍覆盖全部历史记录。

自动主从同步(MySQL 原生复制)

侧栏「自动主从同步」→「配置与管理」,支持 MySQL 8.0.23 及以上的 8.x 版本。通过 MySQL 的 syncdb_auto 命名通道持续复制新增、修改、删除和 DDL;关闭 syncdb 或浏览器不停止复制,配置与复制位置由 MySQL 自身保存。MySQL 重启后是否自动启动取决于其 skip_replica_start 配置。

  1. 准备不同实例的主库、专用从库数据源,两端 server_id 必须非零且不同,gtid_mode=ON,主库开启 log_bin 并使用 binlog_format=ROW。
  2. 在从库恢复包含 GTID 的一致性初始快照,启用 read_only 和 super_read_only。普通全量同步任务不包含一致性快照/GTID 信息,不能代替此步骤。应用检查已清理 GTID 是否被从库覆盖,以及从库是否存在主库没有的事务,但无法证明初始数据完全一致。
  3. 在主库预先创建 syncdb_ 前缀的复制账号(可使用复制账号向导),授予复制读取权限,账号 host 允许从库连接。复制密码使用 16–32 个可见 ASCII 字符,不含引号或反斜杠。数据源连接账号须能读取全局变量;从库管理账号需要 REPLICATION_SLAVE_ADMIN、REPLICATION CLIENT 及相关 Performance Schema 表的查询权限。
  4. 选择主从及「配置并启动」,填写复制账号,点击「重新检查并继续」,逐项查看连接、主从设置、通道及 GTID 检查,核对剩余 SQL 后确认初始快照并「执行剩余步骤」。主库数据源地址必须能从从库服务器访问;不能使用只对 syncdb 主机有效的本地隧道地址。若主库数据源启用 TLS,配置和恢复启动均强制 TLS 与证书验证,表单需填写从库服务器上的可信 CA 文件路径(API 字段 ssl_ca),Windows 路径使用 /;不会降级为不验证。
  5. 查看 IO/SQL 线程、延迟秒数及错误编号;对话框每 5 秒刷新一次。管理命令成功不代表线程已连接或追平。延迟未知时显示未知;错误详情请在 MySQL 侧查看。已有通道选择「启动已有通道」或「停止通道」;停止只需要从库连接,主库不可达时也可操作。

复制范围是配置时从主库数据库下拉框选择的一个数据库,通过 REPLICATE_DO_DB 过滤器排除主库其他数据库;该库的新增、修改、删除和 DDL 都会复制,不应用字段映射、脱敏或表筛选。不会删除从库已有的其他数据库(例如主库没有的 ashare)。专用空从库可使用下述流式快照初始化;当前不提供文件位点模式、重置或覆盖已有通道、多源配置及故障自动切主。标记为生产的目标仍禁止 Web 变更。

检查与执行按步骤显示通过、待执行、失败、等待或结果待核实。进度及不含密码的表单保存在加密元数据中;关闭页面或重启 syncdb 后,可在「已保存的进度」点击「恢复检查」。恢复时重新读取 MySQL 实际状态,缓存的“完成”标记不能跳过检查。

配置、IO 线程启动、SQL 线程启动分别执行和保存进度。例如通道配置成功但 IO 启动失败,修复后继续只启动缺失的线程;IO 已启动但 SQL 启动失败时只执行 SQL 线程启动。已匹配的通道不再要求输入复制密码或重复确认初始快照。超时、断连及程序中断均需重新核实,避免服务器已执行但响应丢失时重复配置。线程仍在连接时显示等待,管理 SQL 成功不等于复制已就绪。

首次配置拒绝覆盖不匹配的已配置通道或复制过滤器;恢复时检查地址、端口、账号、GTID 与 TLS 配置,启动前重新检查实例和计划指纹。同一从库的检查、计划及执行互斥,避免并发覆盖进度。复制设置不使用显式事务,逐条审计;部分失败或超时不会回滚、重置或盲目重试。密码只传给 MySQL,计划、审计及应用元数据不保存复制密码;MySQL 自身的复制连接元数据会保存连接凭据。

复制运维、初始化与切换

「复制运维」展示主从拓扑、IO/SQL 线程、复制延迟、告警、延迟历史、诊断、在线一致性检查、维护作业和事件;可配置采样间隔、延迟阈值、连续失败/恢复次数及自动重连。人工停止通道后期望状态会保存为 stopped,监控不会自动将其重启;重新启动或重连后恢复为 running。

  • 初始化专用从库和计划内主从切换属于高风险操作,默认关闭。完成运维评审后设置 SYNCDB_ENABLE_HIGH_RISK_OPERATIONS=true 并重启 syncdb;该开关只开放入口,不会跳过计划检查、生产目标保护、互斥或权限校验。
  • 应用初始化或切换计划时,除已登录会话外还必须再次输入当前服务 Token。服务端以恒定时间方式校验 Token,并重新生成、核对短期有效的计划;切换还要求输入目标数据源 ID。Token 错误只拒绝本次高风险操作,不代表当前会话自动退出。
  • 初始化和切换只支持专用实例:主库与从库除所选业务数据库外,均不得包含其他非系统数据库。检查忽略 information_schema、mysql、performance_schema、sys;若实例承载其他业务库,计划会以 replicationDedicatedInstanceRequired 拒绝执行。
  • 初始化不需要 mysqldump / mysql 客户端,也不生成中间 SQL 文件。目标必须没有表、视图、存储程序、事件、GTID 或复制通道,并开启 read_only / super_read_only。选择 shopee-wes-test 后,可直接从主库并行导入同名从库。
  • 在「配置与管理」保存了 configure 进度但尚未建立通道时,拓扑显示为「待初始化」(pending)。在复制运维页输入并行数、分片行数、备用 INSERT 批量行数和复制账号,生成初始化计划;确认后输入复制密码及服务 Token,提交后台作业。校验通过后会自动配置并启动 syncdb_auto,并启用原有监控。密码仅在本次作业内存中使用,不写入检查点、计划或审计。

流式初始化步骤:

  1. 用 LOCK INSTANCE FOR BACKUP 防止表 DDL,在最长 10 秒的全局读锁窗口内读取 gtid_executed 并为所有工作连接建立 REPEATABLE READ 一致性快照,随后释放全局读锁,主库继续 DML。快照读事务及防 DDL 锁保留到导入、校验和接续完成;长快照会增加主库 undo 保留量,请预留容量和 binlog 保留时间。
  2. 优先选择主键,否则选择非空唯一 B-tree 索引,使用键范围 [lower, upper) 分片;支持复合键、字符串排序规则及超过 2^53 的整数。无可用键的表在预检查中拒绝。默认 4 个工作线程、每片 20000 行,最多 16 个线程。
  3. 各线程持有自己的原快照连接,优先通过驱动注册的内存 Reader 执行 LOAD DATA LOCAL INFILE。字段使用十六进制与独立 NULL 标记,支持二进制、JSON、BIT、ENUM/SET、零自增值及生成列。LOCAL 不可用时回退多行参数化 INSERT,默认每批 500 行,并受 1 MiB、服务器包大小及参数数量限制。单行超出 INSERT 上限会明确失败。
  4. 每个分片用独立目标事务保证原子提交,导入会话关闭 sql_log_bin 和外键检查,保留唯一约束;仅临时关闭 super_read_only,普通账号仍受 read_only 限制。成功、重试及失败检查点保存到现有加密元数据,包含边界、次数、行数、抽样哈希和导入方式。瞬时故障最多尝试 4 次,只重传未完成分片;提交回包丢失时先校验是否已提交。转换、截断或冲突警告会回滚当前分片。
  5. 恢复强制只读后,按每个分片的确定顺序比较行数和 SHA-256 抽样哈希(每 256 行及末行),并用仍存活的源快照核对整表行数。只有全部通过且快照之后的日志未被清理,才设置目标 gtid_purged 为所记录的 GTID,并以 SOURCE_AUTO_POSITION=1 启动 syncdb_auto;再检查 IO / SQL 线程真实状态。抽样校验不等同于全量逐字节证明。

当前支持 MySQL 8.0.23+ 的 InnoDB 普通表;视图、触发器、存储程序、事件、空间列、外部表空间,以及仅有前缀/函数/浮点/生成列等不适合分片的索引,均在计划阶段拒绝,避免遗漏对象后接续复制。主库账号需要 BACKUP_ADMIN、RELOAD、SELECT 等快照权限;从库导入账号需要建库建表、写入、关闭会话 binlog、越过 read_only 及管理 GTID/复制的权限。导入期间不得由其他管理账号修改从库。

检查点不能恢复已经消失的 MySQL MVCC 快照。 当前自动重试限于同一存活作业;进程退出、快照连接丢失、取消或重试耗尽后,保留检查点用于核对,但不会拿新快照续传旧分片,也不会自动删除已导入数据。需人工核对并重新准备专用空从库,再初始化。若仅在已验证并设置 GTID 后配置/启动通道失败,可使用「配置与管理」核实已有通道并继续。启动恢复会尝试重新开启目标只读保护,中断的作业仍标记为需核对。

API 沿用 POST /api/replications/operations/seed/plan 和 /seed/apply,请求中的 seed 为 { "workers": 4, "shard_rows": 20000, "batch_rows": 500, "username": "syncdb_repl", "ssl_ca": "" };应用计划时额外提交 password、token、confirm 和 plan_hash。作业查询包含 snapshot_gtid、snapshot_valid 与 seed_shards 检查点。

开发验证:go test ./... 覆盖快照锁顺序、并发分片、INSERT 回退、回滚重试、提交回包丢失、检查点恢复和 GTID 缺口。可选设置 SYNCDB_TEST_SEED_DSN 指向有快照权限的独立测试 MySQL,运行 go test ./cmd/syncdb -run TestLiveSeedStreamingSnapshot -v,验证实际 LOCAL 内存流、复杂类型和快照后写入隔离;测试仅创建和删除自己命名的测试数据库,不重置服务器 GTID。该测试不替代真实双实例复制接续演练。

复制采样和运维事件存放在数据目录的独立 SQLite 文件 replication-monitor.sqlite,与加密 JSON 元数据及 binlog-index.sqlite 分开。每个拓扑的采样和事件固定保留最近 7 天,新记录写入时清理更早数据;该 SQLite 不使用元数据的 AES 加密,备份与访问权限应按运维数据管理。

正常关闭服务时,两个 SQLite 存储会等待正在打开的连接、取消读取和事务回滚完成,底层连接释放后关闭流程才返回,避免重启或移动数据目录时文件仍被占用。

「导出状态 CSV」按时间范围创建异步任务,并在「下载中心」提供下载。单个文件最多包含 8,000 条状态采样和 1,900 条运维事件;任一上限被超过时,任务仍完成并发布已截断文件,同时显示 replicationExportTruncated 警告。需要完整数据时应缩小时间范围后分段导出。

映射、类型和脱敏

任务「高级配置」使用 JSON:

{
  "target_objects": {"users": "users_copy"},
  "mapping": {"id": "_id", "user_name": "profile.name"},
  "defaults": {"active": true},
  "conversions": {"_id": "string"},
  "omit": ["internal_notes"],
  "masking": {"email": "hash"},
  "filters": [{"field": "id", "operator": "gte", "value": 100}],
  "key_fields": ["_id"]
}

在表单中分别将这些值填入对应配置项。字段映射支持点路径;默认值填补缺失或 null;转换支持 string/int/float/bool/json/datetime/objectid/decimal。显式转换失败会停止,不悄悄改成空值。大整数/高精度小数不要以 JavaScript 浮点数输入:过滤条件可用 {"$numberLong":"9007199254740993"} 或 {"$numberDecimal":"123.4567890123456789"};MongoDB 类型值支持 {"$oid":"..."}、{"$date":"2026-09-24T00:00:00Z"}。

脱敏支持 mask/hash/random/null/truncate。生产来源默认对常见姓名、电话、邮箱、地址、密码等字段进行递归脱敏;这是基于字段名的辅助保护,不是完整敏感信息识别,需人工核对映射和自定义规则。如果默认脱敏会改动源主键,任务会拒绝继续,避免生成错误匹配键。

结构对比与安全计划

可对比两个对象的字段差异、索引及原始元数据,并生成只增加、不删除的计划。同类型数据库可创建目标对象;MySQL 已有表只允许新增安全类型的可空、非主键列。不自动删除列、改类型或根据 MongoDB 采样推断变更 Schema。

UI 展示计划后需再次确认才能应用;服务端重新计算并核对计划哈希,拒绝过期计划和生产目标。Schema 操作也会记录审计。不能安全应用的差异交由 DBA 处理。

SQL 导出

  • MySQL 支持仅结构、仅数据、结构加数据;创建导出时自动加载并默认选中来源数据库的全部表(不含视图),可手动取消或编辑表清单。切换数据源会重新选择新来源的全部表;仍可配置过滤条件、行数上限和脱敏规则。
  • 异步生成真实 .tar.gz,包含 manifest.json 和按表分目录的 SQL。清单记录行数、是否达到上限、文件列表和 SHA-256 校验值,不含密码。
  • 数据 SQL 分片最大 4 MiB,未压缩总内容最大 1 GiB;大单行或不支持的值会使导出失败,而非生成损坏文件。
  • 下载需要登录;支持取消、删除、过期清理,失败会删除未完成文件。生产来源需明确确认并默认脱敏。
  • 不使用长事务,因此导出不是跨表一致快照。达到上限不代表导出了所有记录,请检查清单的 limit_reached。

WES 配置导出

在独立的「WES 配置导出」菜单点击「创建 WES 导出」,选择 MySQL 或 MongoDB 数据源及数据库,分别生成配置包。MySQL 默认读取 ttx_table_model、ttx_bill_model、ttx_wso_data、ttx_navigator、config_detail、config_header、config_value 七张表,可调整配置数据表清单。异步完成后在该页面或下载中心下载 .tar.gz。

数据来源 包内路径
ttx_table_model.structure wes/table/{code}.json
ttx_bill_model.structure wes/bill/{code}.json
ttx_wso_data.structure wes/view/{tid}/{oid}.json
ttx_navigator.structure wes/navigator/{code}.json
config_header、config_detail(按 recordType 关联) wes/config/{recordType}.json
config_value wes/data/config_value.csv
其他选中的配置表 wes/data/{表名}.csv
选中的 ttx_system wes/sql/ttx_system.sql
当前数据库全部普通表(包含未选择导出数据的业务表) schema/{表名}.sql

JSON 文件格式化缩进,导出时直接删除 structure JSON 字符串内未转义的 Tab,其余字段和值保留,已有 \t 转义不变;CSV 使用数据库列名及 UTF-8 编码,空值输出空单元格,供 WES 配置加载使用。manifest.json 记录行数、表及文件清单和 SHA-256。建表 SQL 与 WES 配置分目录,导出过程只读取数据库。配置校验失败时,任务的「错误详情」显示表名、记录标识和字段。

config_header 和 config_detail 选择任意一张时自动补齐另一张,不再生成这两张表的 CSV。每个 recordType 一份 JSON,格式为 { "recordType": "STATUS", "warehouses": [{ "warehouseCode": "*", "header": { ... }, "details": [{ ... }] }] }。相同类型内按 warehouseCode 匹配,避免混合全局和不同仓库的数据;关联不依赖 id/headerId,但保留原始字段、ID、空字符串、NULL 和数值精度。没有明细的头保留空数组,没有对应头的明细保留在所属仓库且 header 为 null;重复头记录会报错,不覆盖数据。两张表的行数分别计入清单,共用 JSON 文件各自列入文件清单。这是供后续导入处理的关联 JSON 格式,现有 WES CSV 加载器不会自动加载 wes/config/。

配置标识中的通配符 *、? 在 JSON 文件名中分别转义为 %2A、%3F,% 转义为 %25,JSON 内的原始标识保持不变。例如 ClearRedis* 导出为 ClearRedis%2A.json,可在 Windows 解压。

选择 MongoDB 后无需填写数据库或集合,自动遍历全部可访问数据库,导出所有 edi_erp_setting_<名称>、edi_erp_transform_file_<名称>(包括 ess、shopee 等多组配置)、edijson、xslt 和 ttx_scheduler 集合。数据源的默认数据库不会限制遍历范围,原认证库保持有效。总行数上限跨数据库累计;没有匹配集合的数据库不会读取业务文档。清单记录扫描的数据库和每个集合的来源,任一数据库读取失败时不发布不完整包。

edi_erp_setting_<名称> 导出为 edi/<数据库>/<名称>/edijson.json;edi_erp_transform_file_<名称> 按文档的 path、type、text 导出为 edi/<数据库>/<名称>/xslt/.../*.xslt(支持资源路径前导 /,已有 xslt/ 或扩展名时不重复添加)。直接命名为 edijson、xslt 的集合放入 edi/<数据库>/。EDI JSON 去除 _id、_class,保留嵌套对象和数值精度;XSLT 保留文本原样。同一集合中同路径的 XSLT,主目录保留按 _id 升序读取的第一份,其余原文存入 duplicates/<数据库>/<集合>/<序号>.xslt,清单的 duplicate_resources 记录两份文档标识及文件路径,导出任务显示提示。大小写冲突或同一配置集合存在多份 EDI JSON 仍会报错。

ttx_scheduler 的完整文档导出到 mongo/<数据库>/ttx_scheduler.json,格式为 MongoDB Canonical Extended JSON 数组,保留 _id、日期和精确数值类型,可用 mongoimport --jsonArray 读取。MongoDB 配置包不包含 SQL DDL。将对应 EDI 目录中的文件放入项目 src/main/resources/ 使用。

单文件最大 4 MiB,未压缩总内容最大 1 GiB;默认配置记录总上限 100,000,最多可设为 1,000,000。超过行数上限、JSON 损坏、标识不一致、文件名不安全或路径重复时会失败并清理未完成文件,不发布截断包。生产数据源沿用确认和数据库记录脱敏规则。导出不是跨表一致快照。

参考目录里的 template/(Excel、报表模板)和 yml/ 属于项目资源文件,此数据库导出不包含这些文件。解压后将所需 wes/ 内容合并到项目配置目录;本功能不会直接覆盖本地项目文件。

API:POST /api/exports/wes。MySQL 请求包含 source_id、database、objects;MongoDB 仅需 source_id,自动遍历所有可访问数据库(忽略请求中的 database),objects 省略或为空时自动发现上述配置集合,也可传入集合名进行筛选。可配置 batch_size、max_rows、confirm_production;完成后复用 /api/exports/{id}/download 下载。

数据库本地备份与恢复

在数据源列表的 MySQL 数据源上选择「快速备份」。默认开启「无锁读取」,使用单连接的 InnoDB 一致性快照读取全部分片,不执行 LOCK INSTANCE FOR BACKUP 或 FLUSH TABLES WITH READ LOCK,不需要相应锁权限;该模式自动使用一个读取线程,不记录 GTID,不能用于原生主从接续。备份期间避免执行 DDL,普通读取仍可能持有 MySQL 元数据锁。取消「无锁读取」后,使用带备份锁及短暂全局读锁的并行 GTID 快照。两种模式均按主键/唯一索引分片,直接写入本地压缩二进制行帧(.backup.gz);NULL、空值和二进制内容按原值保存。普通同步任务使用普通 SELECT,不执行上述备份锁。生产数据的本地制品未加密,创建备份前必须明确确认本地访问权限。

备份任务及每个分片的边界、行数和 SHA-256 校验保存在加密元数据中。完成后可在「下载中心」下载制品,或选择「恢复到新数据库」:目标必须是非生产 MySQL 数据源,目标数据库必须尚不存在,恢复会按分片事务批量写入并校验行数和哈希。恢复制品在作业完成前不能删除或被过期清理;校验失败不会发布成功状态。

勾选「恢复后准备主从接续」属于高风险操作,仅允许专用空实例,并要求设置 SYNCDB_ENABLE_HIGH_RISK_OPERATIONS=true。服务会确认目标没有其他业务数据库、目标 GTID 为空且主库仍覆盖快照 GTID,在导入期间关闭会话 binlog,完成后应用快照 GTID 并启用 read_only/super_read_only。之后可在「配置与管理」建立 syncdb_auto 增量复制;共享实例、空 GTID 或日志已被清理时会拒绝接续。

接口为 POST /api/backups(请求 source_id、database、workers、shard_rows,生产源另需 confirm_production: true)、GET /api/backups、GET /api/backups/{id}、GET /api/backups/{id}/download,以及 POST /api/backups/{id}/restore。恢复请求使用 target_id、target_database、confirm: true 和可选 prepare_replication: true;失败的恢复作业可用 POST /api/backups/{restore_id}/resume 携带 confirm: true 续跑,服务会校验目标库结构和已完成分片哈希,只重放未完成分片。备份和恢复作业沿用下载中心的状态、取消、删除与保留策略。

Binlog 本地索引、审计与权限模板

  • Binlog 查询与 CSV 导出只读取本地 SQLite 索引,不在请求中回放源 Binlog,也不提供元数据/直接预览模式。数据库和表名下拉也来自本地索引,不连接源库列举对象;支持手填名称,便于筛选历史库表。
  • 索引保存在 data_dir/binlog-index.sqlite,与配置、凭据及任务使用的加密 JSON 存储独立。后台通过 MySQL 复制协议持续采集 INSERT/UPDATE/DELETE、部分行镜像和压缩事务中的事件,保存原始事件值及原始 SQL;页面查询时脱敏,CSV 保留原始值。SQLite 本身不使用配置存储的 AES 加密,应按原始业务数据管理其目录、备份和访问权限。
  • 首次点击「建立 / 继续索引」,选择「现有历史日志」或「从当前开始」。历史模式从仍可用的历史日志开始采集一次,再持续跟进新增事件;首次历史采集耗时取决于日志数量和体积。当前模式从启动时的当前位置开始,不能补回更早事件。首次起点建立后锁定,重试不会重置历史范围。
  • 每批事件与复制解析检查点一起落盘,后续启动从已保存的检查点继续;检查点包含恢复行事件解码所需的上下文。服务启动时自动恢复仍启用的采集任务,手动停止的任务保持停止。源实例 UUID 或连接身份变化、检查点日志已被清理、索引损坏等情况会报错,不静默跳过缺口或重新套用旧索引。
  • 默认按数据源保留 7 天,容量为 1 GiB;可配置保留天数及容量(10 MiB–64 GiB)。容量限制统计保存的事件 JSON payload 字节数,不等于 SQLite 文件或磁盘占用:表、索引、检查点和 WAL 另有开销,过期清理后的页供后续写入复用,文件不会随每次清理自动缩小。达到容量上限会停止采集并报告 binlogIndexStorageFull;调整容量或保留期后点击继续。
  • 面板实时显示 CATCHING_UP(追赶历史)、WATCHING(持续采集)、STOPPED 或 FAILED,以及当前文件/位置、扫描事件/字节、已存事件/字节、覆盖起点和更新时间。停止采集后仍可查询已保存且尚未过期的事件。完整性取决于采集进度和保留范围;未追平或查询起点早于覆盖范围时,列表会明确提示仅包含已采集且仍保留的部分。
  • 按库表导出 INSERT/UPDATE:建立索引并等待追平后,选择数据源、数据库和表,将操作类型设为「插入 + 更新」,填写开始、结束时间后提交 CSV 后台任务。也可单独选择插入、更新或删除;更新包含前后镜像。操作筛选只匹配对应 ROW 行事件,不会把 Query/DDL 当成目标表的行变更,MySQL 必须开启 ROW 格式 Binlog。
  • 时间筛选以 Binlog 事件时间为准,包含两个边界,不保证事务提交顺序;页面将本地时间转换为 RFC3339。列表按本地 index_id 升序分页,下一页使用 after_id,不使用源文件位置分页。库表、事件类型、操作及关键词条件共同生效;索引保留时间外或尚未采集的数据无法通过查询恢复。
  • CSV 使用 UTF-8 BOM,每个匹配事件占一条记录,包含 UTC 时间、源文件/位置、事件类型、库表、行数和原始前后镜像 JSON。row_changes_json 保留字段值,Query 事件的 info 保留原始 SQL(含内部换行),二进制值按 JSON 规则编码为 Base64,NULL 保持为 null。压缩事务子事件独立成行,event_path 表示层级;可能触发电子表格公式的文本添加单引号前缀。
  • CSV 与列表使用同一本地索引,不受列表页大小影响;索引尚未追平时返回 binlogIndexNotReady,请求时间超出保留范围时返回 binlogIndexRangeUnavailable,未建索引时返回 binlogIndexRequired,不会退回直接读取源日志。后台 CSV 最多运行 2 小时,导出最多 10,000 事件、32 MiB CSV;失败、取消或超限不发布部分文件。重新提交导出可复用索引,无需重新扫描历史源日志。
  • 后台采集需要 MySQL 开启 Binlog,账号具有 REPLICATION CLIENT 和 REPLICATION SLAVE 权限,并能通过复制协议连接 MySQL。普通 SQL 连接测试成功不代表复制协议可用;不透传复制协议的代理可能返回 binlogReplicationUnsupported。应用不自动授予权限或配置原生主从拓扑。
  • binlogDisabled 表示 MySQL 二进制日志未开启。可用 SELECT @@version, @@log_bin; 确认;log_bin 不能通过运行时 SET GLOBAL 开启,需配置 [mysqld] 下的 log-bin=mysql-bin、binlog-format=ROW,移除冲突的 skip-log-bin / disable-log-bin 参数,安排重启 MySQL 后再确认 SHOW BINARY LOGS。重启 syncdb 不会开启 MySQL Binlog,也无法恢复已清理的历史日志。
  • binlogPermissionDenied 表示采集账号权限不足,binlogIndexGap 表示检查点所需日志缺失,binlogIndexSourceChanged 表示数据源身份变化。接口只返回安全错误码,不输出凭据或原始数据库错误。页面详情仍有行、列、单元格展示限额,截断会明确提示,不代表 SQLite 中的原始事件值被页面限额截断。
  • SQL 审计记录服务实际发起的写入及 dry-run,保留占位符、不保存绑定参数。支持任务、执行、数据源、操作、状态、时间与关键词筛选,以及有界分页。
  • 审计详情保留完整脱敏 SQL 的 SHA-256、原始字节数及截断标识,预览最多 4096 字节并保持有效 UTF-8。哈希针对脱敏语句,不包含绑定值;无法据此还原真实参数。
  • 旧审计记录没有原始长度/哈希时,界面标记完整性未知,不把历史预览重新计算成“完整 SQL”证明。

Binlog 页面的「停止」会取消当前事件查询,并停止当前提交的后台 CSV 任务(如仍在运行),保留已显示的结果。停止后后台刷新不会重新查询;手动点击刷新、下一页或导出可重新开始。更改筛选条件、离开页面或退出登录会取消旧的本地查询请求,已提交的 CSV 后台任务继续运行,可在下载中心停止。「停止索引采集」是独立操作;页面查询停止不会停止持续采集。

CSV 后台导出与下载中心

Binlog 与慢 SQL 页面的 CSV 按钮提交后台任务,立即返回任务编号,无需保持页面或浏览器打开。在「下载中心」查看类型、筛选范围、状态、记录数、文件大小、错误和提示;完成后点击下载,可重复下载原始、不脱敏的 CSV。运行中的任务可停止,结束后可删除。SQL 压缩包导出继续使用原菜单和下载接口。

Binlog 后台任务实时更新当前文件、文件总数、已扫描事件/字节、匹配事件及输出字节数;下载中心和已打开的详情自动刷新,文件完成保存后才显示 100%。进度每 5 秒保存到加密元数据,刷新页面后仍可查询。GET /api/exports/{id}/events 提供每秒一次的 SSE;普通查询对象的 progress 字段包含 file、files_total、files_completed、scanned_events、scanned_bytes、total_bytes、matched_events、output_bytes、updated_at。当前进度用于观察,服务重启会将未完成任务标记为失败,尚不支持从该进度断点回放。

任务元数据持久化至原加密存储,CSV 写入数据目录的 exports 子目录。先写 .csv.partial,完整生成后才发布下载;失败、超限或取消时不会提供部分文件。完成文件在应用重启后仍可下载,未完成任务会标记为 interruptedOnRestart,需要重新提交;文件沿用 export.retention_hours 清理策略。后台导出与其他任务共享并发额度,满额时返回 jobsBusy,不会无限排队。

创建接口:POST /api/exports/csv,JSON 为 {"kind":"binlog","query":{"source_id":"ds-1","storage":"index","from":"2026-09-25T00:00:00Z","to":"2026-09-25T01:00:00Z","database":"app","table":"orders","operations":"insert,update"}};Binlog 的 storage 省略时同样使用本地索引,不接受 live。慢 SQL 使用 kind: "slow-sql" 和对应筛选参数。query 各值均为字符串,校验规则和原 CSV 接口一致。成功返回 HTTP 202 及任务对象。通过 GET /api/exports 或 /api/exports/{id} 查询任务,GET /api/exports/{id}/download 下载,POST /api/exports/{id}/cancel 停止,DELETE /api/exports/{id} 删除;均需要身份验证。同步 CSV GET 接口保留兼容,Binlog 同步接口也只读本地索引;页面使用异步任务。

慢 SQL 查询与 CSV

  • 在「慢 SQL」中选择 MySQL 数据源,可按数据库、起止时间、最小耗时(秒)和 SQL 关键词查询;默认过去 24 小时、耗时至少 1 秒,单次时间跨度最多 31 天。展示执行时间、查询/锁等待耗时、返回/扫描行数、连接用户、线程及原始 SQL。
  • 数据来自 mysql.slow_log,使用普通 SQL 连接,无需 Binlog 复制协议。账号需有该表的 SELECT 权限;只有服务器已开启 slow_query_log 且 log_output 包含 TABLE 时才会新增表记录。关闭日志或仅写 FILE 时仍可查询已有表记录,界面会提示当前状态;无法通过此接口读取服务器文件或补回未记录的 SQL。应用不会开启日志、修改阈值、授予权限或清空日志。
  • 数据库筛选匹配日志的默认数据库字段,不解析 SQL 中的跨库表名;关键词是原始 SQL 中的字面子串。实际收集范围取决于 MySQL 的 long_query_time 等服务器设置,降低查询耗时条件不能恢复未记录的语句。
  • CSV 按当前筛选导出全部匹配记录,不受列表分页影响,保留原始 SQL、不脱敏;含 UTF-8 BOM,公式前缀会转义。单次最多 10,000 条、每条 SQL 最多 1 MiB、原始结果最多 16 MiB、CSV 最多 32 MiB。超限、非 UTF-8 日志或读取失败时返回错误,不下载截断文件。
  • 慢日志表通常没有适用索引,时间条件和 LIMIT 不保证避免扫描。读取使用并发限制、15 秒请求时限及连接读超时;前端不自动轮询慢日志。列表按时间、线程倒序,实时新增日志可能使偏移分页变化。

接口:GET /api/slow-sql 和 GET /api/slow-sql/export.csv,均需要登录或 Bearer 令牌。必填 source_id、from、to(RFC3339);可选 database、keyword、min_duration(秒,默认 1)。列表另支持 limit(1–1000,默认 100)、offset(0–100000),返回 {rows: [...], has_more: false, warnings: []};CSV 不接收分页参数,空结果仍提供表头。

复制账号安全向导

选择 MySQL 数据源、创建/删除操作、账号角色、明确的来源主机,再生成在线检查后的 SQL 计划。支持 reader、repl、replica_admin 和 applier;创建账号的密码单独输入或在浏览器安全随机生成,计划、复制/下载的 SQL 及审计中只保留密码占位符。

  • 当前支持 MySQL 8.x(8.0 至少 8.0.23),不将 MariaDB 或其他版本按 MySQL 8 语义执行。管理连接需要读取 mysql.user、mysql.role_edges、mysql.proxies_priv 和相关 performance_schema 表;检查权限不足或 Performance Schema 关闭时拒绝生成可执行计划。
  • 用户名限定 syncdb_ 前缀、总长度不超过 32;来源主机必须是明确的 IP/主机名,不允许 % 或 _ 通配。有默认库时数据库范围固定为该库,无默认库时必须填写授权库;按 partial_revokes 设置处理数据库名的通配语义。
  • 检查账号是否存在、活动连接、复制通道及角色授权。计划展示服务器版本、观察到的角色、主机、授权范围和风险;应用前再次检查并校验计划哈希,配置变化需重新生成。
  • 计划校验与 DDL 之间不是跨进程原子操作;执行期间不要由其他管理工具并行变更同一账号。检测到强制角色、双向角色引用或双向 PROXY 授权时,同样保守阻止删除。
  • 默认只生成计划;必须明确确认后才执行账号 DDL。 生产标签、服务器只读状态或数据源正在使用时禁止 Web 执行。不会自动关闭只读保护,也不会自动修改复制拓扑。
  • 账号操作属于服务器级变更:同一连接地址的任一数据源标记生产时,其他库的开发/测试标签不能绕过保护。请统一同一服务器 IP/DNS 别名的环境标签,应用不能仅凭不同地址识别所有服务器别名。
  • 账号被应用配置引用、有活动连接、继承角色或存在复制依赖时不生成可执行删除 SQL。复制连接账号及具有复制读取/应用权限的账号,一律交由 DBA 核查离线和远端依赖,不通过向导删除。
  • 为避免同一服务器的 IP/DNS 别名绕过引用保护,只要工作区任何 MySQL 数据源使用同名账号,就保守禁止删除;跨服务器同名账号也需人工核查。
  • replica_admin 使用 REPLICATION_SLAVE_ADMIN 与 REPLICATION CLIENT,不授予无关的连接管理权限。applier 仅授予复制应用权限与选定库的基本 DML,不自动赋予 DDL/存储程序权限或设置复制通道的权限检查用户。
  • 执行前持久化批准记录,每条语句记录脱敏审计。DDL 不使用事务:创建成功而授权失败会明确返回已完成语句数,不自动删除账号“回滚”;需由 DBA 检查后补全权限。执行结果不确定时也不要盲目重复操作。
  • 密码为 16–128 个可见 ASCII 字符,不允许空白、单双引号或反斜杠;数据库自身的密码策略仍可能拒绝。关闭弹窗或退出会清除表单密码,密码不写入应用元数据。HTTP 环境下传输不加密,真实环境请使用 HTTPS 与数据库 TLS。

持久化与恢复

配置、凭据及任务元数据使用 AES-256-GCM 加密 JSON;Binlog 事件、索引状态和检查点另存于独立的 SQLite。数据目录包含 store.json、store.key、session-token、exports/ 和 binlog-index.sqlite(运行时可能还有 -wal、-shm 文件)。备份索引请停止服务后复制完整数据库,或使用 SQLite 一致性备份,不能只复制写入中的主文件。备份/迁移必须同时保存数据和密钥,不能删除密钥后继续使用旧数据;损坏或密钥不匹配时启动失败,不静默丢弃配置。

重启保留数据源、密码、任务、执行/导出历史与审计;中断的工作标记失败,不假报完成。历史保留最近约 1000 项,正在运行的记录保留。元数据采用临时文件落盘后替换,Windows 不保证所有断电场景的替换原子性。文件权限设置不等于 Windows ACL;请限制整个数据目录的本机访问权限。

导出制品本身不是加密文件。应用层登录、目录权限和生产数据脱敏仍需同时使用。

API 与验证

主要接口:/api/session、/api/datasources、/api/tasks、/api/executions、/api/events、/api/exports、/api/backups、/api/binlog/index、/api/binlog/index/events、/api/sql-executions、/api/schema/diff|plan|apply。创建/修改使用 POST/PUT,删除使用 DELETE;任务和导出支持 /id/cancel,导出与本地备份支持 /id/download。

Binlog 索引接口均需要已登录会话或 Bearer 令牌:

  • GET /api/binlog/index?source_id=<id>:读取采集状态、配置、当前文件/位置、扫描量、已存量、coverage_from、caught_up 和更新时间;未建立时返回 NOT_STARTED。
  • POST /api/binlog/index/start:JSON 为 {"source_id":"ds-1","start_mode":"history","retention_days":7,"max_bytes":1073741824}。首次 start_mode 可选 history 或 current,后续继续沿用原起点及检查点;调整保留期、容量后也通过此接口继续。
  • POST /api/binlog/index/stop:JSON 为 {"source_id":"ds-1"},停止持续采集,保留已有索引供查询。启停返回 HTTP 202 和状态对象,状态轮询用于确认最终结果。
  • GET /api/binlog/index/events?source_id=<id>:可选 from、to(RFC3339)、database、table、type、operations、keyword、limit(1–1000,默认 100)和 after_id。返回脱敏 events、next_cursor、status、coverage_from、complete 和 warnings。将上一页的非空 next_cursor 作为下一页 after_id,按 index_id 升序继续;变更筛选后清除游标。complete: false 表示已采集且仍保留的范围不能完整覆盖请求。
  • GET /api/binlog/index/objects?source_id=<id>:返回本地索引记录的数据库名称数组 [{"name":"app"}];加 database=<name> 返回该库的表名数组。此接口不连接源 MySQL,尚未采集的名称不会出现在选项中,可手填筛选。

Binlog CSV 同步兼容接口为 GET /api/binlog/export.csv?source_id=<id>&from=<RFC3339>&to=<RFC3339>,仅查询 SQLite 索引,起止时间必填,可选 type、operations、keyword、database、table。operations=insert,update 只保留插入和更新,也支持单独 insert、update、delete 或三者的不重复组合;省略表示不限操作,与其他条件共同生效。查询值需 URL 编码(包括时区中的 +)。成功返回 CSV 附件,无匹配事件时仅包含表头;未建立、未追平、时间超出保留范围或其他失败返回 JSON 错误码,不读取源 Binlog 作为替代。

库表选择:GET /api/datasources/{id}/databases 返回 {databases: [...], truncated: false};GET /api/datasources/{id}/tables?database=<name> 返回 {tables: [...], truncated: false},支持 MySQL 表和 MongoDB 集合。均需认证,使用已保存的凭据,不要求目标库是该数据源的默认数据库。

GET /api/datasources/{id}/objects 和 /objects/{name} 支持 ?database=<name>。任务接受 source_database / target_database,导出接受 database,结构对比、计划及应用接受 source_database / target_database;省略时兼容数据源默认库,两者都为空则返回 databaseRequired。

复制账号向导使用 POST /api/replications/accounts/plan 与 /apply。执行、导出的 /api/executions/{id}/events、/api/exports/{id}/events 只发送对应资源的 SSE,终态发送后关闭;全局控制台仍使用 /api/events。审计分页参数为 limit(1–1000)和 offset,返回数组以兼容原有客户端。

原生复制使用 POST /api/replications/auto/check 分步检查(检查未通过也返回步骤列表),/plan 保留旧版错误状态码语义;两者均返回 steps、statements、can_apply、needs_password 及可用时的 plan_hash。参数为 source_id、target_id、database、action(configure / start / stop),其中 database 必须明确选择主库数据库,不能省略或沿用数据源默认库;配置及其续行还需 username。例如:{"source_id":"primary","target_id":"replica","database":"shopee-wes-test","action":"configure","username":"syncdb_repl"}。POST /api/replications/auto/apply 要求最新 plan_hash 与 confirm: true,只有 needs_password: true 时需要 password 和 seed_confirmed: true;只执行剩余步骤并返回实际核实结果及步骤状态。stop 不要求 source_id 或 database。GET /api/replications/auto/progress 返回保存的进度,GET /api/replications/auto/status?target_id=<id> 返回脱敏的实时通道状态。所有接口均需认证。

go test ./...
go vet ./...
cd frontend
npm ci
npm run build
npm test
cd ..
node --check cmd/syncdb/web/app.js
node --check cmd/syncdb/web/auto-replication.js
node tests/auto-replication-ui.test.cjs
node tests/auth-errors.test.cjs

在 frontend 中执行 npm run test:browser 可使用本机 Edge/Chrome 检查主界面的桌面与窄屏布局、原生样式、搜索、语言切换和登录流程。其他系统可通过 SYNCDB_BROWSER_PATH 指定 Chromium 浏览器可执行文件。测试使用临时本地静态服务和模拟 API,不连接配置的数据源;截图输出至 frontend/browser-artifacts/。

开发过程中还使用临时隔离验证套件检查适配器协议/命令、任务生命周期、存储恢复、认证、导出文件和 UI DOM 交互;这些测试不替代真实 MySQL/MongoDB 环境验收。首次正式同步前,必须在测试数据库中验证目标类型、权限、数据约束与脱敏结果。

原生复制的自动化测试覆盖管理接口、GTID/实例/已有通道保护、TLS、停止流程、各步骤失败及响应丢失后的恢复、重启恢复、并发互斥、密码隐藏与审计持久化;前端控制器测试覆盖保存进度的恢复、剩余步骤确认和过期请求隔离。测试使用模拟 SQL 驱动及最小 DOM,不替代真实 MySQL 主从环境和浏览器的端到端验证。

docs/design.md 保留产品设计目标;前端使用 React + Ant Design,配置与任务存储仍为加密 JSON。独立 SQLite 已用于 Binlog 事件索引及持续采集检查点,不能据此推断其他设计项已实现。MySQL 原生复制由独立管理入口提供。CI 执行 Go 检查、Linux/Windows 编译和 Docker 发布,静态页面随二进制嵌入。

S
Description
No description provided
Readme
1.8 MiB
Languages
Go 66.3%
JavaScript 31.9%
CSS 1.4%
HTML 0.3%