Building a Sync Engin
内容来自:https://www.grouparoo.com/blog/building-a-sync-engine
内容主要介绍了如何开发一个同步引擎,没有太多高深的,主要是基于了变动的时间戳以及水印算法
简单说明
- 预备
添加水印列,当然对于不同的数据库处理方式会不一样的,有些可能需要通过触发器
ALTER TABLE users ADD COLUMN mysql_updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
- 简单算法模式
基于水印列判断
const watermark = await getWatermark();
let rows;
if (!watermark) {
// first time we've ever sync'd - get all rows
rows = await User.findAll();
} else {
rows = await User.findAll({
// otherwise, use watermark
where: {
updatedAt: {
[Op.gt]: watermark, // WHERE updatedAt > {watermark}
},
},
order: [["updatedAt", "ASC"]],
});
}
if (rows && rows.length > 0) {
for (const row of rows) {
await processRow(row);
}
const newWatermark = new Date(); // set to now
await setWatermark(newWatermark); // for next time
}
return true; // done!
问题:数据只能增长,数据同步的时候可能会变动,数据可能会重复处理,时间可能会不一致(服务器时间戳,,,)
修复,时间问题,基于db 时间
const watermark = await getWatermark();
let rows;
if (!watermark) {
// first time we've ever sync'd - get all rows
rows = await User.findAll();
} else {
rows = await User.findAll({
// otherwise, use watermark
where: {
updatedAt: {
[Op.gte]: watermark, // WHERE updatedAt >= {watermark}
},
},
order: [["updatedAt", "ASC"]],
});
}
if (rows && rows.length > 0) {
for (const row of rows) {
await processRow(row);
}
const newWatermark = rows[rows.length - 1].updatedAt;
await setWatermark(newWatermark); // for next time
}
return true; // done!
- 批处理
基于偏移以及分页
// using node and sequelize
const saved = await getWatermark();
const watermark = saved ? saved.watermark : null;
const oldOffset = saved ? saved.offset || 0 : null;
const sqlOptions = {
limit: batchSize,
offset: oldOffset,
order: [["updatedAt", "ASC"]],
};
if (watermark) {
sqlOptions.where = {
updatedAt: {
[Op.gte]: watermark, // WHERE updatedAt >= {watermark}
},
};
}
const rows = await User.findAll(sqlOptions);
if (!rows || rows.length === 0) {
return true;
} else {
for (const row of rows) {
await processRow(row);
}
const done = rows.length < batchSize; // is there more to be done?
const lastTime = rows[rows.length - 1].updatedAt.getTime();
let newOffset = 0;
if (!done && watermark === lastTime) {
// the last one was the same as the first, need to use offset
newOffset = oldOffset + batchSize;
}
await setWatermark({ watermark: lastTime, offset: newOffset });
return done;
}
说明
以上方法还是值得参考学习的,尽管有时我们是不能直接使用的,但是还是很不错的实践,cdc,except 有时可能会是一个其他的选择
参考资
https://www.grouparoo.com/blog/building-a-sync-engine
https://github.com/grouparoo/sync-engine-example
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2021-02-01 cube.js 上下文实践的一些说明
2020-02-01 Performance Profiling Zeebe
2020-02-01 bazel 学习一 简单java 项目运行
2020-02-01 一个好用node http keeplive agnet
2019-02-01 Benchmarking Zeebe: An Intro to How Zeebe Scales Horizontally and How We Measure It
2019-02-01 What's New In Zeebe: Scaling Zeebe, New Client APIs, Faster Requests, Timestamps, NodeJS Client, and Default Topic is Back!
2019-02-01 Architecture options to run a workflow engine