设想
最近研发侧在做数据导入的改造,改造的方式是将原来LOAD DATA的方式修改为INSERT插入的方式,主要基于如下两方面考虑:
1、修改成INSERT后能更方便地在程序侧控制写入的线程数,更好地保护后端DB,防止后端DB雪崩
2、减少大批量导入带来DB的延迟(单个LOAD拆分成多个INSERT,多线程同步)
另外监控DB大部分的load数据量都比较小,改成INSERT对导入延迟影响不会太大。
下面是对INSERT和LOAD DATA的方式做的简单的测试结果:
从上面的测试结果,批量INSERT 30000条数据只比LOAD DATA的方式慢了2倍多一点,如果将30000的数据拆分成1000一个批量INSERT的SQL,并发写入,耗时比单线程LOAD要有提升。
上线遇到的问题
上线后,平均耗时增长了很多,如下图所示:
并且深入去看,耗时比较高的都是跨IDC的机器(上海写深圳),而且高得离谱。
从上图中,可以看到,耗时比较大的都是上海的机器。
并且监控DB的主从延迟也有比较大的飙升,如下图:
奇怪,按照之前的计算评估,不应该会有那么大的延迟,而且同步延迟应该有缓解才对,不可能越来越厉害。
分析和解决问题
查看监控发现INSERT出现了暴增,如下图:
针对insert,改写成1000条一次的并发插入,正常也不应该有那么大的增长,怀疑是INSERT INTO的方式不是采用批量的方式,而是一条一条insert的方式。分析binlog发现执行方式如下:
BEGIN;
INSERT INTO TABLENAME VALUES('X','X','X');
INSERT INTO TABLENAME VALUES('X','X','X');
INSERT INTO TABLENAME VALUES('X','X','X');
.......
COMMIT;
这种方式和单个INSERT的方式性能差别其实并不算大,下面是简单的测试结果:
测试场景: 在跨机房场景下,延迟相对比较大,30ms左右,测试INSERT本身的性能
1、INSERT 单行
2、INSERT 单行多values
3、INSERT 多行(27行)
4、INSERT 多行(27行)在一个事务中
测试接入过如下图:
可以看出:
采用INSERT 多values的形式和插入单行差别不算大(主要是行数不多);
采用INSERT多行和把多个INSERT放到一个大事务中性能相差不大。
解决办法就很简单了,直接把大事务修改为INSERT多values的方式。
修改后,INSERT的量大幅度下降:
平均耗时也大幅下降:
同步延迟也逐渐恢复:
总结
在写入效率上,对比关系如下:
1、LOAD DATA效率最高
2、INSERT 多values次之
3、INSERT 多行大事务再次之
4、INSERT 单行最差