一、简介

官网介绍:https://github.com/alibaba/otter

名称:otter [‘ɒtə(r)]
译意: 水獭,数据搬运工
语言: 纯java开发
定位: 基于数据库增量日志解析,准实时同步到本机房或异地机房的mysql/oracle数据库. 一个分布式数据库同步系统

二、整体架构

image.png
原理描述:
1. 基于Canal开源产品,获取数据库增量日志数据。 什么是Canal, 请 点击
2. 典型管理系统架构,manager(web管理)+node(工作节点)
a. manager运行时推送同步配置到node节点
b. node节点将同步状态反馈到manager上
3. 基于zookeeper,解决分布式状态调度的,允许多node节点之间协同工作。

名词解释:

  1. Channel:同步通道,单向同步中一个Pipeline组成,在双向同步中有两个Pipeline组成;Pipeline:从源端到目标端的整个过程描述,主要由一些同步映射过程组成;
  2. Node: 处理同步过程的工作节点,对应一个jvm;

    三、环境准备

  3. 新建需要的manager表:

otter manager依赖于mysql进行配置信息的存储,所以需要预先安装mysql,并初始化otter manager的系统表结构。

  1. 安装mysql(略)
  2. 新建otter manager需要的表,sql地址如下:https://raw.github.com/alibaba/otter/master/manager/deployer/src/main/resources/sql/otter-manager-schema.sql
    1. 安装zookeeper:

整个otter架构依赖了zookeeper进行多节点调度,所以需要预先安装zookeeper,不需要初始化节点,otter程序启动后会自检.

  1. 安装zookeeper(略)
  2. otter-manager需要在配置文件otter.properties中配置zookeeper集群机器
    1. 安装jdk1.8:

注意jdk1.9及以上,在启动node或者是manage时会报错 Unrecognized VM option ‘UseCMSCompactAtFullCollection’,9及以上版本废弃了CMSCompactAtFullCollection,无法支持。

四、安装

1、otter-manage

官方quickStart:https://github.com/alibaba/otter/wiki/Manager_Quickstart

1.1 下载

下载地址:https://github.com/alibaba/otter/releases
本次使用的版本是 4.2.18,下载并解压后如下:
image.png

1.2 修改配置

修改otter.properties
image.png
基本修改内容如下,其他内容可以保持默认

  1. ## 修改为正确访问ip,生成URL使用,node的配置需要用到
  2. otter.domainName= 127.0.0.1
  3. ## manage页面的访问端口
  4. otter.port =8089
  5. ## 修改为正确数据库信息
  6. otter.database.driver.class.name = com.mysql.jdbc.Driver
  7. otter.database.driver.url = jdbc:mysql://127.0.0.1:3306/otter
  8. otter.database.driver.username = root
  9. otter.database.driver.password = 123456
  10. ## 为node连接manager的端口, node的配置需要用到
  11. otter.communication.manager.port= 1099
  12. ## 配置zookeeper集群机器
  13. otter.zookeeper.cluster.default= 127.0.0.1:2181
  14. 其它使用默认配置即可

1.3 manage 启动

bin目录下

  • 启动 ./startup.sh
  • 关闭 ./stop.sh

启动成功后,访问首页http://127.0.0.1:8089/login.htm,初始密码为:admin/admin,即可完成登录. 目前:匿名用户只有只读查看的权限,登录为管理员才可以有操作权限。
image.png

2、node

官网quickStart:https://github.com/alibaba/otter/wiki/Node_Quickstart
node会受ottermanager进行管理,所以需要预先安装otter manager,完成manager安装后,需要在manager页面为node定义配置信息,并生一个唯一id。

2.1 添加zookeeper

首先确保你的zookeeper已启动成功。
otter依赖zookeeper,访问manager页面的机器管理页面,选择菜单进入“机器管理→zookeeper管理”页面:(下图是已经添加好的页面)
image.png
点击添加,进入添加zk集群页面
image.png
填写完信息后点击保存,出现如下页面
image.png

2.2 添加node

