基于logstash6.3.2的同步MySQL数据到ElasticSearch

基于logstash6.3.2的同步MySQL数据到ElasticSearch

    • 背景
    • 环境搭建
    • 同步到es配置文件示例
    • 多节点部署配置一致性解决方案

背景

为了满足业务查询的需求,会把MySQL中的数据导入到elasticsearch中
基本方案是用canal来监听MySQL的binlog,然后把数据写入到kafka中
通过订阅kafka-topic来把插入or变更的数据写入到es中,用status字段来辨别逻辑删除
为了保证数据的完整性,怕出现异常情况导致数据丢失,丢失后不可感知且无法补偿
所以引用了一个MySQL同步elasticsearch的开源组件elasticsearch-jdbc
这个组件的调用一直是通过在服务器上添加crontab任务来调度的
领导给我的需求是想做成一个应用服务,能够加载多个任务(同步不同的表到索引),数据增量同步,并且能够实现scheduled定时调度
于是从github上下载了源码,观察学习了之后,重写了main方法,加载自己定义的配置文件,解析后定时的执行同步任务
刚开始我也以为可以完成需求任务,通过观察后发现应用占用的内存越来越大,经过两天的运行后日志显示程序一直在gc
再加上这个开源框架最近一次提交是三年前了,最高支持的版本也是2.X,所以此时决定寻找其它替代方案
经过各种调研后发现logstash是一个不错的工具,支持读取和输出的数据源非常多,很适合做为新的同步工具
执行同步任务时,每五分钟执行一次,同步最近10分钟更新的数据

环境搭建

logstash依赖jdk,下载安装jdk,配置JAVA_HOME到Path中
基于logstash6.3.2的同步MySQL数据到ElasticSearch_第1张图片
下载logstash 解压 logstash
基于logstash6.3.2的同步MySQL数据到ElasticSearch_第2张图片
解压后logstash目录结构
基于logstash6.3.2的同步MySQL数据到ElasticSearch_第3张图片
logstash 属于开箱即用型 直接执行测试 语句,启动logstash,输入 hello world
启动输入 helloworld
基于logstash6.3.2的同步MySQL数据到ElasticSearch_第4张图片

	logstash -e 'input { stdin { } } output { stdout {} }'
	hello world
	{
		"@version" => "1",
		"host" => "localhost",
		"@timestamp" => 2018-09-18T12:39:38.514Z,
		"message" => "hello world"
    } 

同步到es配置文件示例

logstash至少需要一个input、output两个配置,filter是对input读取到的文件加工处理
本例中input的数据源是jdbc
input {
	jdbc {
		jdbc_connection_string => "jdbc:mysql://ip:port/db_name"	数据库的连接地址
		jdbc_user => "root"				数据库用户名	
		jdbc_password => "root"			数据库密码
		jdbc_driver_library => "/usr/local/lib/mysql-connector-java-5.1.21.jar"	数据库驱动jar包,可以用相对路径也可以用绝对路径,为了与原组件解耦尽量放在外部
		jdbc_driver_class => "com.mysql.jdbc.Driver"	数据库驱动类
		jdbc_paging_enabled => "true"	是否开启分页,这个是需要打开的,防止同步一个非常大的表导致查询和导出数据量太大
		jdbc_page_size => "1000"		分页每页的大小
		use_column_value => true		使用追踪字段的值,用来记录
		tracking_column => "ts"			追踪哪个字段
		tracking_column_type => "timestamp"		被追踪的字段的类型
		record_last_run => true					记录最后一次运行的值
		last_run_metadata_path => "/usr/local/data/ts_last_value"	记录的值保存在哪里
		statement => "select id,DATE_FORMAT(ts, '%Y-%m-%d %H:%i:%s') as ts from user WHERE ts > TIMESTAMPADD(MINUTE,-490,:sql_last_value)"	因为被记录的值用的UTC+8,所以把取到的时间做减法
		schedule => "*/5 * * * *"	此任务的执行频率
		type => "user"		相当于给这个任务起一个名字
	}
}
filter {
	if[type] == "user" {	对type=user的数据进行过滤
		mutate {
			remove_field => [ "@timestamp" ]	移除字段,jdbc会加上@timestamp字段
			remove_field => [ "@version" ]		移除字段,jdbc会加上@version字段
		}
	}
}
output {
	if[type] == "user" {	对指定类型进行处理
		elasticsearch {		写入到elasticsearch中
		  hosts  => "ip:9200"	ip port 
		  index => "user"		写入到的索引,建议先手动创建好
		  document_type => "_doc"	es6后一个索引一个type,es7后要移除type
		  document_id => "%{id}"	id的生成规则
		  routing => "%{id}"		路由值用id
		  parent => "%{id}"			es6前有父子类型,需要一个parent的id
		}
	}
}

干净配置文件如下,建议表字段中用一个时间戳来记录,最后一次更新的时间,这样方便与增量更新,type字段可能会写入到es中

input {
    jdbc {
          jdbc_connection_string => "jdbc:mysql://ip:pror/db_name"
    	  jdbc_user => "user"
          jdbc_password => "user"
          jdbc_driver_library => "/usr/lib/mysql-connector-java-5.1.21.jar"
          jdbc_driver_class => "com.mysql.jdbc.Driver"
          jdbc_paging_enabled => "true"
          jdbc_page_size => "1000"
          use_column_value => true
          tracking_column => "ts"
          tracking_column_type => "timestamp"
          record_last_run => true
          last_run_metadata_path => "/usr/data/db_name_last_value"
    	  statement => "select id,DATE_FORMAT(ts, '%Y-%m-%d %H:%i:%s') as ts from user WHERE ts > TIMESTAMPADD(MINUTE,-490,:sql_last_value)"
          schedule => "*/5 * * * *"
          type => "user"
    }
}
filter {
    if[type] == "user" {
        mutate {
            remove_field => [ "@timestamp" ]
            remove_field => [ "@version" ]
        }
    }
}
output {
    if[type] == "user" {
		elasticsearch {
		  hosts  => "localhost:9200"
		  index => "user"
		  document_type => "_doc"
		  document_id => "%{id}"
		  routing => "%{id}"
		  parent => "%{id}"
		}
	}
}

多节点部署配置一致性解决方案

实际环境中,会有很多个logstash部署在不同的机器,每个logstash跑着很多个任务
所以如何管理logstash就成为一个问题,所有的配置必须统一保存起来,如何统一部署
多台机器间如何保证配置文件一样,经过思考后于是有了这个方案

  1. 创建一个git仓库,项目中有2个目录,bin目录存放启动脚本,conf下根据主机名放不同的配置文件
    bin
    	start.sh
    conf
    	host1
    		cron888.conf
    	host2
    		cron999.conf
    
  2. 每次有任务or新增机器,修改start文件,在对应的host文件夹下增加conf
  3. Jenkins上配置项目,每个logstash可以当做一个项目,用同一个git地址,输出到不同的机器位置,这样解决单点启动不用更新所有logstash
  4. 在shell中取到本机hostname,然后得到conf路径,启动logstash指定 -f conf/hostname/
  5. 每台机器logstash安装位置统一,可以在start中写死,启动脚本指定配置文件,data文件,log文件 在外部文件加 不破坏logstash原生结构
  6. 以上可以解决一个项目一个logstash的问题,如果两台机器同时跑同一个任务,那么每次修改还要修改2个文件
    这时候可以用另一个方式来解决,只写一份文件,然后在bin脚本中,根据hostname把配置文件复制到一个新的文件夹
    然后指定新的文件夹启动

你可能感兴趣的:(ELK)