Skip to content

面试速答(先看这里)

**一句话结论:**在业务中,我们通常需要把数据库中的数据变更同步到ES中,那么如何保证数据库和ES的一致性呢?

60秒标准回答:

在业务中,我们通常需要把数据库中的数据变更同步到ES中,那么如何保证数据库和ES的一致性呢?通常有以下几种做法

在代码中,对数据库和ES进行双写,并且先操作本地数据库,后操作ES,而且还需要把两个操作放到一个事务中

在以上逻辑中,如果写数据库成功,写ES失败,那么事务会回滚

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **要点1:**在以上逻辑中,如果写数据库成功,写ES失败,那么事务会回滚。
  • **要点2:**如果写数据库成功,写ES超时,实际上ES操作成功,这时候数据库会回滚,导致数据不一致。
  • **要点3:**这个方案的好处就是简单,容易实现。
  • **要点4:**缺点首先是需要改代码,有侵入性,还有就是存在不一致的情况。
  • **要点5:**在应用中,如果我要更新数据库了,那么就抛一个消息出去,然后数据库和ES各自有一个监听者,监听消息之后各自去做数据变更,如果失败了就基于消息的重试在重新执行。

**记忆锚点:**binlog → 的方式来保证最终一致性 → 监听binlog同步 → 两个操作放到一个事务 → 败了就基于消息的重试 → 不太会关心引入新框架

关键取舍:

  • 这个方案的好处是用了MQ,起到了解耦的作用,而且还做到了异步,提升了整体性能。
  • 这个方案优点是没有侵入性,数据库的写操作处不需要改代码。
  • 缺点是实时性很差,并且轮询可能存在性能问题、效率问题以及给数据库带来压力。

易错提醒:

  • 通常有以下几种做法: 在代码中,对数据库和ES进行双写,并且先操作本地数据库,后操作ES,而且还需要把两个操作放到一个事务中: 在以上逻辑中,如果写数据库成功,写ES失败,那么事务会回滚。
  • 在应用中,如果我要更新数据库了,那么就抛一个消息出去,然后数据库和ES各自有一个监听者,监听消息之后各自去做数据变更,如果失败了就基于消息的重试在重新执行。

加分表达:

  • 如果写数据库成功,写ES超时,实际上ES操作成功,这时候数据库会回滚,导致数据不一致。

追问准备:

  • 围绕「binlog」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「的方式来保证最终一致性」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「监听binlog同步」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

在业务中,我们通常需要把数据库中的数据变更同步到ES中,那么如何保证数据库和ES的一致性呢?通常有以下几种做法:

双写 ​

在代码中,对数据库和ES进行双写,并且先操作本地数据库,后操作ES,而且还需要把两个操作放到一个事务中:

plain
@Transactional(rollbackFor = Exception.class)
public void update(OrderDTO orderDTO) {
	//更新本地数据库
	updateDb(orderDTO);
  //远程更新ES
	updateEs(orderDTO);
}

在以上逻辑中,如果写数据库成功,写ES失败,那么事务会回滚。

如果写数据库成功,写ES超时,实际上ES操作成功,这时候数据库会回滚,导致数据不一致。这时候需要重试来保证最终一致性。

这个方案的好处就是简单,容易实现。并且实时性比较高。

缺点首先是需要改代码,有侵入性,还有就是存在不一致的情况。并且在本地事务中发生了外调(外部调用,调ES),大大拖长了事务,白白占用数据库链接,影响整体的吞吐量。

MQ异步消费 ​

在应用中,如果我要更新数据库了,那么就抛一个消息出去,然后数据库和ES各自有一个监听者,监听消息之后各自去做数据变更,如果失败了就基于消息的重试在重新执行。

或者像之前那个方案一样,先操作数据库,然后异步通知ES去更新,这时候就可以借助本地消息表的方式来保证最终一致性了。

这个方案的好处是用了MQ,起到了解耦的作用,而且还做到了异步,提升了整体性能。

缺点就是MQ可能存在延迟,并且需要引入新的中间件,复杂度有所提升。

扫表定时同步 ​

如果是ES中的数据变更的实时性要求不高,可以考虑定时任务扫表, 然后批量更新ES。

这个方案优点是没有侵入性,数据库的写操作处不需要改代码。

缺点是实时性很差,并且轮询可能存在性能问题、效率问题以及给数据库带来压力。

监听binlog同步 ​

还有一种方案,就是可以利用数据库变更时产生的binlog来更新ES。通过监听binlog来更新ES中的数据,也有成熟的框架可以做这样的事情

好处就是对业务代码完全没有侵入性,业务也非常解耦,不需要关心这个ES的更新操作。

缺点就是需要基于binlog监听,需要引入第三方框架。存在一定的延迟。

总结一下,目前业内比较流行的方案是基于binlog监听的这种,首先一般业务量小的业务也不太需要用ES,所以用了ES的团队,一般并不太会关心引入新框架的复杂度问题,而且ES这种搜索,一般来说,毫秒级的延迟都是可以接受的,所以,综合来讲,基于canal做数据同步的方案,是比较合适的。