概述

canal是阿里巴巴旗下的一款开源项目,纯Java开发。依据数据库增量日志解析,提供增量数据订阅&消费,现在首要支撑了MySQL(也支撑mariaDB)。

背景

前期,阿里巴巴B2B公司由于存在杭州和美国双机房部署,存在跨机房同步的事务需求。不过前期的数据库同步事务,首要是依据trigger的办法获取增量改变,不过从2010年开端,阿里系公司开端逐步的测验依据数据库的日志解析,获取增量改变进行同步,由此衍生出了增量订阅&消费的事务,从此敞开了一段新纪元。ps. 现在内部运用的同步,已经支撑mysql5.x和oracle部分版本的日志解析

依据日志增量订阅&消费支撑的事务:

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

当时的 canal 支撑源端 MySQL 版本包含 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x

作业原理

Mysql的BinLog

它记载了所有的DDL和DML(除了数据查询句子)句子,以事件办法记载,还包含句子所履行的消耗的时间。首要用来备份和数据同步。

binlog 有三种形式:STATEMENT、ROW、MIXED

  1. STATEMENT 记载的是履行的sql句子
  2. ROW 记载的是实在的行数据记载
  3. MIXED 记载的是1+2,优先依照1的形式记载
举例阐明

举例来说,下面的sql

COPYupdate user set age=20

对应STATEMENT形式只要一条记载,对应ROW形式则有可能有成千上万条记载(取决数据库中的记载数)。

MySQL主备复制原理

【建议收藏】超详细的Canal入门,看这篇就够了!!!

  1. Slave 上面的IO线程连接上 Master,并恳求从指定日志文件的指定方位(或许从最开端的日志)之后的日志内容;
  2. Master 接纳到来自 Slave 的 IO 线程的恳求后,经过担任复制的 IO 线程依据恳求信息读取指定日志指定方位之后的日志信息,返回给 Slave 端的 IO 线程。返回信息中除了日志所包含的信息之外,还包含本次返回的信息在 Master 端的 Binary Log 文件的名称以及在 Binary Log 中的方位;
  3. Slave 的 IO 线程接纳到信息后,将接纳到的日志内容依次写入到 Slave 端的Relay Log文件(mysql-relay-bin.xxxxxx)的最末端,并将读取到的Master端的bin-log的文件名和方位记载到master- info文件中,以便在下一次读取的时分能够清楚的高速Master“我需求从某个bin-log的哪个方位开端往后的日志内容,请发给我”
  4. Slave 的 SQL 线程检测到 Relay Log 中新增加了内容后,会立刻解析该 Log 文件中的内容成为在 Master 端实在履行时分的那些可履行的 Query 句子,并在自身履行这些 Query。这样,实际上便是在 Master 端和 Slave 端履行了相同的 Query,所以两端的数据是完全一样的。 当然这个进程本质上仍是存在必定的推迟的。

mysql的binlog文件长这个姿态。

COPYmysql-bin.003831
mysql-bin.003840  
mysql-bin.003849  
mysql-bin.003858 

启用Binlog留意以下几点:

  1. Master主库一般会有多台Slave订阅,且Master主库要支撑事务体系实时改变操作,服务器资源会有瓶颈;
  2. 需求同步的数据表必定要有主键;

canal能够同步数据的原理

理解了mysql的主从同步的机制再来看canal就比较清晰了,canal首要是听过伪装成mysql从server来向主server拉取数据。

【建议收藏】超详细的Canal入门,看这篇就够了!!!

  1. canal模仿mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
  2. mysql master收到dump恳求,开端推送binary log给slave(也便是canal)
  3. canal解析binary log目标(原始为byte流)

Canal架构

canal的规划理念

canal的组件化规划非常好,有点相似于tomcat的规划。运用组合规划,依靠倒置,面向接口的规划。

【建议收藏】超详细的Canal入门,看这篇就够了!!!

canal的组件

  1. canal server 这个代表了我们部署的一个canal 运用
  2. canal instance 这个代表了一个canal server中的多个 mysql instance ,从这一点阐明一个canal server能够搜集多个库的数据,在canal中叫 destionation。

每个canal instance 有多个组件构成。在conf/spring/default-instance.xml中装备了这些组件。他其实是运用了spring的容器来进行这些组件管理的。

instance 包含的组件

这里是一个cannalInstance作业所包含的大组件。截取自 conf/spring/default-instance.xml

COPY<bean id="instance" class="com.alibaba.otter.canal.instance.spring.CanalInstanceWithSpring">
    <property name="destination" value="${canal.instance.destination}" />
    <property name="eventParser">
        <ref local="eventParser" />
    </property>
    <property name="eventSink">
        <ref local="eventSink" />
    </property>
    <property name="eventStore">
        <ref local="eventStore" />
    </property>
    <property name="metaManager">
        <ref local="metaManager" />
    </property>
    <property name="alarmHandler">
        <ref local="alarmHandler" />
    </property>
