4、同步触发机制:定时批量同步、实时增量同步(CDC)、事件驱动同步、API调用同步、消息队列触发
同步触发机制,说白了就是「什么时候干活」的问题。
我见过不少团队,一上来就纠结用 CDC 还是用消息队列,结果连业务对数据时效性的要求都没搞清楚。你想想看,一个每天凌晨跑一次的财务汇总,跟一个需要秒级响应的库存变更,触发方式能一样吗?
这一章,我把五种主流触发机制掰开揉碎了讲。每种机制我都会结合项目里的真实场景,告诉你什么时候该用,什么时候千万别碰。
核心观点:没有最好的触发机制,只有最合适的。选型之前,先回答三个问题——数据能等多久?数据量有多大?下游系统扛得住吗?
4.1 定时批量同步
这是最古老、也最稳妥的方式。说白了就是定个闹钟,到点干活。
我习惯用 Cron 表达式来定义调度周期。比如每天凌晨 2 点跑一次全量同步,或者每 15 分钟拉一次增量。
// 定时任务配置示例(Spring Boot + @Scheduled)
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行全量同步
public void fullSync() {
log.info("开始全量同步主数据...");
// 1. 查询源系统全量数据
// 2. 对比目标系统差异
// 3. 执行 upsert 操作
// 4. 记录同步日志
}
@Scheduled(cron = "0 */15 * * * ?") // 每15分钟执行增量同步
public void incrementalSync() {
log.info("开始增量同步...");
// 只同步上次同步时间戳之后变更的数据
}
我的经验:定时批量同步最适合「数据一致性要求不那么苛刻」的场景。比如客户主数据每天同步一次,财务数据每天凌晨跑批。我曾经在一个项目中,硬要用定时批量去做库存实时扣减,结果数据延迟导致超卖——嗯,那是选型失误的典型。
注意:定时批量同步有两个坑:一是大表全量扫描会锁表,建议分批读取;二是如果同步任务失败,要有重试和告警机制。我曾经因为没加重试,某个定时任务挂了三天没人发现,数据差了三天。
4.2 实时增量同步(CDC)
CDC(Change Data Capture),说白了就是「盯着数据库的变更日志干活」。源数据库里每发生一次 insert、update、delete,CDC 组件都能捕获到,然后实时推给目标系统。
我常用的 CDC 工具有 Debezium、Canal、Maxwell。底层原理都是读取数据库的 binlog(MySQL)或 WAL(PostgreSQL)。
// Debezium 连接器配置示例(JSON)
{
"name": "customer-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"database.hostname": "192.168.1.100",
"database.port": "3306",
"database.user": "debezium",
"database.password": "debezium_pwd",
"database.server.id": "184054",
"database.server.name": "customer_master",
"database.include.list": "customer_db",
"table.include.list": "customer_db.t_customer",
"database.history.kafka.bootstrap.servers": "kafka:9092",
"database.history.kafka.topic": "schema-changes.customer"
}
}
CDC 的延迟通常在秒级甚至毫秒级。我见过一个电商项目,用 CDC 把订单变更实时同步到搜索引擎,用户下单后 3 秒内就能在搜索结果里看到。
关键点:CDC 不是银弹。它要求源数据库开启 binlog,会增加一定的 IO 开销。另外,如果源库做了主从切换或版本升级,CDC 连接器可能会断连,需要设计自动恢复机制。
4.3 事件驱动同步
事件驱动跟 CDC 有点像,但触发源不一样。CDC 盯着数据库日志,事件驱动盯着「业务事件」。
举个例子:当客户状态从「正常」变为「冻结」时,业务系统会发布一个 CustomerFrozenEvent。订阅了这个事件的同步服务收到后,立刻把冻结状态同步到所有下游系统。
// 事件驱动同步示例(伪代码)
public class CustomerEventConsumer {
@EventListener
public void handleCustomerFrozen(CustomerFrozenEvent event) {
String customerId = event.getCustomerId();
// 1. 查询客户最新数据
Customer customer = customerService.getById(customerId);
// 2. 同步到CRM系统
crmClient.syncCustomerStatus(customer);
// 3. 同步到订单系统
orderClient.syncCustomerStatus(customer);
// 4. 同步到风控系统
riskClient.syncCustomerStatus(customer);
log.info("客户 {} 冻结状态已同步", customerId);
}
}
我的建议:事件驱动同步特别适合「业务语义明确」的场景。比如订单状态变更、客户审核通过、合同签署完成。我在做供应链主数据项目时,就用事件驱动来处理「供应商资质变更」——资质一更新,采购、财务、质检三个系统同时收到通知。
4.4 API 调用同步
API 调用同步是最直接的方式。源系统主动调用目标系统的接口,把数据传过去。
这种方式通常用于「按需同步」。比如用户在前台修改了个人信息,点击保存后,前端直接调用后端接口,后端同步更新主数据平台。
// API 调用同步示例(RESTful)
POST /api/v1/sync/customer
Content-Type: application/json
{
"customerId": "CUST2024001",
"customerName": "张三",
"phone": "13800138000",
"syncType": "UPDATE",
"timestamp": "2024-01-15T10:30:00Z"
}
// 目标系统响应
HTTP/1.1 200 OK
{
"code": "SUCCESS",
"message": "同步成功",
"syncId": "SYNC202401151030001"
}
避坑指南:API 同步最怕「超时」和「重试风暴」。我曾经遇到过一个场景:下游系统响应慢,上游不断重试,结果把下游打挂了。后来我加了「熔断」和「限流」——如果连续 5 次超时,直接熔断 30 秒,同时把请求转存到消息队列异步处理。
4.5 消息队列触发
消息队列触发,本质上是一种「异步解耦」的同步方式。源系统把数据变更发布到消息队列,目标系统从队列里消费。
常用的消息中间件有 Kafka、RocketMQ、RabbitMQ。我个人偏爱 Kafka,吞吐量高,持久化好,适合主数据同步这种「不能丢消息」的场景。
// 消息队列生产者(源系统)
public void publishCustomerChange(Customer customer) {
String message = JSON.toJSONString(customer);
kafkaTemplate.send("topic_customer_sync", customer.getId(), message);
log.info("客户变更消息已发送: {}", customer.getId());
}
// 消息队列消费者(目标系统)
@KafkaListener(topics = "topic_customer_sync", groupId = "customer-sync-group")
public void consumeCustomerChange(String message) {
Customer customer = JSON.parseObject(message, Customer.class);
// 1. 幂等性检查(防止重复消费)
if (idempotentService.isProcessed(customer.getId(), customer.getVersion())) {
log.info("消息已处理,跳过: {}", customer.getId());
return;
}
// 2. 执行同步
targetSystemClient.syncCustomer(customer);
// 3. 记录处理状态
idempotentService.markProcessed(customer.getId(), customer.getVersion());
}
核心要点:消息队列触发的最大优势是「削峰填谷」。源系统每秒发 1000 条变更,下游系统每秒只能处理 200 条,没关系——消息队列帮你缓冲,下游慢慢消费。我做过一个项目,双十一期间订单主数据同步就是靠 Kafka 扛住的,峰值 5000 TPS,下游稳稳的。
4.6 五种机制对比与选型建议
说了这么多,到底怎么选?我整理了一张对比表,你直接拿去用。
| 触发机制 | 实时性 | 数据一致性 | 实现复杂度 | 典型延迟 | 推荐场景 |
|---|---|---|---|---|---|
| 定时批量同步 | 低 | 最终一致 | 低 | 分钟~小时级 | 日结报表、数据仓库 |
| 实时增量(CDC) | 高 | 强一致 | 中 | 秒级 | 交易数据、库存同步 |
| 事件驱动同步 | 高 | 最终一致 | 中 | 秒级 | 业务状态变更、工作流 |
| API调用同步 | 中 | 强一致 | 低 | 毫秒~秒级 | 按需查询、低频接口 |
| 消息队列触发 | 高 | 最终一致 | 高 | 毫秒~秒级 | 高吞吐、削峰填谷 |
我的选型习惯:先看业务对实时性的要求。能等 1 小时以上的,用定时批量;需要秒级响应的,上 CDC 或事件驱动;如果下游系统扛不住突发流量,中间加一层消息队列。记住一个原则——能用简单的就别搞复杂,定时批量能解决的问题,别硬上 CDC。
4.7 组合使用:混合触发策略
实际项目中,很少只用一种触发机制。我常用的组合方式有两种:
- CDC + 消息队列:CDC 捕获变更,写入消息队列,下游系统从队列消费。既保证了实时性,又实现了削峰填谷。
- 定时批量 + API 补偿:日常用定时批量同步,如果发现数据不一致,通过 API 触发单条数据的补偿同步。
举个例子。我在做一个大型零售集团的主数据平台时,用了三层触发:
- CDC 实时捕获门店、商品、供应商的变更,推送到 Kafka
- Kafka 消费者把数据同步到各个业务系统
- 每天凌晨跑一次定时全量对账,发现差异后通过 API 触发补偿同步
这套组合拳跑了两年,数据一致率保持在 99.99% 以上。
最后提醒一句:不管用哪种触发机制,一定要加「监控」和「告警」。同步延迟超过阈值要报警,同步失败要自动重试,重试还失败就要人工介入。我见过太多项目,同步机制设计得花里胡哨,结果出了问题没人知道——数据差了半个月才发现,那才叫灾难。