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

posted on   荣锋亮  阅读(34)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用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

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示