问题:
在大型网站架构中,DB会采用分库分表来解决容量和性能的问题。但这带来个新的问题:比如不同维度的查询或者聚合查询
方案:
一般会通过数据异构机制来解决问题。
具体示例:
为提升系统的接单能力,需要对订单表进行分库分表,随之而来的问题:用户如何查询自己的订单列表?
方法1:扫描所有订单表,然后内存聚合,在大流量的架构中肯定是不行的;
方法2:双写,但是双写无法保证一致性;
方法3:订阅数据库变更日志,比如订阅mysql的binlog日志模拟数据库的主从同步机制,然后解析变更日志写到订单列表,从而实现数据异构,
这种机制也能保证数据的一致性。
比如,订单中心按照订单号分库分表,然后异构出:订单列表按照用户分库分表,商家订单,订单缓存,ES搜索
MYSQL主从复制
原理
--canal类似该原理
1.准备:
github:https://github.com/alibaba/canal
包括
1-canal的文档
2-server端
3-client端的
4-例子
5-源码包等等
2.canal概述
canal是应阿里巴巴存在杭州和美国的双机房部署,存在跨机房同步的业务需求而提出的。
早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求。不过早期的数据库同步业务,主要是基于trigger的方式获取增量变更,不过从2010年开始,阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业务,从此开启了一段新纪元。
说明:目前内部使用的同步,已经支持mysql5.x和Oracle部分版本的日志解析
canal server通过slave机制订阅数据库的binlong日志
基于日志增量订阅&消费支持的业务:
(1)数据库镜像
(2)数据库实时备份
(3)多级索引 (卖家和买家各自分库索引)
(4)search build
(5)业务cache刷新
(6)价格变化等重要业务消息
keyword:数据库同步,增量订阅&消费。
3.canal工作原理:
从上层来看,复制分成三步:
(1) master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看);
(2) slave将master的binary log events拷贝到它的中继日志(relay log);
(3) slave重做中继日志中的事件,将改变反映它自己的数据。
4.部署canal:
4.0 前提:
Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH.
需要安装JDK
4.1 部署canal-server:
4.1.1数据库配置:
开启MySQL的binlog功能,并配置binlog模式为row。
在my.cnf 加入如下:
vi /etc/my.cnf
[mysqld]
log-bin=mysql-bin #开启二进制日志
binlog-format=ROW #选择row模式 ,不要使用statement或者mix模式
server_id=1 #配置主数据库ID,不能和从数据库重复,即不能和canal的slaveId重复,配置mysql replaction需要定义
binlog提供的三种记录模式:
见书,在使用Canal时建议使用row模式
另外在MYSQL中执行"show binary logs",将看到当前有哪些二进制文件及其大小
4.1.2在mysql中 配置canal数据库管理用户,配置相应权限(repication权限)
CREATE USER canal IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES;
说明:一定要重启,否则不生效,避免类似这样的错误
4.1.3下载canal https://github.com/alibaba/canal/releases
并解压到相应文件夹,比如我下载的是canal.deployer-1.0.24.tar.gz
mkdir /usr/server/canal
cd /usr/server
tar -zxvf canal.tar.gz -C canal
canal 文件目录结构
drwxr-xr-x 2 jianghang jianghang 136 2013-02-05 21:51 bin
drwxr-xr-x 4 jianghang jianghang 160 2013-02-05 21:51 conf
drwxr-xr-x 2 jianghang jianghang 1.3K 2013-02-05 21:51 lib
drwxr-xr-x 2 jianghang jianghang 48 2013-02-05 21:29 logs
4.1.4修改配置-canal的数据库实例 instance.properties
说明:这里可以使用已经存在的 example,也可以新起实例,这个名字和客户端java类中写的名字需要一致
这里我们新配置一个canal Server实例
// vi /usr/server/canal/conf/example/instance.properties
mkdir -p /usr/server/canal/conf/product
cp /usr/server/canal/conf/example/instance.properties /usr/server/canal/conf/product/
vi /usr/server/canal/conf/product/instance.properties
#################################################
## mysql serverId 必须和master的SQL的ID不一致
canal.instance.mysql.slaveId = 101
# position info:连接的数据库地址,从哪个二进制文件,哪个位置开始
canal.instance.master.address = 127.0.0.1:3306
# MYSQL主库连接时,起始的binlog文件
canal.instance.master.journal.name =
# MYSQL主库连接时,起始的binlog文件 偏移量
canal.instance.master.position =
# MYSQL主库连接时,起始的binlog文件 时间戳
canal.instance.master.timestamp =
#从库
#canal.instance.standby.address =
#canal.instance.standby.journal.name =
#canal.instance.standby.position =
#canal.instance.standby.timestamp =
# username/password,需要改成自己的数据库信息
canal.instance.dbUsername = canal
canal.instance.dbPassword = canal
canal.instance.defaultDatabaseName = canal_test
canal.instance.connectionCharset = UTF-8
# 通过如下配置过滤订阅的是 哪个库中的哪些表,减少不必要的订阅;
# 比如只关注产品数据库,通过如下模式可只订阅产品数据库
# table regex
# canal.instance.filter.regex = .*\\..*
canal.instance.filter.regex = product_\d+\\.*
#################################################
说明:若多个库订阅,则需要配置多个实例,为每个数据库配置一个配置文件
4.1.5 进行canal Server 的配置,修改conf/canal.properties
vi /usr/server/canal/conf/canal.properties
canal.id= 1
canal.ip=
canal.port= 11111
canal.zkServers=
# 当前canalserver上部署的实例,配置多个时用逗号分隔,此处配置product
canal.destinations= product
# 使用zk持久化模式,这样可以保证集群数据共享,共享HA
canal.instance.global.spring.xml = classpath:spring/default-instance.xml
4.1.5然后cd到bin目录 启动和停止canal-server
启动
/usr/server/canal/bin/startup.sh & tail -f /usr/server/canal/logs/canal/canal.log
停止
/usr/server/canal/bin/stop.sh
验证启动状态,查看log文件
tail -f /usr/server/canal/logs/canal/canal.log
2014-07-18 10:21:08.525 [main] INFO com.alibaba.otter.canal.deployer.CanalLauncher - ## start the canal server.
2014-07-18 10:21:08.609 [main] INFO com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[10.12.109.201:11111]
2014-07-18 10:21:09.037 [main] INFO com.alibaba.otter.canal.deployer.CanalLauncher - ## the canal server is running now ......
---> 上述日志信息显示启动canal成功
*********canal server 集群**************
canal server可以部署一台,也可以部署多台,但是只有一台是活跃的,其它的作为备机;canalserver的高可用是通过zk维护的。
需要安装:zookeeper
配置文件如下修改:
vi /usr/server/canal/conf/canal.properties
canal.id= 1
canal.ip=
canal.port= 11111
canal.zkServers=127.0.0.1:2181
4.2运行canal-client实例:
4.2.1 建立实例maven工程
mvn archetype:create -DgroupId=com.alibaba.otter -DartifactId=canal.sample
[实践:手动创建Maven工程]
4.2.2 添加pom依赖:
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.0.12</version>
</dependency>
4.2.3 更新依赖 mvn install
4.2.4 ClientSamplet.Java
实例代码
package canal.sample;
/**
* Created by hp on 14-7-17.
*/
import java.net.InetSocketAddress;
import java.util.List;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.common.utils.AddressUtils;
import com.alibaba.otter.canal.protocol.Message;
import com.alibaba.otter.canal.protocol.CanalEntry.Column;
import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
import com.alibaba.otter.canal.client.*;
import com.google.protobuf.InvalidProtocolBufferException;
//import org.jetbrains.annotations.NotNull;
public class ClientSample {
public static void main(String args[]) throws Exception {
// 连接 canal Server
//CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(AddressUtils.getHostIp(),
CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.1.121",
11111), "example", "", "");
//11111), "chapter6", "", "");
/**通过zk连接canal Server
String zkServers = "192.168.1.121:2181";
//目标实例:可以自定义一个,例如product
String destination = "product";
//连接 canal Server
CanalConnector connector = CanalConnectors.newClusterConnector(zkServers, destination, "", "");
**/
int emptyCount = 0; //空跑的次数
int totalEmtryCount = 1200;//循环多少次为空时退出
try {
connector.connect(); //连接
connector.subscribe(".*\\..*"); //订阅所有,和不写此行一个效果
//connector.subscribe("product_.*\\.product_.*");//订阅product数据库下的product表
connector.rollback();
while (emptyCount < totalEmtryCount) {
//while(true){//一直循环
//批量获取1000个日志(不确认模式)
Message message = connector.getWithoutAck(1000);//这个值根据实际情况修改
long batchId = message.getId();
//以下为空跑计数
int size = message.getEntries().size();
if (batchId == -1 || size == 0) {
emptyCount++;
System.out.println("empty count : " + emptyCount);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
emptyCount = 0;
//做数据处理
// System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);
printEntry(message.getEntries());
}
connector.ack(batchId); // 提交确认
// connector.rollback(batchId); // 处理失败, 回滚数据
}
System.out.println("empty too many times, exit");
} finally {
connector.disconnect();
}
}
//private static void printEntry(@NotNull List<Entry> entrys) {
private static void printEntry(List<Entry> entrys) throws InvalidProtocolBufferException {
for (Entry entry : entrys) {
if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
continue;
}
//如果是行数据
if(entry.getEntryType() == EntryType.ROWDATA){
//则解析行变更
RowChange rowChage = null;
try {
rowChage = RowChange.parseFrom(entry.getStoreValue());
} catch (Exception e) {
throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e);
}
EventType eventType = rowChage.getEventType();
//这里捕获binlog变更信息
// System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
// entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
// entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
// eventType));
for (RowData rowData : rowChage.getRowDatasList()) {
//如果是删除,则获取删除的数据进行业务处理
if (eventType == EventType.DELETE) {
printColumn(rowData.getBeforeColumnsList());
//List<Column> columns = rowData.getBeforeColumnsList();
//delete(columns);
//如果是新增修改则获取新增修改数据进行处理
} else if (eventType == EventType.INSERT || eventType == eventType.UPDATE) {
//printColumn(rowData.getAfterColumnsList());
List<Column> columns = rowData.getAfterColumnsList();
save(columns);
} else {
System.out.println("-------> before");
printColumn(rowData.getBeforeColumnsList());
System.out.println("-------> after");
printColumn(rowData.getAfterColumnsList());
}
}
}
}
}
//新增的异构操作
private static void save(List<Column> columns) {
for (Column col:columns) {
String name = col.getName();
String value = col.getValue();
System.out.println("name: "+ name + ",value:" + value);
//name: uid,value:4
//name: name,value:10
}
}
//private static void printColumn(@NotNull List<Column> columns) {
private static void printColumn(List<Column> columns) {
for (Column column : columns) {
System.out.println(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated());
}
}
}
报错[有个jar包没找到注释掉了]
import org.jetbrains.annotations.NotNull;
通过以上代码,捕获数据库日志变更,然后进行相关业务的处理。无论是数据异构还是缓存更新
4.2.5 运行java实例
启动后看到控制端信息:
empty count : 1
empty count : 2
empty count : 3
empty count : 4
4.2.6触发数据库变更
create table test (
uid int (4) primary key not null auto_increment,
name varchar(10) not null
);
insert into test (name) values('10');
4.2.7 client 抓取mysql信息:
================> binlog[mysql-bin.000016:3281] , name[canal_test,test] , eventType : INSERT
uid : 7 update=false
name : 10 update=false
empty count : 1
empty count : 2
[发现没有捕获到信息]
tail -f /usr/server/canal/logs/example/example.log
2017-06-03 13:11:28.802 [destination = chapter6 , address = /127.0.0.1:3306 , EventParser] WARN c.a.otter.canal.parse.inbound.mysql.MysqlEventParser - prepare to find start position just show master status
2017-06-03 13:11:28.817 [destination = chapter6 , address = /127.0.0.1:3306 , EventParser] ERROR c.a.otter.canal.parse.inbound.mysql.MysqlEventParser - dump address /127.0.0.1:3306 has an error, retrying. caused by
com.alibaba.otter.canal.parse.exception.CanalParseException: command : 'show master status' has an error! pls check. you need (at least one of) the SUPER,REPLICATION CLIENT privilege(s) for this operation
2017-06-03 13:11:28.820 [destination = chapter6 , address = /127.0.0.1:3306 , EventParser] ERROR com.alibaba.otter.canal.common.alarm.LogAlarmHandler - destination:chapter6[com.alibaba.otter.canal.parse.exception.CanalParseException: command : 'show master status' has an error! pls check. you need (at least one of) the SUPER,REPLICATION CLIENT privilege(s) for this operation
]
原因很简单:因为Mysql需要开启binlog,我设置了,但是没重启。
这样可可以捕获到mysql信息了:
================> binlog[mysql-bin.000001:557] , name[chapter6,test] , eventType : INSERT
uid : 1 update=true
name : 10 update=true
empty count : 1
empty count : 2
5、部署过程中产生问题:
(1)启动失败,log日志中地址正在使用
1、11111端口正在被占用 可以用 ls -i:11111 查看监听进程谁占用端口 或者 用 ps -ef | grep 11111 查看哪个进程占用端口号 然后 kill -9 进程号 杀掉占用进程
2、可以编辑 canal/conf/canal.properties 中的端口号 ,改为不占用的端口
(2)canal无法抓取mysql触发数据库改变的信息
1、检查mysql是否打开binlog写入功能 检查binlog 是否为行模式。
show variables like "binlog_format"
2、检查my.cnf 和 instance.properties 等配置文件填写信息是否正确。
3、检查client 代码 调试实例代码
4、版本兼容问题,canal 1.8 换成 canal 1.7 继续测试
5、查看所有日志文件 分析日志
相关推荐
数据异构之 Canal 初探(技巧篇)
整理的这些资料希望对Java开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。其实面试这一块早在第一个说的25大面试专题就全都有的。以上提及的这些全部的面试+学习的各种笔记资料,我这差不多来回搞了三个多月,收集整理真的很不容易,其中还有很多自己的一些知识总结。正是因为很麻烦,所以对以上这些学习复习资料感兴趣《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
Python paramiko ssh执行shell 报错Cannot find a Java JDK
Python编程使用paramiko模块的ssh远程linux执行shell报错 “Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH.” 具体代码如下: ssh = paramiko.SSHClient() ssh.set_missing_host_key_polic...
十五、寻龙队列术——数据队列:基于Canal实现数据异构
Canal是阿里开源的一款基于MySQL数据库binlog的增量订阅和消费组件,通过 它可以订阅数据库的binlog 0志,然后进行一些数据消费,如数据镜像、数据异构、数 据索引、缓存更新等。相对于消息队列,通过这种机制可以实现数据的有序性和一致性。Canal架构如下图所示。MySQL数据库首先需要部署canal server,可以同时部署多台,但是只有一台是活跃的,其他的作 为备机。canal server会通过slave机制订阅数据库的binlog H志。
报找不到jdk问题
把ecplise中的jdk删除掉 重新安装jdk
基于alibaba-canal的异构数据同步解决方案
随着业务的发展,公司的整体架构方向向微服务演进。随之衍生出各种问题,本文主要提供的是数据库隔离(分库)之后的跨库join问题一种解决方案。 起因: 商品服务的抽离,表结构细化;导致各依赖模块出现了各种跨库join问题。在某些复杂的业务场景下,基于原表(未分库前的商品信息)的一次join操作,在新的表结构下需要做多次跨库join。基于此场景,最终确定了技术方案: 对于简单的跨库join操...
Redis(七):数据异构的武器-BINLOG+MQ 以及阿里cannal的使用
1、定义 何谓数据异构,比如我们将DB里面的数据持久化到REDIS里面去,就是一种数据异构的方式。如果要下个定义的话:把数据按需(数据结构、存取方式、存取形式)异地构建存储。 2、常见应用场景 分库分表中有一个最为常见的场景,为了提升数据库的查询能力,我们都会对数据库做分库分表操作。比如订单库,开始的时候我们是按照订单ID维度去分库分表,那么后来的业务需求想按照商家维度去查询,比如我想查询某...
Spring Cloud Alibaba基础练习01: 使用Cannal保持MySQL与Redis数据一致原理
Spring Cloud Alibaba基础教程01: 使用Cannal保持MySQL与Redis数据一致 2020-05-20 19:40:40 星期日 最近一致在研究Spring Cloud Alibaba的一些组件,在这里记录下心得体会,准备整理成一个系列的简单入门,方便日后查看. MySQL与Redis如何保持一致性的问题 首先来看下MySQL与Redis如何保持一致性的问题,我们会将数据放入到Redis中,从而减轻MySQL数据库的访问的压力,如果Redis缓存中没有存在该数据的情况下
数据异构就该这样设计,yyds
本文主要叙述了数据异构的使用场景,方法。这里面涉及到的activemq以及canal并没有深入分析,关于这块的内容可以直接参考相关具体文档,文中已给了链接地址。根据数据异构的定义,将数据异地构建存储,我们可以应用的地方就非常多,文中说的分库分表之后按照其它维度来查询的时候,我们想脱离DB直接用缓存比如redis来抗量的时候。数据异构这种方式都能够很好的帮助我们来解决诸如此类的问题。
异构系统交互
https://blog.csdn.net/liujiahan629629/article/details/23716357java https://www.cnblogs.com/bdqczhl/p/8777180.htmlc# 转载于:https://www.cnblogs.com/jinlin-2018/p/11147558.html
源码分析 | 数据异构Canal 初探
源码分析 Canal 系列开始了,一个全新的系列,即能探讨 canal 本身的实现原理,也是笔者源码阅读技巧的展示。 1、应用场景 提到 Canal,大家应该都能想到这是一个用于解析 MySQL binlog 日志的工具,并将 MySQL 数据库中数据同步到其他存储介质中,例如 Elasticsearch。 即 Canal 一个非常常用的使用场景:数据异构,一种更高级别的数据读写分离架构设计方法。 随着业务不断的发展,企业发展到一定阶段,发现单体的关系型数据库已无法支撑业务高速发展带来数据不断累.
MySQL Cannal Kafka数据采集
介紹 canal [kə’næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。 基于日志增量订阅和消费的业务包括 数据库镜像 数据库实时备份 索引构建和实时维护(拆分异构索引、倒排索引等) 业务 cache
大促突围:京东到家基于Canal的数据异构设计
张磊京东到家 高级研发工程师8年+软件研发经验,曾先后就职于链家地产、互动吧、寺库网等公司,任研发人员或团队leader,在解决业务系统设计落地方面拥有丰富经验;现就职于达达-京东到家,主...
mysql加载数据源代码_MySQL数据源接入DBus
MySQL数据源接入DBus作者:尹正杰版权声明:原创作品,谢绝转载!否则将追究法律责任。一.cannal概述1>.cannal简介canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从2010年开始,业...
redis+cannal+mysql共,canal redis mysql实现数据同步
https://github.com/alibaba/canal/wiki/Canal-Admin-Guidecanal 原理【官方文档】简介canal [kə’næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年...
Otter数据同步原理
上图是基于memory的仲裁器实现的数据扭转时许图,数据在经过SETL四个阶段完成数据处理,每个数据阶段处理完成将数据推送至下一个阻塞队列由下一个阶段的任务处理器完成数据处理再推向下一个阶段。在讲otter同步原理前需要提一下mysql的日志,otter是基于mysql的增量日志解析来实现数据同步的,这里的日志就是mysql二进制日志——binlog日志,该类型日志详细记录了所有修改的数据的sql,当某一时刻的数据误操作导致问题,或者数据库当即数据丢失,就可以通过binlog日志完成数据的找回。
Oracle 异构服务实践
近期公司准备开发一个计费项目,数据库当然还是选用我最信赖的Oracle了。由于一部分基础数据在一台Win2000Server的SQLServer7.0数据库中,需要考虑灵活、可靠的方法实现从Or acle数据库端访问SQLServer中的数据。于是我有机会真正体验Oracle 9I 中的新增强功能“异构服务”(Heterogeneous Services)并为此...
mysql cannal client_MYSQL同步其它类型数据库利器 - Canal的使用
Canal主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。其基本原理是伪装成辅库把数据同步到其他数据库中。基于日志增量订阅和消费的业务包括数据库镜像数据库实时备份索引构建和实时维护(拆分异构索引、倒排索引等)业务 cache 刷新带业务逻辑的增量数据处理当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0...
异构平台同步(mysql-->oracle)
https://www.cnblogs.com/andy6/p/6159060.html 转载于:https://www.cnblogs.com/littlehb/p/9226223.html
大数据 --> 淘宝异构数据源数据交换工具 DataX
淘宝异构数据源数据交换工具 DataX DataX是什么? DataX是一个在异构的数据库/文件系统之间高速交换数据的工具,实现了在任意的数据处理系统(RDBMS/Hdfs/Local filesystem)之间的数据交换,由淘宝数据平台部门完成。 DataX用来解决什么? 目前成熟的数据导入导出工具比较多,但是一般都只能用于数据导入或者导出...

1129





被折叠的 条评论
为什么被折叠?



