MySQL同步到ES的方案探索

0    81    1

Tags:

👉 本文共约5903个字,系统预计阅读时间或需23分钟。

前言

有了MySQL为什么还要有ES?要解答这个问题,就要来看看MySQL的不足和ES的优势了!

MySQL的不足

MySQL 架构天生不适合海量数据查询(一千万行以内),它只适合海量数据存储,但无法应对海量数据下各种复杂条件的查询。
有人说加索引不是可以避免全表扫描,提升查询速度吗,为啥说它不适合海量数据查询呢?

加索引也没法提升查询速度

加索引确实可以提升查询速度,但在 MySQL 中加多个索引最终在执行 SQL 的时候它只会选择成本最低的那个索引。
如果没有索引满足搜索条件,就会触发全表扫描,而且即便你使用了组合索引,也要符合最左前缀原则才能命中索引。但在海量数据多种查询条件下很有可能不符合最左前缀原则而导致索引失效。
每加一个索引就会创建一颗B+树,如果是海量数据那将大大增加存储成本,所以索引绝对不是越多越好的!

模糊查询的能力弱

有些查询条件是 MySQL 加索引都解决不了的,比如我要查询商品中所有 title 带有「格力空调」的关键词,如果你用 MySQL 写,会写出如下代码:

这样的话无法命中任何索引,会触发全表扫描,而且你不能指望所有人都能输对他想要的商品,是人就会犯错误,我们经常会犯类似把「格力空调」记成「格空间」的错误。那么 SQL 语句就会变成:

这种情况下就算你触发了全表扫描也无法查询到任何商品,综上所述,MySQL 的查询确实能力有限。

ES的优势

与其说上面列的这些点是 MySQL 的不足,倒不如说 MySQL 本身就不是为海量数据查询而设计的。术业有专攻,海量数据查询还得用专门的搜索引擎,这其中 ES 是当之无愧的王者。它的特点很明显:

  • 每个字段都被索引(ES中一切字段皆为索引),而且采用的是高效的倒排索引
  • 有丰富的分词插件,支持自定义打分和排序,能够实现任意复杂条件的全文检索
  • 支持分布式存储,轻松处理 PB 级别的结构化或非结构化数据。高可用,容灾性能好。

ES大宽表设计

为什么会存在大宽表嘞?假设有这么一个场景:在电商 APP 里,用户输入关键词来查询相对应的商品了,服务器要返回商品信息!
一个商品会有多个 sku(sku 即同一个商品下不同规格的品类,比如苹果手机有 iPhone 6,iPhone 6s 等),会有其基本属性如价格,标题等,商品会有分类(居家,服饰等),品牌,库存等。
为了保证表设计的合理性,我们会设计几张表来存储这些属性:product_sku(sku 表),product_property(基本属性表),sku_stock(库存表),product_category(分类表)

如果我们直接在MySQL中做join,海量商品的join操作可能会压垮数据库,而且MySQL还不支持我们分词搜索商品名称!
这个时候ES大宽表就诞生了,我们不访在ES中建一个大宽表,MySQL只要能保证实时地将多表数据更改同步到ES大宽表内就OK了,这样我们就可以摆脱MySQL实现快速精确的搜索了!

大家应该都在各种电商网站检索过商品,检索商品一般都是通过什么实现呢?搜索引擎Elasticsearch。

那么问题来了,商品上架,数据一般写入到MySQL的数据库中,那么用于检索的数据又是怎么同步到Elasticsearch的呢?

MySQL同步到ES的方案探索MySQL同步ES

接下来我们将会探索一下实现MySQL到ES数据同步的技术方案!

1. 同步双写

这是一种最为简单的方式,在将数据写到mysql时,同时将数据写到ES。

本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信db_bao,谢谢!

MySQL同步到ES的方案探索优点:

  1. 业务逻辑很简单、实现简单

缺点:

  1. 硬编码,有需要写入mysql的地方都需要添加写入ES的代码;
  2. 业务强耦合,商品的管理中耦合大量数据同步代码
  3. 存在双写失败丢数据风险;
  4. 性能较差:本来mysql的性能不是很高,再加一个ES,系统的性能必然会下降。

上面说的双写失败风险,包括以下几种:

  • ES系统不可用;
  • 程序和ES之间的网络故障;
  • 程序重启,导致系统来不及写入ES等。

针对这种情况,有数据强一致性要求的,就必须双写放到事务中来处理,而一旦用上事务,则性能下降更加明显。

2. 异步双写(MQ方式)

由于MQ的性能基本比mysql高出一个数量级,所以性能可以得到显著的提高。

我们也很容易想到异步双写的办法,上架商品的时候,先把商品数据丢进MQ,为了解耦合,我们一般会拆分一个搜索服务,由搜索服务去订阅商品变动的消息,来完成同步。

MySQL同步到ES的方案探索

优点:

  1. 性能高
  2. 不存在丢失数据的问题

缺点:

  1. 还存在硬编码、业务强耦合等问题;
  2. 系统中增加了mq的代码,复杂度增加;
  3. 可能存在时延问题,程序的写入性能提高了,但是由于MQ的消费可能由于网络或其它原因导致用户写入的数据不一定可以马上看到。

3. 异步双写(worker方式)

上面两种方案中都存在硬编码问题,也就是有任何对mysq进行增删改查的地方要么植入ES代码,要么替换为MQ代码,代码的侵入性太强,若是实时要求不高的情况下,可以考虑用定时器来处理,具体步骤如下:

  1. 数据库的相关表中增加一个字段为timestamp的字段,任何crud操作都会导致该字段的时间发生变化;
  2. 原来程序中的crud操作不做任何变化;
  3. 增加一个定时器程序,让该程序按一定的时间周期扫描指定的表,把该时间段内发生变化的数据提取出来;
  4. 逐条写入到ES中。

优点:

  • 不改变原来代码,没有侵入性、没有硬编码;
  • 没有业务强耦合;
  • 不改变原来程序的性能;
  • Worker代码编写简单不需要考虑增删改查。

缺点:

  • 时效性较差,由于定时器工作周期不可能设在秒级,所以实时性没有上面2中好;
  • 对数据库有一定的轮询压力,一种改进方法是将轮询放到压力不大的从库上。

前面说的,一些数据需要聚合处理成类似宽表的结构怎么办呢?例如商品库的商品品类、spu、sku表是分开的,但是查询是跨维度的,在ES里再聚合一次效率就低一些,最好就是把商品的数据给聚合起来,在ES里以类似大宽表的形式存储,这样一来查询效率就高一些。

MySQL同步到ES的方案探索多维度多条件查询

这种其实没什么好办法,基本上还是得搜索服务直接查库,或者远程调用,再查询一遍商品的数据库,就是所谓的回查。

MySQL同步到ES的方案探索回查完成聚合

定时任务

假如我们要快速搞搞,数据量有没那么大,怎么办呢?定时任务也可以。

MySQL同步到ES的方案探索定时任务

定时任务,最麻烦的一点是频率不好选,频率高的话,会非自然地形成业务的波峰,导致存储的CPU、内存占用波峰式上升,频率低的话实时性比较差,而且也有波峰的情况。

这种方式:

  • 优点:实现比较简单

  • 缺点:

    • 实时性难以保证
    • 对存储压力较大

4. Binlog同步方式(数据订阅)

上面三种方案要不有代码侵入、要不有硬编码、要不有时延,那么有没有一种更好的方法嘞?答案就是利用mysql的binlog!
具体步骤如下:

  • 读取mysql的binlog日志,获取指定表的日志信息;
  • 将读取的信息转为MQ;
  • 编写一个MQ消费程序;
  • 不断消费MQ,每消费完一条消息,将消息写入到ES中。

优点:

  • 没有代码侵入、没有硬编码;
  • 原有系统不需要任何变化,没有感知;
  • 性能高
  • 业务解耦,不需要关注原来系统的业务逻辑

缺点:

  • 构建Binlog系统复杂
  • 存在MQ延时的风险

MySQL通过binlog订阅实现主从同步,各路数据订阅框架比如canal就依据这个原理,将client组件伪装成从库,来实现数据订阅。

MySQL同步到ES的方案探索MySQL主从同步

我们以应用最广泛的canal为例,canal通过canal-adapter,支持多种适配器,其中就有ES适配器,通过一些配置,启动之后,就可以直接把MySQL数据同步到ES,这个过程是零代码的。

MySQL同步到ES的方案探索canal同步数据

但是,和老板了解过,使用canal看起来很美好,帮我们把同步的事情都干了,但其实,还是要写代码。为什么呢?

前面提到的多张表数据聚合,canal的支持没那么好,所以还是得回查。这时候用canal-adapter就不合适了,需要自己实现canal-client,监听和聚合数据,写入ES:

MySQL同步到ES的方案探索数据订阅+回查

这种看起来和异步双写比较像,但是第一降低了商品服务的耦合,第二数据的实时性更好。

所以使用数据订阅:

  • 优点:

    • 业务入侵较少
    • 实时性较好

5. 方案选型

问题背景:云上RDS中的多表数据要构造成一张大宽表存入ES,那么如何高效且稳定地将RDS的多表数据同步到ES宽表嘞?
通过之前的铺垫,我们权衡利弊,选择Binlog同步的方式来解决这个问题,整体思路如下所示:

  • 通过DTS数据订阅服务来订阅源RDS的增量日志
  • 第二步:根据业务需求,在消费应用中实现数据定制化消费,对RDS多表数据进行加工,写入宽表RDS
  • 第三步:通过DTS数据同步服务实现宽表RDS和ES的数据同步,这一步我们是无条件信任的

加入宽表RDS来中转而不选择消费应用直接写入ES,可以保证整个数据同步过程的稳定性。我们无条件信任DTS同步过程,但是消费应用写入宽表RDS的过程是有风险的,宽表RDS能够让我们快速定位错误。
MySQL同步到ES的方案探索

至于数据订阅框架的选型,主流的大体上是这些:

CancalMaxwellPython-Mysql-Rplication
开源方阿里巴巴Zendesk社区
开发语言JavaJavaPython
活跃度活跃活跃活跃
高可用支持支持不支持
客户端Java/Go/PHP/Python/RustPython
消息落地Kafka/RocketMQ 等Kafka/RabbitNQ/Redis 等自定义
消息格式自定义JSON自定义
文档详略详细详细详细
Boostrap不支持支持不支持

除了MySQL同步ES,MySQL同步到其它的数据存储,例如HBase,其实大体上都是类似的几种方法。

参考

https://mp.weixin.qq.com/s/FAyG25ejmvV5MVEr0iLgTw

https://mp.weixin.qq.com/s/S7Vmr6Ofd5vq2L9s1jHvXw

标签:

头像

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

发表回复

嘿,我是小麦,需要帮助随时找我哦
  • 18509239930
  • 个人微信

  • 麦老师QQ聊天
  • 个人邮箱
  • 点击加入QQ群
  • 个人微店

  • 回到顶部
返回顶部