在实践中我总结出了以下几种方式。
1同步双写
这是一种最为简单的方式,在将数据写到mysql时,同时将数据写到ES。
优点:
业务逻辑简单。
缺点:
1、 硬编码,有需要写入mysql的地方都需要添加写入ES的代码;
2、 业务强耦合;
3、 存在双写失败丢数据风险;
4、 性能较差:本来mysql的性能不是很高,再加一个ES,系统的性能必然会下降。
附:
上面说的双写失败风险,包括以下几种:
1) ES系统不可用;
2) 程序和ES之间的网络故障;
3) 程序重启,导致系统来不及写入ES等。
针对这种情况,有数据强一致性要求的,就必须双写放到事物中来处理,而一旦用上事物,则性能下降更加明显。
2异步双写(MQ方式)
针对上面同步的性能和丢数据问题,可以考虑引入MQ,从而形成了上图的方案。
由于MQ的性能基本比mysql高出一个数量级,所以性能可以得到显著的提高。
优点:
性能高;
不存在丢数据问题。
缺点:
还存在硬编码、业务强耦合等问题;
系统中增加了mq的代码,复杂度增加;
可能存在时延问题,程序的写入性能提高了,但是由于MQ的消费可能由于网络或其它原因导致用户写入的数据不一定可以马上看到。
3异步双写(Worker方式)
上面2中方案中都存在硬编码问题,也就是有任何对mysq进行增删改查的地方要么植入ES代码,要么替换为MQ代码,代码的侵入性太强,若是实时要求不高的情况下,可以考虑用定时器来处理,具体步骤如下:
1) 数据库的相关表中增加一个字段为timestamp的字段,任何crud操作都会导致该字段的时间发生变化;
2) 原来程序中的crud操作不做任何变化;
3) 增加一个定时器程序(京东内部叫Worker),让该程序按一定的时间周期扫描指定的表,把该时间段内发生变化的数据提取出来;
4) 逐条写入到ES中。
优点:
不改变原来代码,没有侵入性、没有硬编码;
没有业务强耦合;
不改变原来程序的性能;
Worker代码编写简单不需要考虑增删改查。
缺点:
时效性较差,由于定时器工作周期不可能设在秒级,所以实时性没有上面2中好;
对数据库有一定的轮询压力,一种改进方法是将轮询放到压力不大的重库上。
4Binlog 同步方式:
上面三种方案要不有代码侵入、要不有硬编码、要不有时延,那么有没有一种更好的方法?答案就是利用mysql的binlog!
具体步骤如下:
1) 读取mysql的binlog日志,获取指定表的日志信息;
2) 将读取的信息转为MQ;
3) 编写一个MQ消费程序;
4) 不断消费MQ,每消费完一条消息,将消息写入到ES中。
优点:
没有代码侵入、没有硬编码;
原有系统不需要任何变化,没有感知;
性能高; 业务解耦,不需要关注原来系统的业务逻辑。
缺点:
构建Binlog系统复杂;
也像方案二,存在MQ延时的风险。