# 使用canal同步mysql数据

# 场景:

有两个独立的MySql数据库A和B

数据库A中有个表名为news,该表三个字段:

字段 type
id INT
title VARCHAR
data LONGTEXT

数据库B中有两个表,news1 和 news 2, 如下

news 1

字段 type
id INT
title VARCHAR

news 2

字段 type
news_id INT
data LONGTEXT

如果要同步数据库A的news到数据库B的new1和new2,传统的同步方式满足不了

# 实现思路

利用canal获取数据库A中news的变化数据(增,删,改)

利用canal_mysql_nosql_sync将变化的数据存入到redis队列 (https://github.com/liukelin/canal_mysql_nosql_sync)

起一个守护脚本一直消费该redis,将变化的数据写入到数据库B

整个过程如下:

数据库A -> canal_server -> canal_client / canal_mysql_nosql_sync -> redis -> 脚本处理 -> 数据库B

# 实施步骤

# 一.配置canal

**1.**确定服务器上有java环境,如果没有需安装下java

# java -version
java version "1.8.0_121"

**2.**获取canal包

访问https://github.com/alibaba/canal/releases,下载相应的版本
本次以1.0.24为例
wget https://github.com/alibaba/canal/releases/download/canal-1.0.24/canal.deployer-1.0.24.tar.gz

**3.**解压并配置

详细的配置参数可参考https://github.com/alibaba/canal/wiki/AdminGuide

将安装包解压后,默认有如下文件夹

# ls
bin  conf  lib  logs

canal的主配置文件为/conf/canal.properties,这里可以配置监听的端口等

canal.id= 2
canal.ip=
canal.port= 11111
canal.zkServers=

编辑example的配置文件

vim ./conf/example/instance.properties
配置好数据库的相关信息
canal.instance.master.address = 127.0.0.1:3306

# username/password
canal.instance.dbUsername = root
canal.instance.dbPassword = root123456
canal.instance.defaultDatabaseName = news
canal.instance.connectionCharset = UTF-8

配置下正则表达式,用于指定要同步的表
# table regex
canal.instance.filter.regex = .* 
# table black regex
canal.instance.filter.black.regex = platform_news\\..*

这里注意下

canal.instance.filter.regex的白名单配置是无效的,不用配置
如果要指定库和表,可以把不同步的库和表写到canal.instance.filter.black.regex配置中,黑名单的配置有效.
比如我不要同步test这个数据库,则可以这样写
canal.instance.filter.black.regex = test\\..*

配置完成后,启动服务

sh bin/startup.sh
启动完成后,会看到有个端口号为11111的进程

# 二.部署canal_mysql_nosql_sync

**1.**获取canal_mysql_nosql_sync

git clone https://github.com/liukelin/canal_mysql_nosql_sync

**2.**配置

cd canal_mysql_nosql_sync/canal-client/conf
vim canal.properties
配置好对应参数即可,注意下canal.binlog.dir路径

**3.**启动

sh ./canal_mysql_nosql_sync/canal-client/start_canal_client.sh

正常启动后,去项目A的表中改个数据,可立即看到变化

# 三.通过脚本获取redis数据,并写入到数据库B中

1. 连接redis,获取redis中变化的数据,任意语言都可以,只要能使用redis和mysql即可。

以PHP为例:

        $predis = new \Predis\Client('tcp://172.16.93.239:2889');
        $predis->connect();
        $msg = $predis->lpop('canal_binlog_data');
        $predis->disconnect();

获取的数据结构如下:

eventType :操作类型(UPDATE/INSERT/DELETE)

db:   涉及库

table: 涉及表

before:变更前数据

after: 变更后数据

time:  操作时间

**2.**提取出变化的数据,根据eventType,将数据更新到数据库B中

# 故障排除

启动canal时,提示Could not find first log file name in binary log index file

需删除example下的meta.dat文件,再重新启动

redis中没数据

1.确保数据库是ROW模式
2.数据库的用户需要有slave权限
3.看下canal的logs/example/meta.log文件是否有更新
4.看下canal_client中canal.binlog.dir路径下的文件是否有更新