每天从外部系统同步百万数据,用什么方案?

4,904 阅读4分钟

前言

最近接到一个需求,每天定时从一个外部的第三方BI系统中拉取数据到我们的系统中,使得两边系统数据保持一致。本想以接口的形式,就是对方系统提供一个查询接口,我们每天查询一次,然后落到我们的系统中。但是后来开会讨论的时候得知对方系统数据量比较多,接近百万,未来数据量还可能更大,一个接口传输不了这么多数据。所以后来讨论了一下,想了几个方案,和小伙伴们分享一下。

方案1:期初导入,后期接口拉取每天增量数据

其实也就期初的时候数据量比较大,对方可以直接导入一个Excel给我们,我们写个导入程序导进来就OK了,后续每天的增量数据其实不多,也就几百几千条而已,一个接口就搞定了。但问题是对方是个BI系统,他们的数据也是每天从他们公司内部的其他系统全量拉过去的,没有更新时间这些字段,也没有唯一标识,他们区分不出来什么是增量数据。所以只能每天都给全量的数据给我们,然后我们把表里数据都清了再重新写入一遍,这样才能保证两边系统数据是一致的。那么又回到了一开始的问题,一个接口搞不定这么大量的数据。

方案2:以文件的形式从OSS中获取

对方系统每天导出数据到文件中,可以是excel、csv等格式,然后将文件上传到OSS中。然后再以接口或者什么其他形式将文件链接给到我们系统,我们系统再从OSS中下载文件进行导入。如果单个文件还是太大,可以分成多个文件,我们多次下载导入。如果数据量再增长几倍,这种方案也是不错的选择。不过这种就稍微复杂些,还要考虑文件上传失败下载失败的情况,再说了数据也不是特别大也没达到大几百万、上千万这个量级,最后我们也没有采用这个方案。

方案3:直接开放数据库地址

这种方案就是对方数据库可以通过公网访问,并且愿意将地址、账号、密码给我们,那我们直接访问他们对应的表,每天灌进我们的系统中就OK了。但是这种方式太不安全了,风险太高,估计大多数人都不愿意这么干。

方案4:分页获取

其实核心就是解决一个接口承载不了那么多数据的问题,那就拆呗,将全量查询的接口改成分页查询,一次查询少量数据,多次查询即可。如果串行执行太慢,可以改用多线程并发处理。如果因为网络问题导致获取某一页数据失败了,重试即可,重试的方式也有多种,失败了立即重试,或者记录下来,隔一段时间后重试。需要注意的一点就是,对方BI系统数据也是每天定时更新的,所以我们定时拉取他们数据要在他们更新完成后再进行,否则会出现数据混乱的情况。

以下是我写的大概的代码,第一步清空表数据,因为是清空再同步,数据丢了找不回来也没事,再同步一遍呗,所以这里用truncate要比delete效率高。先调用一遍查询接口获取数据的页数,然后根据页数进行循环处理,这里采用线程池提交线程并发处理的方式提高效率。同时通过Semaphore限制最多20个线程同时执行,也是担心请求太多了对方系统扛不住。

public List<Pair<Integer, String>> syncData() {
    // todo 先清空表数据

    // 先调一次拿到总页数
    int pageNow = 1;
    Info.Page page = process(pageNow);
    log.info("BI数据数量: {}", page));
    int pageCount = page.getPageCount();
    CountDownLatch countDownLatch = new CountDownLatch(pageCount - 1);

    List<Pair<Integer, String>> errMsgList = new CopyOnWriteArrayList<>();

    TimeInterval timer = DateUtil.timer();
    AtomicDouble totalTime = new AtomicDouble();
    Semaphore concurrentThreshold = new Semaphore(20);
    while (++pageNow <= pageCount) {
        int finalPageNow = pageNow;
        concurrentThreshold.acquire();
        executor.execute(() -> {
            try {
                // 处理业务逻辑
                TimeInterval pageTimer = DateUtil.timer();
                process(finalPageNow);
                double time = pageTimer.intervalMs() / 1000.0d;
                log.info("第{}页执行结束,耗时{}秒", finalPageNow, time);
                totalTime.addAndGet(time);
            } catch (Exception e) {
                log.error("第{}页处理业务逻辑异常", finalPageNow, e);
                errMsgList.add(Pair.of(finalPageNow, e.getMessage()));
            } finally {
                concurrentThreshold.release();
                countDownLatch.countDown();
            }
        });
    }

    countDownLatch.await();

    log.info("拉取BI数据总耗时: {}秒", totalTime.get());
    log.info("拉取BI数据耗时: {}秒", timer.intervalMs() / 1000.00);
    log.info("拉取BI数据,错误数据: {}", errMsgList);
    return errMsgList;
}

总结

以上就是对从外部系统同步百万数据的一些方案的介绍,希望小伙伴们看完能有一些收获!

学习更多编程知识,欢迎关注公众号『 R o b o d 』: