项目需要实现对MySQL数据的快速读取以及查询功能,查询包括对所有列的全文检索,单列的下拉过滤,搜索自动补全等。ElasticSearch能够满足所有的项目搜索需求。MySQL中共有6个view需要同步到ES,每个view都是join多张表,最大的view有一千多万条数据,每天有上万条的新增。每个view中均有updated_time列,每当数据更新时,updated_time会更新。以其中的project view为例,介绍如何做到MySQL与ElasticSearch之间的实时同步。
软件版本
Logstash: 7.9.3
ElasticSearch: 7.9.3
MySQL:AWS Aurora MySQL 5.7.12
input {
jdbc {
jdbc_driver_library => "/opt/mysql-connector-java-8.0.16.jar"
jdbc_driver_class => "com.mysql.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql://${JDBC_HOSTNAME}:${JDBC_PORT}/${DB_NAME}?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull"
jdbc_user => "${JDBC_USER}"
jdbc_password => "${JDBC_PASSWORD}"
jdbc_paging_enabled => true
tracking_column => "unix_ts_in_secs"
use_column_value => true
tracking_column_type => "numeric"
schedule => "*/1 * * * *"
record_last_run => true
last_run_metadata_path => "/mnt/lastrun/.project_last_run"
statement => "SELECT *, UNIX_TIMESTAMP(updated_time) AS unix_ts_in_secs FROM view_project WHERE (UNIX_TIMESTAMP(updated_time)) > :sql_last_value AND updated_time < NOW() ORDER BY updated_time asc"
}
}
filter {
mutate {
copy => { "id" => "[@metadata][_id]"}
remove_field => ["@version", "unix_ts_in_secs"]
}
}
output {
elasticsearch {
index => "view_project_idx"
document_id => "%{[@metadata][_id]}"
doc_as_upsert => true
action => "update"
hosts => "http://localhost:9200"
manage_template => true
template => "/opt/project-index-template.json"
template_name => "project-index-template"
template_overwrite => true
}
}
配置文件有三个部分组成,分别是输入,转换,输出。
定义数据从哪里来,也就是告诉Logstash如何获取数据。
对数据的转换,处理等在此处配置,Logstash已有多个filter plugin。
配置ElasticSearch地址,ES Index template等。
[2021-06-04T06:29:00,520][INFO ][logstash.inputs.jdbc ][main][cef0a724ea4fc07490bbe09f83d681572322bd71f3fbef06cacab4f1ba501378] (0.267778s) SELECT count(*) AS `count` FROM (SELECT *, UNIX_TIMESTAMP(updated_time) AS unix_ts_in_secs FROM view_project WHERE (UNIX_TIMESTAMP(updated_time)) > 0 AND updated_time < NOW() ORDER BY updated_time asc) AS `t1` LIMIT 1
[2021-06-04T06:29:01,503][INFO ][logstash.inputs.jdbc ][main][cef0a724ea4fc07490bbe09f83d681572322bd71f3fbef06cacab4f1ba501378] (0.980027s) SELECT * FROM (SELECT *, UNIX_TIMESTAMP(updated_time) AS unix_ts_in_secs FROM view_project WHERE (UNIX_TIMESTAMP(updated_time)) > 0 AND updated_time < NOW() ORDER BY updated_time asc) AS `t1` LIMIT 100000 OFFSET 0
SELECT * FROM (SELECT *, UNIX_TIMESTAMP(updated_time) AS unix_ts_in_secs FROM view_project WHERE (UNIX_TIMESTAMP(updated_time)) > 1622787508 AND updated_time < NOW() ORDER BY updated_time asc) AS `t1` LIMIT 100000 OFFSET 0
在实际使用过程中碰到一些问题,解决方案总结如下:
比如需要对其中的一列或者多列基于业务逻辑做分解,预处理等,可以通过filter ruby插件,直接写Ruby来实现。Logstash是基于Ruby写的,所以并不需要额外配置运行环境。
def filter(event)
service = event.to_hash
project_name = service["name"]
## your process logic
event.set("project_name ",project_name)
return [event]
end
数据同步会增加MySQL的负载,可能会对MySQL正常的数据读写造成影响,可以在MySQL clusters中增加一个专用的Readonly Node,这个Node的地理位置尽可能与Logstash,ElasticSearch在同一个机房,
除了设置schedule提供数据更新Job运行频率外,当MySQL数据更新时,需要确保表中的updated_time列更改为当前时间。如果updated_time设置不正确,比如早于sql_last_run,则Logstash将取不到此条数据。
schedule根据业务可以容易的delay来设置。所谓实时只要能满足业务需求就是实时。绝大部分不需要秒级更新。
在一个pipeline中配置type参数,可以在一个pipeline配置文件中支持多个view的同步。
Logstash通过时间戳或ID等监视MySQL中的新数据或者updated的数据,如果数据在MySQL中被删除,Logstash没有办法在ElasticSearch中将数据删除。两个办法处理:
Logstash官方jdbc input文档中支持秒表调度,通过rufus-scheduler来支持。如每隔10s执行一次同步任务,这对同步性要求非常高的数据是非常有用的。经测试这在服务器上运行时是可以的,但当在k8s上部署时,并不支持秒级调度,在ES论坛发了帖子Logstash input plugin jdbc schedule issue on k8s但未收到回应。
canal是阿里MySQL Binlog增量消费组件,相比Logstash执行SQL查询获得数据,canal是通过读取MySQL的binlog来获得数据更新,速度会比Logstash快,项目中没有使用canal有以下几个原因。