Zookeeper添加成功后,进入“机器管理→Node管理”页面:
image.png
点击进入添加机器页面
image.png

  • 机器名称:可以随意定义,方便自己记忆即可
  • 机器ip:对应node节点将要部署的机器ip,如果有多ip时,可选择其中一个ip进行暴露. (此ip是整个集群通讯的入口,实际情况千万别使用127.0.0.1,否则多个机器的node节点会无法识别)
  • 机器端口:对应node节点将要部署时启动的数据通讯端口,建议值:2088
  • 下载端口:对应node节点将要部署时启动的数据下载端口,建议值:9090
  • 外部ip :对应node节点将要部署的机器ip,存在的一个外部ip,允许通讯的时候走公网处理。
  • zookeeper集群:为提升通讯效率,不同机房的机器可选择就近的zookeeper集群.
  • node这种设计,是为解决单机部署多实例而设计的,允许单机多node指定不同的端口

image.png

2.3 配置nid

机器添加完成后,跳转到机器列表页面,获取对应的机器序号nid:
image.png
通过这几步操作,获取到了node节点对应的唯一标示,称之为node id,简称nid,比如我添加的机器对应序号为1。
在conf目录下创建nid文本,不需要任何后缀,并写入nid:1
image.png
image.png

2.4 修改配置

打开conf/otter.properties,配置如下,其他配置可以保持不变

  1. # node的安装目录
  2. otter.nodeHome = ${user.dir}/node
  3. # manager的服务地址
  4. otter.manager.address = 127.0.0.1:1099

2.5 启动

bin目录下

  • 启动 ./startup.sh
  • 关闭 ./stop.sh

logs/node/node.log 日志如下信息,则表示启动成功
image.png
访问: http://127.0.0.1:8089/,查看“机器管理-Node管理”页面,对应的节点状态,如果变为了已启动,代表已经正常启动。
image.png

五、配置一个简单的单项同步任务

本次演示将edu_db1库中的表t_udict 同步到 库user_db的表t_udict。
image.pngimage.png

1、mysql开启binlog

(1)首先通过如下命令判断mysql是否开启binlog

  1. show master status

若出现如下信息,则表示已开启binlog
image.png
若没有任何信息,则需要修改配置文件(my.cnf),开启binlog,

关于找到my.cnf的方式可以参考如下 https://www.yuque.com/wangchao-volk4/whmpo0/pad6m7#CITaO

2、添加canal

  • Canal官方文档:https://github.com/alibaba/canal/wiki
  • Otter使用canal开源产品获取数据库增量日志数据,可以把cannal看作是源库的一个伪slave。
  • 原理:canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master 发送dump请求,mysql master收到dump请求,开始推送binarylog给slave(也就是canal),canal解析binary log对象(原始为byte流)。
  • Node集成了canal,不需要单独下载canal。

(1)在otter manager “配置管理-canal配置”页面点击添加:
image.png
(2)进入添加canal页面:
image.png
Cannal 存储机制分为memory和file,也可以在运行模式选项选择作为独立服务运行;
勾选其他参数设置,可以设置cannal的服务端口;
必须配置位点信息,否则如果你的数据库已有存量数据,第一次运行时需要等待比较长的时间,可以通过连接源库客户端执行sql获取,如下:
image.png
image.png

3、添加数据源

添加数据源过程较为简单,这里不做详细阐述,具体下面两张图。添加完后会出现下图列表中的两个数据源信息(一个为源库edu_db1,一个是目标库user_db,如果两个库在同一个数据源下,则不需要配置两个数据源)
image.png
image.png

4、添加数据表

