logstash input-jdbc 分页性能优化
场景:
DB 数据全量同步 Elasticsearch 时,数据量过大时需要使用分页查询。在conf中配置jdbc_paging_enabled、jdbc_page_size这两个参数,input-jdbc插件会自动对SQL进行分页查询,分批同步数据。
问题:
项目的ES升级至5.6后,全量数据同步由java定时任务改为logstash。线上数据量比较大,优化索引后单个分页查询执行依旧很慢,需要几十秒。
原因在于input-jdbc插件的分页是使用临时表分页,每条分页SQL都是将全集查询出来当作临时表,再在临时表上分页查询。这样导致每次分页查询都要对主表进行一次全表扫描。
举个例子:
商品表(goods)有1000万条数据,每次同步1万条。input-jdbc插件执行的分页查询sql类似下面这样的。
select * from (select * from goods left join ...) as T limit 0,10000
解决方案
通过修改input-jdbc插件源码,支持子查询分页。修改后执行的查询sql类似下面这样的,这样每次分页只需要扫描主表中需要的数量。
select * from (select * from goods limit 0,10000 ) as T left join ...
效果
项目中单个分页查询都在2S内完成。并且优化后的插件由运维推广到了所有项目,一次有价值的优化。
优化内容:
源码
1、logstash-input-jdbc\lib\logstash\plugin_mixins\jdbc.rb中添加subquery_paging_enabled、sum_statement变量;(具体参见我修改的源码 Line100)
2、perform_query方法中优先判断是否进行子查询优化。(具体参见我修改的源码 Line255)
配置文件
1、添加两个配置项:
subquery_paging_enabled => "true"
sum_statement => "select count(*) as sum from goods"
2、优化statement SQL
主表改为分页子查询,添加:data_offset、:jdbc_page_size分页参数。例如:
statement => " select * from (select * from goods limit :data_offset , :jdbc_page_size ) as T left join ... "
开发参考
https://github.com/logstash-plugins/logstash-input-jdbc
https://github.com/jeremyevans/sequel
插件构建和安装
1、项目安装依赖(先去github下载源码)
bundle install
2、构建插件gem (在项目主目录下产生logstash-input-jdbc-4.3.5.gem文件)
gem build logstash-input-jdbc.gemspec
3、logstash卸载logstash-input-jdbc插件
/bin/logstash-plugin uninstall logstash-input-jdbc
4、logstash安装本地插件 (先将构建成功的gem文件拷贝至logstash主目录的trade_gem文件夹中)
/bin/logstash-plugin install --no-verify --local ../logstash-input-jdbc-4.3.5.gem