项目背景:公司对现有系统做重构,新系统上线前需要将老系统的全量数据迁移到新的系统,其中有10几张表的数据有1千多万的数据量需要全量迁移过来,但是新老系统的表结构不是一样的,所以我们需要将老系统的数据全部转化为新系统的数据结构
过程:
项目开始时是从简单的版本开始做,并没有考虑速度问题,按照简单的单线程开发,数据分页读取然后做数据转化后通过mybatis批量写入数据库。第一版只是完成业务逻辑上的开发,保证数据不会有误。完成之后开始第一次测试,测试过程中发现数据迁移很慢,读取一次需要几十秒,批量写入时每次写入1000条,批量插入,发现写入1000条居然要等上半个小时,这个肯定是不行的。于是对写入做了第一次整改,将写入由一次写入1000条改为每次写入100条,测试后发现写入速度快了,由开始的半个小时优化了20-30秒就能完成1000条数据的写入。接着就开始了多线程的操作,将系统有开始的单个线程读取转化写入过程改成了多线程模式,用一个线程来负责每次从数据库中读取1000条,然后把数据分给多线程去处理这1000天数据的转化并写入。第一次尝试我使用了10个线程,结果测试一跑起来傻眼了,发现数据写库由之前的单线程每1000条20-30秒变成100秒以上了,线程越多写入的性能还越来越慢了。这样肯定是不行的,于是经过高手的请教,使用单条插入,批量commit。这里的多线程写入变慢是因为在批量写入的时候需要大量的数据库临时空间去装载大sql,并发越多,临时空间就越不够用,这也就出现了线程越多,写入越慢的情况了。经过指点后通过单条提交,分批commit之后数据写入性能有了质的飞越,1000条写入耗时只要7左右秒就能完成,而且多线程也不会影响速度。于是开启了第二版的飞越,测试一次之后在写入到了200万之后速度满了下来,而且是越来越慢,通过日期观察写的数据没有问题,那就只有读的问题了。通过日志分析发现,当数据读取到了100万以后,后面的数据读取越来越慢了,想起了数据库分页读取后面的页数需要翻过前面一堆数据之后才能读到,于是我们有队数据读取表做了修改,加入了一个自增顺序的id,这样我们就不用分页读取了,每次读取完以后取最后一个数据id,后面的数据从这个之后读取就不需要分页了。每次读取的速度都是在一个稳定的值。这样之后我们的性能提升到了只需要几个小时就能完成了。但是老板对这个速度还不是很满意,于是我们引入了多机器,其中一个机器负责读取id,然后发送到mq中,其他机器从mq中读取id,根据id来获取数据并转化落库,整个迁移过程就在1小时左右完成了。
心得: 这次全量数据的迁移,学到了2个点,第一点:数据插入并不是批量比单个快,批量sql需要消耗临时空间,sql越长需要的空间越大。单条插入通过手动控制事物提交到达的性能效果比批量的好;第二点数据读取分页到后面会越来越慢