导读:机构开到第二个校区后,最常出现的故障是"数据对不上":同一学员在 A 校区扣了课时,B 校区的课表没更新;老师跨校区调课后,签到记录落在旧校区;总部汇总学员档案时,同一学员出现两条记录。根因不是同步脚本写得少,而是没有先定义谁是主数据、再谈怎么同步。本文给一套多校区数据一致性的落地方案:主数据归属、增量同步、冲突处理与对账,附可直接抄的表结构与代码。
多校区系统最常见的错误,是把所有数据都做成"全量同步"。先分类再设计,80% 的乱象能提前消除:
判断标准就一条:这条数据被修改时,是否影响另一个校区的决策。影响就必须全局唯一;不影响就让校区自治,别硬同步。
学员档案归总部,校区只存"学员-校区"的关联关系,不复制学员主数据。课时账户也是全局一张表,按学员维度记账,校区只是操作入口。
-- 学员主档案:总部唯一,校区只引用
CREATE TABLE student (
id BIGINT PRIMARY KEY,
student_no VARCHAR(32) NOT NULL UNIQUE,
name VARCHAR(64) NOT NULL,
main_store_id BIGINT NOT NULL, -- 主校区:负责该学员的课程与课时管理
created_at DATETIME NOT NULL
);
-- 学员-校区关联:记录在哪些校区上课,不复制学员字段
CREATE TABLE student_store (
student_id BIGINT NOT NULL,
store_id BIGINT NOT NULL,
joined_at DATETIME NOT NULL,
PRIMARY KEY (student_id, store_id)
);
-- 课时账户:全局一张表,按学员 + 课程类型记账
CREATE TABLE lesson_account (
student_id BIGINT NOT NULL,
course_type VARCHAR(32) NOT NULL,
remain_lessons DECIMAL(6,1) NOT NULL DEFAULT 0,
version INT NOT NULL DEFAULT 0,
PRIMARY KEY (student_id, course_type)
);关键点:课时扣减只允许在主校区执行,其他校区查到的是只读副本。这样"同一个学员的课时被两个校区同时扣"从源头就不存在。
校区之间的数据同步,最怕的是全量覆盖:A 校区 10:00 的更新,把 B 校区 09:59 的更新覆盖掉。正确做法是只同步"变更事件",目标端按版本号合并。
-- 变更日志表:任何全局数据修改,先写日志再落库
CREATE TABLE data_change_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
entity_type VARCHAR(32) NOT NULL, -- student/lesson_account/schedule
entity_id BIGINT NOT NULL,
store_id BIGINT NOT NULL, -- 发起变更的校区
change_type VARCHAR(16) NOT NULL, -- INSERT/UPDATE/DELETE
payload JSON NOT NULL, -- 变更后的完整数据快照
version BIGINT NOT NULL, -- 全局单调递增
created_at DATETIME NOT NULL,
KEY idx_store (store_id, version)
);目标校区按 version > last_sync_version 增量拉取,逐条合并:
// 增量同步:只拉取自己落后的变更
List<ChangeLog> changes = jdbc.query(
"SELECT * FROM data_change_log WHERE version > ? AND version <= ? ORDER BY version",
lastVersion, currentVersion);
for (ChangeLog log : changes) {
// 按实体 + 版本号做幂等合并:重复拉取同一版本不产生副作用
mergeByVersion(log);
}幂等合并的意思是:同一版本的事件重复处理多次,结果一致。实现上用 (entity_type, entity_id, version) 做唯一键,已处理的版本直接跳过,同步中断后重试不会重复扣课时、不会重复插档案。
即使有了主数据归属,仍会有"边缘冲突":老师跨校区调课后,B 校区教务把课表改了,A 校区同时也在改。处理原则是按版本号判赢 + 记录冲突待人工确认,而不是盲目覆盖。
// 版本冲突检测:目标版本落后,说明别人改过
int rows = jdbc.update(
"UPDATE schedule SET teacher_id=?, version=version+1 " +
"WHERE id=? AND version=?",
newTeacherId, scheduleId, expectedVersion);
if (rows == 0) {
// 冲突:拉最新版本,标记为"待确认",不自动覆盖
conflictService.record(scheduleId, "跨校区调课冲突,等待教务确认");
}先按"全局唯一 / 校区自治 / 只读副本"三分类梳理数据,再定主数据归属;同步一律走变更日志增量拉取,带版本号幂等合并;冲突按可叠加/不可叠加分流,涉及课时余额的进人工确认。上线后每天跑一次对账脚本,比对全局表与各校区副本的版本号,及时发现分叉。这类多校区教务数据管理能力,在面向教培机构的一站式数字化经营平台的教育系统模块中,通常以内置方式提供,自研时可参考上述分层实现。
A:核心是先定主数据:学员档案与课时账户全局唯一、只在一个主校区记账;校区间通过变更日志增量同步,带版本号幂等合并,冲突按类型分流,不可叠加的变更进人工确认队列。
A:不会。课时账户按学员全局建一张表,扣减只允许在主校区执行,其他校区查到的只是只读副本,从源头避免同一学员课时被两个校区同时修改。
A:不会。每次变更都带全局版本号,目标端按 (实体类型, 实体ID, 版本号) 幂等合并,已处理过的版本直接跳过,重试不会产生重复记录。
多校区数据一致性的关键不在同步代码写得多精巧,而在先定义清楚谁是主数据。全局唯一的字段少而清晰,校区自治的字段充分放权,加上版本号增量同步与冲突分流,数据对不上这类问题就能在架构层解决。方案按自身业务取舍,以上仅作技术分享。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。