• 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

  • 相关阅读:
    Springboot Endpoint之二:Endpoint源码剖析
    Linux进程被杀掉(OOM killer),查看系统日志
    docker常用命令详解
    micrometer自定义metrics
    使有prometheus监控redis,mongodb,nginx,mysql,jmx
    Grafana+Prometheus打造springboot监控平台
    Grafana介绍
    Prometheus介绍
    Groovy与Java集成常见的坑
    ES之1:基本概念及原理
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/15859650.html
Copyright © 2020-2023  润新知