</bean>  
EventParser规划

eventParser 最根本的组件,相似于mysql从库的dump线程,担任从master中获取bin_log

【建议收藏】超详细的Canal入门,看这篇就够了!!!

整个parser进程大致可分为几步:

  1. Connection获取上一次解析成功的方位 (假如第一次发动,则获取初始指定的方位或许是当时数据库的binlog位点)
  2. Connection树立链接,发送BINLOG_DUMP指令 // 0. write command number // 1. write 4 bytes bin-log position to start at // 2. write 2 bytes bin-log flags // 3. write 4 bytes server id of the slave // 4. write bin-log file name
  3. Mysql开端推送Binaly Log
  4. 接纳到的Binaly Log的经过Binlog parser进行协议解析,弥补一些特定信息 // 弥补字段名字,字段类型,主键信息,unsigned类型处理
  5. 传递给EventSink模块进行数据存储,是一个阻塞操作,直到存储成功
  6. 存储成功后,定时记载Binaly Log方位
EventSink规划

eventSink 数据的归集,运用设置的filter对bin log进行过滤,作业的进程如下。

【建议收藏】超详细的Canal入门,看这篇就够了!!!

阐明:

数据过滤:支撑通配符的过滤形式,表名,字段内容等

数据路由/分发:处理1:n (1个parser对应多个store的形式)

数据归并:处理n:1 (多个parser对应1个store)

数据加工:在进入store之前进行额定的处理,比方join

数据1:n事务

为了合理的运用数据库资源, 一般常见的事务都是依照schema进行隔离,然后在mysql上层或许dao这一层面上,进行一个数据源路由,屏蔽数据库物理方位对开发的影响,阿里系首要是经过cobar/tddl来处理数据源路由问题。

所以,一般一个数据库实例上,会部署多个schema,每个schema会有由1个或许多个事务方关注

数据n:1事务

相同,当一个事务的数据规划到达必定的量级后,必然会涉及到水平拆分和垂直拆分的问题,针对这些拆分的数据需求处理时,就需求链接多个store进行处理,消费的位点就会变成多份,而且数据消费的进展无法得到尽可能有序确实保。

所以,在必定事务场景下,需求将拆分后的增量数据进行归并处理,比方依照时间戳/大局id进行排序归并.

EventStore规划

eventStore 用来存储filter过滤后的数据,canal现在的数据只在这里存储,作业流程如下

  • 现在仅完成了Memory内存形式,后续计划增加本地file存储,mixed混合形式
  • 学习了Disruptor的RingBuffer的完成思路

【建议收藏】超详细的Canal入门,看这篇就够了!!!

定义了3个cursor

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

学习Disruptor的RingBuffer的完成,将RingBuffer拉直来看:

【建议收藏】超详细的Canal入门,看这篇就够了!!!

完成阐明:

  • Put/Get/Ack cursor用于递加,采用long型存储
  • buffer的get操作,经过取余或许与操作。(与操作: cusor & (size – 1) , size需求为2的指数,功率比较高)
metaManager

metaManager 用来存储一些原数据,比方消费到的游标,当时活动的server等信息

alarmHandler

alarmHandler 报警,这个一般情况下便是过错日志,理论上应该是能够定制成邮件等办法,可是现在不支撑

各个组件现在支撑的类型

canal采用了spring bean container的办法来组装一个canal instance ,目的是为了能够更加灵活。

canal经过这些组件的选取能够到达不同运用场景的作用,比方单机的话,一般运用file来存储metadata就行了,HA的话一般运用zookeeper来存储metadata。

eventParser

eventParser 现在只要三种

  • MysqlEventParser 用于解析mysql的日志
  • GroupEventParser 多个eventParser的集合,理论上是对应了分表的情况,能够经过这个合并到一起
  • RdsLocalBinlogEventParser 依据rds的binlog 的复制
eventSink

eventSink 现在只要EntryEventSink 便是依据mysql的binlog数据目标的处理操作

eventStore

eventStore 现在只要一种 MemoryEventStoreWithBuffer,内部运用了一个ringbuffer 也便是说canal解析的数据都是存在内存中的,并没有到zookeeper傍边。

metaManager

metaManager 这个比较多,其实依据元数据寄存的方位能够分为三大类,memory,file,zookeeper

Canal-HA机制

canal是支撑HA的,其完成机制也是依靠zookeeper来完成的,用到的特性有watcher和EPHEMERAL节点(和session生命周期绑定),与HDFS的HA相似。