进入数据表配置项,这里需要配置两个表edu_db1库中的表t_udict 和 库user_db的表t_udict
image.png
填入相应的信息
image.png
table示例说明

  • 单表配置:alibaba.product
  • 分表配置:alibaba[1-64].product , alibaba.product[01-32]
  • 正则配置:(.).(.)
  • schema name和table name都设置成.*表示全库同步

    5、添加一个channel

    进入同步管理
    image.png
    image.png

    6、添加pipeline

    添加channel成功后,点击Channel名字,进入Pipeline管理页面,添加一个Pipleline
    image.png
    image.png
    如上图填好所需信息,勾选高级设置,可以选是否过滤ddl同步等选项,点击保存,成功后会返回Pipeline管理页面。

    7、添加映射关系

    添加pipeline成功后,点击pipeline名字
    image.png
    进入映射关系列表页面,点击添加
    image.png
    进入添加映射关系页面
    image.png
    点击保存返回映射关系列表页面,如果源数据表是只同步一个表可以点击下一步,选择需要同步的字段映射关系;
    映射关系页面如下
    image.png

    8、启动同步

    (1)以上步骤都配置成功之后,返回channel页面,点击启用
    image.png
    image.png
    (2)测试
    在edu_db1的t_udit中新建一条数据
    image.png
    在user_db的表t_udit中查看同步效果,如下图,同步成功
    image.png

    六、配置双向同步任务

    1、添加新的canal、channel、pipeline、映射关系,添加流程和第五章一样,第五章是将将edu_db1库中的表t_udict 同步到 库user_db的表t_udict。这里将源库和目标库反过来重新配置就行了。
    2、配置完后如图所示
    image.png
    image.png
    image.png
    image.png
    3、验证
    同时开启两个Channel即可。分别在两个表中添加信息,都能够成功同步到另外一张表则表示同步成功。

    七、配置单表向多表同步任务

    可用于的业务场景:分库分表时,用于增量数据的同步(原表同步到分表)
    整体步骤与 配置单项同步类似,以下只突出描述差异的地方。
    1、canal配置
    image.png
    2、添加数据源和表
    添加数据源的过程略过
    注意分表的写法,当分表作为目标表是,不能使用[0-1]这种写法,要使用[0|1]
    下面这种只能作为源表,当用作目标表时,会报错
    image.png
    下面这种写法可以用作目标表,当验证连接表时会报错 ‘SELECT未成功’,没有任何影响
    image.png
    image.png
    3、添加channel
    image.png
    4、添加pipeline
    image.png
    5、添加映射关系
    如下配置,注意目标表
    关于EventProcessor的编写可以参考 https://github.com/alibaba/otter/wiki/Otter%E6%89%A9%E5%B1%95%E6%80%A7
    image.png
    EventProcessor如下: ```sql package com.alibaba.otter.node.extend.processor;

import com.alibaba.fastjson.JSON; import com.alibaba.otter.shared.etl.model.EventColumn; import com.alibaba.otter.shared.etl.model.EventData; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory;

import java.math.BigInteger;

/**

  • 根据Id分片
  • id % 2 */ public class One2NTfIncomeDetailEventProcessor extends AbstractEventProcessor {

    private static final Logger logger = LoggerFactory.getLogger(One2NTfIncomeDetailEventProcessor.class);

    private static final String ID = “id”; private static final BigInteger TWO = BigInteger.valueOf(2L);

    /**

    • king_user -> kinguser[0-1] *
    • @param eventData
    • @return */ @Override public boolean process(EventData eventData) { logger.error(“eventData:{}”, JSON.toJSONString(eventData)); EventColumn idEventColumn = getColumn(eventData, ID); if (idEventColumn == null) {
      1. logger.error("[同步] id is null :{}", JSON
      2. .toJSONString(eventData.getColumns()));
      3. return false;
      } String id = idEventColumn.getColumnValue(); if (StringUtils.isBlank(id)) {
      1. logger.error("[同步] id is null :{}", JSON
      2. .toJSONString(eventData.getColumns()));
      3. return false;
      } String name = “kinguser“ + getShardNum(new BigInteger(id)); logger.info(name); eventData.setTableName(name); return true; }
  1. /**
  2. * 获取分片键
  3. */
  4. private static int getShardNum(BigInteger id) {
  5. return id.mod(TWO).intValue();
  6. }

} ```

八、配置多表向单表同步任务

注意,多表向单表同步时,不解决主键冲突,如果有主键冲突,则覆盖。(理论上不应该存在主键冲突)
配置过程已经简化,如下
image.png
注意多表的写法,[0-1],必须使用这个写法
image.png