logstash可以將不同數據源,例如日誌、文件、或jdbc等,同步到ElasticSearch中,本文利用logstash實現mysql數據庫表之間的數據。(實例:數據庫DB1中的表A有添加或者修改,數據庫DB2中的表B也會自動同步)
一、準備:
數據源輸入使用logstash中自帶的logstash-input-jdbc
,無需額外安裝,官網使用說明地址。
數據源輸出需要使用logstash-output-jdbc
,但是在loastash官網中output plugins列表中並沒有相關插件,需要額外安裝,使用說明在Github地址。
安裝logstash
將logstash下載後,放到/opt/elastic/
目錄下,並將logstash目錄重命名爲logstash-test
。
安裝logstash-output-jdbc
,在/opt/elastic/logstash-test
目錄下執行:
bin/logstash-plugin install logstash-output-jdbc
安裝成功:
二、數據庫表
在數據庫DB1中創建表A,並添加數據如下:
在數據庫DB2中創建表B,表結構與A一致,暫不添加數據:
三、logstash配置文件
logstash配置文件中必須包含兩個元素input
和output
,分別是數據來源的配置和數據輸出的配置。還有一個可選項filter
,用來處理數據源和數據輸出的之間的適配,例如,需要將某個字段的值10以後再輸出,這個10的動作就應該寫在filter
模塊;還有數據源和數據輸出字段的編碼不同,日期類型不同等情況的處理。(由於本文中A表和B表中數據結構都是一樣的,只是實現簡單的數據同步,暫時用不到filter
)
同步配置文件如下:後面對每個部分進行解釋。
input {
jdbc {
jdbc_connection_string => "jdbc:mysql://IP:3306/DB1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true"
jdbc_user => "expert"
jdbc_password => "123456"
jdbc_driver_library => "/opt/elastic/logstash-test/mysql-connector-java-5.1.6.jar"
jdbc_driver_class => "com.mysql.jdbc.Driver"
statement => "SELECT title,description FROM A"
}
}
filter {}
output {
jdbc {
connection_string => "jdbc:mysql://IP:3306/DB2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true"
username => "root"
password => "root123"
driver_jar_path => "/opt/elastic/logstash-test/mysql-connector-java-5.1.6.jar"
driver_class => "com.mysql.jdbc.Driver"
statement => ["insert into B (title,description) values (?,?)","[title]","[description]"]
}
stdout {
codec => json_lines
}
}
input
配置
input的配置中有很多強大的功能,詳細使用見官網地址,此處只介紹上面涉及到的參數:
- jdbc_connection_string :數據庫連接配置
- jdbc_user :用戶名(連接DB1的用戶名)
- jdbc_password:密碼(連接DB1的密碼)
- jdbc_driver_library:數據驅動的jar位置
- jdbc_driver_class :數據庫驅動類名(類似jdbc中的Class.forName(“com.mysql.jdbc.Driver”))
- statement :查詢數據源的sql語句(output中就是將此處的查詢結果insert到數據庫DB2中)
output
配置
由於logstash-output-jdbc
是額外擴展的output插件,在配置參數的寫法上也略有不同,比如:所有的參數前面都沒有jdbc_前綴
- connection_string :數據庫連接配置
- username :用戶名(連接DB2的用戶名
- password :密碼(連接DB2的密碼)
- driver_jar_path :與input參數中的
jdbc_driver_library
一致 - driver_class :與input參數中的
jdbc_driver_class
一致 - statement :向DB2中添加數據的insert語句。
四、執行啓動命令
將上述的配置文件命令爲logstash_default.conf
,放在logstash-test/conf
文件夾下,在logstash-test
下執行:
logstash啓動命令:
./bin/logstash -f /opt/elastic/logstash-test/config/logstash_default.conf --path.data=/opt/elastic/logstash-test/test
啓動後發現報如下異常:
java.lang.IllegalAccessError: tried to access class com.mysql.jdbc.EscapeProcessor from class com.mysql.jdbc.ConnectionImpl
java.lang.IllegalAccessError: com/mysql/jdbc/EscapeProcessor
遺憾的是目前沒有找到爲什麼會報這個異常,不過換了一種驅動的配置方式,這個異常就消失了。另外一種指定驅動jar包的方式也是官網給出的方式如下:
下面結合本例中給出解決辦法。
啓動異常處理方式:
在logstash目錄下,創建目錄vendor/jar/jdbc
(/opt/elastic/logstash-test/vendor/jar/jdbc),將驅動jar包放入該路徑下。
將配置文件中的driver_jar_path
註釋掉,
重新執行logstash啓動命令
./bin/logstash -f /opt/elastic/logstash-test/config/logstash_default.conf --path.data=/opt/elastic/logstash-test/test
執行結果:
檢查DB2中的表B:數據已經全部同步過去了。
五、定時自動同步數據
按照上面的過程,能實現執行logstash命令以後,DB1中的表A和DB2中的表B數據同步,但是如果後續表A中的數據有新增或者修改,還需要再去啓動logstash。logstash提供了一種定時任務的方式,定期去檢查表A中的數據是否有變化,根據表A的最後修改時間(LastUpdateDate)將表A中新增和修改的數據修改新增到表B中。
將配置文件做如下修改:
input {
jdbc {
jdbc_connection_string => "jdbc:mysql://IP:3306/DB1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true"
jdbc_user => "expert"
jdbc_password => "123456"
jdbc_driver_library => "/opt/elastic/logstash-test/mysql-connector-java-5.1.6.jar"
jdbc_driver_class => "com.mysql.jdbc.Driver"
statement => "SELECT title,description FROM A"
schedule => "* * * * *"
record_last_run => true
use_column_value => true
tracking_column => "LastUpdateDate"
tracking_column_type => "timestamp"
last_run_metadata_path => "/opt/elastic/logstash-test/last_record/logstash_default_last_time"
clean_run => false
lowercase_column_names => false
}
}
filter {}
output {
jdbc {
connection_string => "jdbc:mysql://IP:3306/DB2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true"
username => "root"
password => "root123"
driver_class => "com.mysql.jdbc.Driver"
statement => ["insert into B (title,description) values(?,?) on duplicate key update title=values(title),description=values(description)","[title]","[description]"]
}
stdout {
codec => json_lines
}
}
input
中新增的參數:
schedule => "* * * * *"
:每分鐘檢查一次- ` record_last_run => true``:是否記錄上一下執行的時間
- ` use_column_value => true``:是否使用數據源的字段
- `tracking_column => “LastUpdateDate”``:數據源的最後修改時間字段
- `tracking_column_type => “timestamp”``:字段類型
- ` last_run_metadata_path => “/opt/elastic/logstash-test/last_record/logstash_default_last_time”``:存放最後修改時間的文件位置
- ` clean_run => false``:這個參數表示你在開啓Logstash同步數據時需不需要clean掉上次的記錄
lowercase_column_names => false
讀取字段時是否區分大小寫
output
新增的參數:
stdout {
codec => json_lines
}
stdout
爲可選字段,將輸出數據的方式加一種,stdout
可以把input中statement 的select結果轉爲json字符串打印到logstash的log中,便於追蹤檢查哪些數據被更新了。
六、補充
output中的statement
修改:
將表B中的title字段設置爲唯一約束,將statement改爲如下。即可實現如果title相同的時候,只修改記錄,而不是新增。唯一約束可以根據實際需求去設置。
["insert into B (title,description) values(?,?) on duplicate key update title=values(title),description=values(description)","[title]","[description]"]
表A修改完只有需要1分鐘以後才能在表B中看到同步效果,因爲定時任務設置的每分鐘執行一次。