canal的ha分为两部分,canal server和canal client别离有对应的ha完成

  • canal server: 为了削减对mysql dump的恳求,不同server上的instance(不同server上的相同instance)要求同一时间只能有一个处于running,其他的处于standby状况(standby是instance的状况)。
  • canal client: 为了确保有序性,一份instance同一时间只能由一个canal client进行get/ack/rollback操作,否则客户端接纳无法确保有序。

server ha的架构图如下

【建议收藏】超详细的Canal入门,看这篇就够了!!!

大致进程:

  1. canal server要发动某个canal instance时都先向zookeeper_进行一次测验发动判别_(完成:创立EPHEMERAL节点,谁创立成功就答应谁发动)
  2. 创立zookeeper节点成功后,对应的canal server就发动对应的canal instance,没有创立成功的canal instance就会处于standby状况
  3. 一旦zookeeper发现canal server A创立的instance节点消失后,当即告诉其他的canal server再次进行进程1的操作,从头选出一个canal server发动instance。
  4. canal client每次进行connect时,会首先向zookeeper问询当时是谁发动了canal instance,然后和其树立链接,一旦链接不可用,会从头测验connect。

Canal Client的办法和canal server办法相似,也是运用zookeeper的抢占EPHEMERAL节点的办法进行控制.

canal的作业进程

dump日志

发动时去MySQL 进行dump操作的binlog 方位确定

作业的进程。在发动一个canal instance 的时分,首先发动一个eventParser 线程来进行数据的dump 当他去master拉取binlog的时分需求binlog的方位,这个方位确实定是依照如下的顺序来确定的(这个地方讲述的是HA形式哈)。

  1. 在发动的时分判别是否运用zookeeper,假如是zookeeper,看能否拿到 cursor (也便是binlog的信息),假如能够拿到,把这个信息存到内存中(MemoryLogPositionManager),然后拿这个信息去mysql中dump binlog
  2. 经过1拿不到的话(一般是zookeeper傍边每一,比方第一次搭建的时分,或许由于某些原因zk中的数据被删除了),就去装备文件装备傍边的去拿,把这个信息存到内存中(MemoryLogPositionManager),然后拿这个信息去mysql中dump binlog
  3. 经过2仍然没有拿到的话,就去mysql 中履行一个sql show master status 这个句子会显现当时mysql binlog最终方位的信息,也便是刚写入的binlog所在的方位信息。把这个信息存到内存中(MemoryLogPositionManager),然后拿这个信息去mysql中dump binlog。

后面的eventParser的操作就会以内存中(MemoryLogPositionManager)存储的binlog方位去master进行dump操作了。

mysql的show master status 操作

