30

MYSQL logstash 同步数据到es的几种方案对比以及每种方案数据丢失原因分析。

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAxNTIzNDYxMQ%3D%3D&%3Bmid=2247484165&%3Bidx=1&%3Bsn=f4ff91d05cc865521eec31e4252fef57
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

MYSQL logstash 同步增量数据到ES

最近一段时间,在使用mysql通过logstash-jdbc同步数据到es,但是总是会有一定程度数据丢失。logstash-jdbc无非是通过sql遍历数据表的所有数据,然后同步到es。

对于表里面的所有字段都需要查出来然后同步到es中去。本地测试数据表结构如下:

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) CHARACTER SET utf8 NOT NULL,
  `upnum` decimal(20,0) NOT NULL,
  `last_update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `last_update_time` (`last_update_time`)
) ENGINE=InnoDB AUTO_INCREMENT=25094590 DEFAULT CHARSET=latin1;

数据同步脚本分为全量同步与增量同步。

全量同步sql如下:

select id,username,upnum,last_update_time from user where id>{$MIN_ID} and id<{$MAX_ID}

通过shell的方式在每次执行之前替换脚本里面的最大最小ID,每批查询10万ID数据。

增量同步sql:

select id,username,upnum,last_update_time from user where last_update_time>=:last_sql_value

增量同步,每次记录last_update_time最后时间,然后每次查询查询上一次时间之后的数据。最终结果,增量丢数据!!!

方案1

每次查询last_update_time 时间大于等于上一次数据时间的数据,分页查询。

select * from (select id,username,upnum,last_update_time from user where last_update_time>=:last_sql_val) as a limit 10000 offset 10000

这种方案会涉及到几个问题:

1)  相同last_update_time的数据导致数据最终查询出来的顺序有可能不确定。导致有可能在查第一页的时候,数据id=100001的数据在第二页,但是当分页到第二页的时候,id=100001的数据又在第一页了,从而导致这条数据不会被同步。

2)last_update_time 使用的是ON UPDATE CURRENT_TIMESTAMP,时间是更新语句执行的时间。但是只有当更新语句事务提交的时候才会被查询到。这种情况,当一条数据在 2020-09-30 00:00:00 执行update语句,但是因为事务提交时间比较长,到 2020-09-30 00:00:03 的时候才提交。这个时候在 2020-09-30 00:00:03 的时候,同步sql有可能已经是翻页翻到时间为 2020-09-30 00:00:02 的数据了,因此导致数据丢失。

3)主从同步导致数据分页查询不到。一般都用es了,都会有主从同步,而es数据同步也是在从库中读取数据同步的。因此主从同步的这个时间延时也会导致数据查询不正确。

方案2

不分页查询,避免分页造成的数据丢失。不分页查询的话,需要将数据查询的时间区间减少。调整logstatsh调度频率,每5秒钟执行一次,每次查询数据10万条。

select id,username,upnum,last_update_time from user where last_update_time>=:last_sql_val limit 10000

这个方案的问题:

1) 查询条件是大于等于。因此,当在last_update_time 临界时间 2020-09-30 00:00:00 有10万条数据(比如添加字段初始化数据),就会让整个数据更新脚本 原地踏步

2) 主从同步,已经事务提交延迟的问题依然存在

方案3

使用两个脚本同步数据,新增一个5分钟延迟的同步脚本,减少数据丢失的概率

select id,username,upnum,last_update_time from user where last_update_time>:last_sql_val limit 10000
select id,username,upnum,last_update_time from user where last_update_time>:last_sql_val and last_update_time<DATE_SUB(NOW(),INTERVAL 5 MINUTE) limit 30000

这个方案的问题:

1) 不会出现原地踏步的情况,但是在同时间的数据可能会有数据丢失

2) 可以减少因为主从同步,事务提交延迟的数据丢失,但是没办法确保100%不丢失

方案4

使用binlog的方式同步到es。但是对于现有的历史数据,还是需要通过遍历数据表的方式进行同步。

结论

以上内容是目前在工作中mysql同步数据到es的几个方案。按目前网络中大部分文章,都是通过logstash进行数据同步。

但是请注意,logstash的方案是有可能造成成数据丢失的。而这种数据丢失对于insert操作而言,还是可以确定是否有没有丢数据。毕竟对比一下两边的数据量就可以了。

但是,对于update操作呢,怎么确定是否全部同步过去了呢。假如您有一个几千万用户单据信息,而且还是关于钱的,怎么样确定每次更新都正确无误的同步到了es呢?

基于上面的分析,最靠谱的办法,还是通过binlog的方式同步es。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK