canal开源数据同步神器-概述

昨日浏览公众文章时,偶尔发现阿里开源的这款软件,初步了解,是基于mysql binarylog的增量发布订阅服务。网上也有客户端针对.net平台的支持。java

下面关于canal的介绍,搜集了网上的一些资料,可供你们参考学习之用。mysql

canal的介绍git

canal是阿里巴巴旗下的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB)。github

起源:早期,阿里巴巴B2B公司由于存在杭州和美国双机房部署,存在跨机房同步的业务需求。不过早期的数据库同步业务,主要是基于trigger的方式获取增量变动,不过从2010年开始,阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变动进行同步,由此衍生出了增量订阅&消费的业务,今后开启了一段新纪元。web

基于日志增量订阅&消费支持的业务:redis

  1. 数据库镜像
  2. 数据库实时备份
  3. 多级索引 (卖家和买家各自分库索引)
  4. search build
  5. 业务cache刷新
  6. 价格变化等重要业务消息

工做原理

mysql主备复制实现:spring

从上层来看,复制分红三步:sql

  1. master将改变记录到二进制日志(binary log)中(这些记录叫作二进制日志事件,binary log events,能够经过show binlog events进行查看);
  2. slave将master的binary log events拷贝到它的中继日志(relay log);
  3. slave重作中继日志中的事件,将改变反映它本身的数据。

canal的工做原理

原理相对比较简单:数据库

  1. canal模拟mysql slave的交互协议,假装本身为mysql slave,向mysql master发送dump协议
  2. mysql master收到dump请求,开始推送binary log给slave(也就是canal)
  3. canal解析binary log对象(原始为byte流)

架构设计

我的理解,数据增量订阅与消费应当有以下几个点:api

  1. 增量订阅和消费模块应当包括binlog日志抓取,binlog日志解析,事件分发过滤(EventSink),存储(EventStore)等主要模块。
  2. 若是须要确保HA能够采用Zookeeper保存各个子模块的状态,让整个增量订阅和消费模块实现无状态化,固然做为consumer(客户端)的状态也能够保存在zk之中。
  3. 总体上经过一个Manager System进行集中管理,分配资源。

能够参考下图:

canal架构设计

说明:

  • server表明一个canal运行实例,对应于一个jvm
  • instance对应于一个数据队列 (1个server对应1..n个instance)

instance模块:

  • eventParser (数据源接入,模拟slave协议和master进行交互,协议解析)
  • eventSink (Parser和Store连接器,进行数据过滤,加工,分发的工做)
  • eventStore (数据存储)
  • metaManager (增量订阅&消费信息管理器)

EventParser

整个parser过程大体可分为几部:

  1. Connection获取上一次解析成功的位置(若是第一次启动,则获取初始制定的位置或者是当前数据库的binlog位点)
  2. Connection创建链接,发生BINLOG_DUMP命令
  3. Mysql开始推送Binary Log
  4. 接收到的Binary Log经过Binlog parser进行协议解析,补充一些特定信息
  5. 传递给EventSink模块进行数据存储,是一个阻塞操做,直到存储成功
  6. 存储成功后,定时记录Binary Log位置

EventSink设计

说明:

  • 数据过滤:支持通配符的过滤模式,表名,字段内容等
  • 数据路由/分发:解决1:n (1个parser对应多个store的模式)
  • 数据归并:解决n:1 (多个parser对应1个store)
  • 数据加工:在进入store以前进行额外的处理,好比join

1 数据1:n业务 :

为了合理的利用数据库资源, 通常常见的业务都是按照schema进行隔离,而后在mysql上层或者dao这一层面上,进行一个数据源路由,屏蔽数据库物理位置对开发的影响,阿里系主要是经过cobar/tddl来解决数据源路由问题。 因此,通常一个数据库实例上,会部署多个schema,每一个schema会有由1个或者多个业务方关注。

2 数据n:1业务:

一样,当一个业务的数据规模达到必定的量级后,必然会涉及到水平拆分和垂直拆分的问题,针对这些拆分的数据须要处理时,就须要连接多个store进行处理,消费的位点就会变成多份,并且数据消费的进度没法获得尽量有序的保证。 因此,在必定业务场景下,须要将拆分后的增量数据进行归并处理,好比按照时间戳/全局id进行排序归并.

EventStore设计

目前实现了Memory内存、本地file存储以及持久化到zookeeper以保障数据集群共享。
Memory内存的RingBuffer设计:

定义了3个cursor

  • Put : Sink模块进行数据存储的最后一次写入位置
  • Get : 数据订阅获取的最后一次提取位置
  • Ack : 数据消费成功的最后一次消费位置

借鉴Disruptor的RingBuffer的实现,将RingBuffer拉直来看:

实现说明:

  • Put/Get/Ack cursor用于递增,采用long型存储
  • buffer的get操做,经过取余或者与操做。(与操做: cusor & (size – 1) , size须要为2的指数,效率比较高)

Instance设计

instance表明了一个实际运行的数据队列,包括了EventPaser,EventSink,EventStore等组件。
抽象了CanalInstanceGenerator,主要是考虑配置的管理方式:

1. manager方式: 和你本身的内部web console/manager系统进行对接。(alibaba内部使用方式)

2. spring方式:基于spring xml + properties进行定义,构建spring配置.

  • spring/memory-instance.xml 全部的组件(parser , sink , store)都选择了内存版模式,记录位点的都选择了memory模式,重启后又会回到初始位点进行解析。特色:速度最快,依赖最少
  • spring/file-instance.xml 全部的组件(parser , sink , store)都选择了基于file持久化模式,注意,不支持HA机制.支持单机持久化
  • spring/default-instance.xml 全部的组件(parser , sink , store)都选择了持久化模式,目前持久化的方式主要是写入zookeeper,保证数据集群共享. 支持HA
  • spring/group-instance.xml 主要针对须要进行多库合并时,能够将多个物理instance合并为一个逻辑instance,提供客户端访问。场景:分库业务。 好比产品数据拆分了4个库,每一个库会有一个instance,若是不用group,业务上要消费数据时,须要启动4个客户端,分别连接4个instance实例。使用group后,能够在canal server上合并为一个逻辑instance,只须要启动1个客户端,连接这个逻辑instance便可.

Server设计

server表明了一个canal的运行实例,为了方便组件化使用,特地抽象了Embeded(嵌入式) / Netty(网络访问)的两种实现:

  • Embeded : 对latency和可用性都有比较高的要求,本身又能hold住分布式的相关技术(好比failover)
  • Netty : 基于netty封装了一层网络协议,由canal server保证其可用性,采用的pull模型,固然latency会稍微打点折扣,不过这个也视状况而定。

增量订阅/消费设计

具体的协议格式,可参见:CanalProtocol.proto
get/ack/rollback协议介绍:

  • Message getWithoutAck(int batchSize),容许指定batchSize,一次能够获取多条,每次返回的对象为Message,包含的内容为:
  • a. batch id 惟一标识
  • b. entries 具体的数据对象,对应的数据对象格式:EntryProtocol.proto
  • void rollback(long batchId),顾命思议,回滚上次的get请求,从新获取数据。基于get获取的batchId进行提交,避免误操做
  • void ack(long batchId),顾命思议,确认已经消费成功,通知server删除数据。基于get获取的batchId进行提交,避免误操做
  • canal的get/ack/rollback协议和常规的jms协议有所不一样,容许get/ack异步处理,好比能够连续调用get屡次,后续异步按顺序提交ack/rollback,项目中称之为流式api.
  • 流式api设计的好处:
  • get/ack异步化,减小因ack带来的网络延迟和操做成本 (99%的状态都是处于正常状态,异常的rollback属于个别状况,不必为个别的case牺牲整个性能)
  • get获取数据后,业务消费存在瓶颈或者须要多进程/多线程消费时,能够不停的轮询get数据,不停的日后发送任务,提升并行化. (做者在实际业务中的一个case:业务数据消费须要跨中美网络,因此一次操做基本在200ms以上,为了减小延迟,因此须要实施并行化)

流式api设计:

  • 每次get操做都会在meta中产生一个mark,mark标记会递增,保证运行过程当中mark的惟一性
  • 每次的get操做,都会在上一次的mark操做记录的cursor继续日后取,若是mark不存在,则在last ack cursor继续日后取
  • 进行ack时,须要按照mark的顺序进行数序ack,不能跳跃ack. ack会删除当前的mark标记,并将对应的mark位置更新为last ack cusor
  • 一旦出现异常状况,客户端可发起rollback状况,从新置位:删除全部的mark, 清理get请求位置,下次请求会从last ack cursor继续日后取

 

数据同步方案选择

针对上文的需求,通过思考,初步有以下的一些方案:

  • 代码实现 针对代码中进行数据库的增删改操做时,同时进行elasticsearch的增删改操做。
  • mybatis实现 经过mybatis plugin进行实现,截取sql语句进行分析, 针对insert、update、delete的语句进行处理。显然,这些操做若是都是单条数据的操做,是很容易处理的。可是,实际开发中,老是会有一些批量的更新或者删除操做,这时候,就很难进行处理了。
  • Aop实现 无论是经过哪一种Aop方式,根据制定的规则,如规范方法名,注解等进行切面处理,但依然仍是会出现没法处理批量操做数据的问题。
  • logstash logstash相似的同步组件提供的文件和数据同步的功能,能够进行数据的同步,只须要简单的配置就能将mysql数据同步到elasticsearch,可是logstash的原理是每秒进行一次增量数据查询,将结果同步到elasticsearch,实时性要求特别高的,可能没法知足要求。且此方案的性能不是很好,形成资源的浪费。

实现方式

优缺点

代码实现

技术难度低,侵入性强,实时性高

基于mybatis

有必定的技术难度,可是没法覆盖全部的场景

Aop实现

技术难度低,半侵入性,须要规范代码,依然没法覆盖全部的场景

logstash

技术难度低,无侵入性,无需开发,但会形成资源浪费。

那么是否有什么更好的方式进行处理吗?mysql binlog同步,实时性强,对于应用无任何侵入性,且性能更好,不会形成资源浪费,那么就有了我今天的主角——canal

canal

介绍

canal 是阿里巴巴的一个开源项目,基于java实现,总体已经在不少大型的互联网项目生产环境中使用,包括阿里、美团等都有普遍的应用,是一个很是成熟的数据库同步方案,基础的使用只须要进行简单的配置便可。 canal是经过模拟成为mysql 的slave的方式,监听mysql 的binlog日志来获取数据,binlog设置为row模式之后,不只能获取到执行的每个增删改的脚本,同时还能获取到修改前和修改后的数据,基于这个特性,canal就能高性能的获取到mysql数据数据的变动。

使用

canal的介绍在官网有很是详细的说明,若是想了解更多,你们能够移步官网(https://github.com/alibaba/canal)了解。我这里补充下使用中不太容易理解部分。 canal的部署主要分为server端和client端。 server端部署好之后,能够直接监听mysql binlog,由于server端是把本身模拟成了mysql slave,因此,只能接受数据,没有进行任何逻辑的处理,具体的逻辑处理,须要client端进行处理。 client端通常是须要你们进行简单的开发。https://github.com/alibaba/canal/wiki/ClientAPI 有一个简单的示例,很容易理解。

canal Adapter

为了便于你们的使用,官方作了一个独立的组件Adapter,Adapter是能够将canal server端获取的数据转换成几个经常使用的中间件数据源,如今支持kafka、rocketmq、hbase、elasticsearch,针对这几个中间件的支持,直接配置便可,无需开发。上文中,若是须要将mysql的数据同步到elasticsearch,直接运行 canal Adapter,修改相关的配置便可。

常见问题

  • 没法接收到数据,程序也没有报错? 必定要确保mysql的binlog模式为row模式,canal原理是解析Binlog文件,而且直接中文件中获取数据的。
  • Adapter 使用没法同步数据? 按照官方文档,检查配置项,如sql的大小写,字段的大小写可能都会有影响,若是还没法搞定,能够本身获取代码调试下,Adapter的代码仍是比较容易看懂的。

canal Adapter elasticsearch 改造

由于有了canal和canal Adapter这个神器,同步到elasticsearch、hbase等问题都解决了,可是本身的开发的过程当中发现,Adapter使用仍是有些问题,由于先使用的是elasticsearch同步功能,因此对elasticsearch进行了一些改造:

elasticsearch初始化

一个全新的elasticsearch没法使用,由于没有建立elasticsearch index和mapping,增长了对应的功能。 elasticsearch配置文件mapping节点增长两个参数:

enablefieldmap: true
  fieldmap:
    id: "text"
    BuildingId: "text"
    HouseNum: "text"
    Floors: "text"
    IdProjectInfo: "text"
    HouseDigitNum: "text"
    BuildingNum: "text"
    BuildingName: "text"
    Name: "text"
    projectid: "text"
    bIdProjectInfo: "text"
    cinitid: "text"
    pCommunityId: "text"

enablefieldmap 是否须要自动生成fieldmap,默认为false,若是须要启动的时候就生成这设置为true,而且设置 fieldmap,相似elasticsearch mapping中每一个字段的类型。

esconfig bug处理

代码中获取binlog的日志处理时,必需要获取数据库名,可是当获取binlog为type query时,是没法获取 数据库名的,此处有bug,致使出现 "Outer adapter write failed" ,且未输出错误日志,修复此bug.

后续计划

  • 增长rabbit MQ的支持
  • 增长redis的支持

源码

源码地址:https://github.com/itmifen/canal

相关文章
相关标签/搜索