场景:
DB 数据全量同步 Elasticsearch 时,数据量过大时需要使用分页查询。在conf中配置jdbc_paging_enabled、jdbc_page_size这两个参数,input-jdbc插件会自动对SQL进行分页查询,分批同步数据。
问题:
logstash导入mysql上亿级别数据的效率问题
项目的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)
jdbc.rb源码
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-pl ... -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
PS:这是在公司遇到的一个实际问题,选择的方案是修改源码,大家可以看一下,可以讨论下还可以有其他优化方法或者解决办法没?
DB 数据全量同步 Elasticsearch 时,数据量过大时需要使用分页查询。在conf中配置jdbc_paging_enabled、jdbc_page_size这两个参数,input-jdbc插件会自动对SQL进行分页查询,分批同步数据。
问题:
logstash导入mysql上亿级别数据的效率问题
项目的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)
jdbc.rb源码
- 配置文件
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-pl ... -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
PS:这是在公司遇到的一个实际问题,选择的方案是修改源码,大家可以看一下,可以讨论下还可以有其他优化方法或者解决办法没?
1 个回复
rochy - rochy_he
赞同来自:
每一次同步结束后,批量修改同步状态;
查询的时候,限制修改时间和同步状态即可;
这样每一次只需要查询第一页即可