数据采集的优化之旅

最近在做一个数据同步的小工具,具体场景就是将某个数据库中的几张表,同步到另一个数据库中。

没有采取抓binlog的方式去做,一是解析binlog不是一个很简单的工作。二是对延迟的要求不是很高,2min是可接受范围。所以采取扫全表,全量同步的方式去做。

简单的业务代码写完,发现同步一张9w条数据的表,竟然TMD要近10分钟。立马检查代码的问题。首先要确认主要耗时的是SQL执行、还是在序列化阶段的耗时,通过Guava库中的stopwatch,很好的分析了各个阶段的耗时。惊奇的发现一个简单的SELECT查询,竟然扫了全表。立马检查表结果,发现select查询中的column并没有建立索引。这是踩的第一个坑。

解决了索引问题之后,测试之后,扫一张9w条数据的表耗时接近2min,然而我们线上环境,一张表的数据接近90w条,那么单线程扫一遍,耗时也将接近20min,同样是一个不可接受的时间。

自然而然地想到采用了多线程的方式去完成同步任务。首先,将数据分片,select min(id), max(id) from table;查询到主键最大、最小ID之后,将ID range到不同的线程去执行业务逻辑。

测试之后,发现有的线程耗时很低,有的线程耗时远远超过其他线程几个数量级的耗时。这时,多线程的耗时,取决于最长耗时。立马想到,主键id分布不是均匀的。

为了保证绝对的平均任务数,首先select count(*) from table;得到表中数据的数量之后,num = count / threadNums;相当于每个线程执行的row行数是相同的。select max(id) from (select id from table where id >= min order by id limit num) as t;这条SQL就拿到了每个线程的最大ID值。select * from table where id >= min and id < max;这样就得到了每个线程的数据。

测试之后,任务耗时接近9s,属于可接受范围。

做的一个小工具,也是学到了不少知识。还有很多值得优化的地方。