内容来自: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