COPYmysql> show master status\G
*************************** 1. row ***************************
             File: mysql-bin.000028
         Position: 635762367
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set: 18db0532-6a08-11e8-a13e-52540042a113:1-2784514,
318556ef-4e47-11e6-81b6-52540097a9a8:1-30002,
ac5a3780-63ad-11e8-a9ac-52540042a113:1-5,
be44d87c-4f25-11e6-a0a8-525400de9ffd:1-156349782
1 row in set (0.00 sec

归集(sink)和存储(store)

数据在dump回来之后进行的归集(sink)和存储(store)

sink操作是能够支撑将多个eventParser的数据进行过滤filter

filter运用的是instance.properties中装备的filter,当然这个filter也能够由canal的client端在进行subscribe的时分进行设置。假如在client端进行了设置,那么服务端装备文件instance.properties的装备都会失效

sink 之后将过滤后的数据存储到eventStore傍边去。

现在eventStore的完成只要一个MemoryEventStoreWithBuffer,也便是依据内存的ringbuffer,运用这个store有一个特色,这个ringbuffer是依据内存的,大小是有约束的(bufferSize = 16 * 1024 也便是16M),所以,当canal的客户端消费比较慢的时分,ringbuffer中存满了就会阻塞sink操作,那么正读取mysql binlogeventParser线程也会受阻。   这种规划其实也是有道理的。 由于canal的操作是pull 模型,不是producer push的模型,所以他没必要存储太多数据,这样就能够避免了数据存储和持久化管理的一些问题。使数据管理的复杂度大大下降。

  上面这些整个是canal的parser 线程的作业流程,首要对应的便是将数据从mysql搞下来,做一些根本的归集和过滤,然后存储到内存中。

binlog的顾客

canal从mysql订阅了binlog今后首要仍是想要给顾客运用。那么binlog是在什么时分被消费呢。这便是另一条主线了。就像我们做一个toC的体系,管理体系是必须的,用户运用的app或许web又是一套,eventParser 线程就像是管理体系,往里面录入根底数据。canal的client就像是app端一样,是这些数据的消费方。   binlog的首要顾客便是canal的client端。运用的协议是依据tcp的google.protobuf,当然tcp的形式是io多路复用,也便是nio。当我们的client发起恳求之后,canal的server端就会从eventStore中将数据传输给客户端。依据客户端的ack机制,将binlog的元数据信息定时同步到zookeeper傍边。

canal的目录结构

装备父目录: 在下面能够看到

COPYcanal
├── bin
│   ├── canal.pid
│   ├── startup.bat
│   ├── startup.sh
│   └── stop.sh
└── conf
    ├── canal.properties
    ├── gamer ---目录
    ├── ww_social ---目录
    ├── wother ---目录
    ├── nihao ---目录
    ├── liveim ---目录
    ├── logback.xml
    ├── spring ---目录
    ├── ym ---目录
    └── xrm_ppp ---目录

这里是悉数展开的目录

COPYcanal
├── bin
│   ├── canal.pid
│   ├── startup.bat
│   ├── startup.sh
│   └── stop.sh
└── conf
    ├── canal.properties
    ├── game_center
    │   └── instance.properties
    ├── ww_social
    │   ├── h2.mv.db
    │   ├── h2.trace.db
    │   └── instance.properties
    ├── wwother
    │   ├── h2.mv.db
    │   └── instance.properties
    ├── nihao
    │   ├── h2.mv.db
    │   ├── h2.trace.db
    │   └── instance.properties
    ├── movie
    │   ├── h2.mv.db
    │   └── instance.properties
    ├── logback.xml
    ├── spring
    │   ├── default-instance.xml
    │   ├── file-instance.xml
    │   ├── group-instance.xml
    │   ├── local-instance.xml
    │   ├── memory-instance.xml
    │   └── tsdb
    │       ├── h2-tsdb.xml
    │       ├── mysql-tsdb.xml
    │       ├── sql
    │       └── sql-map
    └── ym
        └── instance.properties

Canal运用场景

同步缓存redis/全文查找ES

canal一个常见运用场景是同步缓存/全文查找,当数据库改变后经过binlog进行缓存/ES的增量更新。当缓存/ES更新出现问题时,应该回退binlog到过去某个方位进行从头同步,并提供全量改写缓存/ES的办法,如下图所示。

【建议收藏】超详细的Canal入门,看这篇就够了!!!

下发使命

  另一种常见运用场景是下发使命,当数据改变时需求告诉其他依靠体系。其原理是使命体系监听数据库改变,然后将改变的数据写入MQ/kafka进行使命下发,比方产品数据改变后需求告诉产品详情页、列表页、查找页等先关体系。这种办法能够确保数据下发的精确性,经过MQ发送音讯告诉改变缓存是无法做到这一点的,而且事务体系中不会散落着各种下发MQ的代码,然后完成了下发归集,如下图所示。

【建议收藏】超详细的Canal入门,看这篇就够了!!!

数据异构

在大型网站架构中,DB都会采用分库分表来处理容量和功能问题,但分库分表之后带来的新问题。比方不同维度的查询或许聚合查询,此时就会非常棘手。一般我们会经过数据异构机制来处理此问题。

所谓的数据异构,那便是将需求join查询的多表依照某一个维度又聚合在一个DB中。让你去查询。canal便是完成数据异构的手法之一。

【建议收藏】超详细的Canal入门,看这篇就够了!!!

往期干货:

  • 为什么大家都说 SELECT * 功率低?

  • 从阿里规约看Spring事务

  • 【代码级】全链路压测的全体架构规划,以及5种完成计划(流量染色、数据隔离、接口隔离、零侵入、服务监控)

  • 最近沉浸Redis网络模型,无法自拔!总算知道Redis为啥这么快了

  • 拆开Netty,我发现了这个8个从来没见过的东西?

  • 学习 Shell准没错

  • 最近迷上了源码,Tomcat源码,看我这篇就够了

  • 为什么这11道JVM面试题这么重要(附答案)

  • 芯片战役50年,Intel为什么干不掉AMD?

  • 翻了ConcurrentHashMap1.7 和1.8的源码,我总结了它们的首要差异。

  • 爱上源码,重学Spring AOP深入

  • 9000字,唠唠架构中的规划形式

  • 号外号外!Ant Design Mobile 5.6.0最新实用攻略!

  • 15755字,解锁MySQL功能优化新姿态

本文由传智教育博学谷狂野架构师教研团队发布。

假如本文对您有协助,欢迎关注点赞;假如您有任何主张也可留言谈论私信,您的支撑是我坚持创作的动力。

转载请注明出处!