如今准备作mysql实时同步数据到kudu,为之后的实时即席查询分析作数据支撑,kudu+impala速度仍是挺快的。mysql
由于实时性要求比较高,并且须要同步的时候对mysql的压力不能太大,否则会影响业务系统的稳定性。web
介于上面的一些要求,咱们选择采用阿里的canal读取mysql的binlog,对binlog解析后对kudu进行操做。由于canal只是模拟mysql的slave,经过主从复制的协议从mysql节点上面拉取binlog信息而后解析转换(其中mysql的binlog模式应该设置为ROW,这种模式会记录操做的详细信息,好比操做类型是增长仍是删除,某个字段是否是主键,字段是否容许为空,更新的时候该列是否改变等等),这样不会印象到业务系统的运行。sql
而后下面分为两个块:数据库
1.对canal数据拉取并分类转换
以下图所示:json
(1)咱们须要实现本身的一个读取canal的Reader,由于存在多个canal实例因此须要启动多个线程建立不一样canal实例的消费者。bash
(2)由于一个库的binlog只有一个,若是咱们操做了数据库:对A表更新了10条数据而后对B表删除了5条数据而后又对A表新增了6条数据,Reader的一次拉取数据可能会将上面的操做封装到一个message里面,而且解析都是有顺序性的,咱们能够将拉取到的数据进行分类,将对A表和B表的操做进行分类,一次Reader的拉取到时候发送到kafka的数据对一个表的操做就是一条信息(若是不分类的话,上面的操做就会产生3条kafka数据,而且对后面的kudu操做去重性能有影响),分类后对后面的入库kudu操做去重有好处。svg
例如: a表 添加10条数据,b表 修改5条数据,a表 删除6条数据oop
不合并会生成3条json:性能
json1:{a表添加10条数据}
json2:{b表修改5条数据}
json3:{a表删除6条数据}
合并只会生成2条json:spa
json1:{a表添加10条数据而且a表删除6条数据}
json2:{b表修改5条数据}
这样合并以后会减小后面的批量查询操做(操做去重),提升性能。
(3)对上面分类的信息进行转换,转换成固定的json格式。格式以下:(参考了宜信的dbus方案),其中payload里面是每条数据的具体信息,schema的fields是每一个字段的元信息,其中ums_id(该ID是为了防止重复操做,消费端可能重复消费一条数据屡次或者某些缘由可能会屡次操做,ums_id是对单个库的一个操做(增删改这些)来讲是惟一且不变的,到时候消费端消费数据后会先根据id查看库中是否有这个ID若是库中的ums_id小于当前这个ums_id则操做,相反就说明该操做已经执行过就丢弃,而且若是存在真实删除数据,kudu端口也只能作逻辑删除,由于若是kudu端删除了,那么之前的数据重复过来了发现没有该ID ,那消费端那边又会去执行一次)是binlog名字(好比:mysql-bin.000010)加上该条数据的偏移量(其中偏移量前面补0暂时定为偏移量长度为30,好比下面的00000000000000090222)做为单个数据库每一个操做的惟一标识,ums_ts就是这条语句执行的具体时间戳。上面这些信息均可以从canal消费的数据获取到
{
"payload": [
{
"tupe": [
"mysql-bin.00001000000000000000090222",1517194451453,"i","14","lijieinsert"],
"mysql-bin.00001000000000000000090345",1517194455643,"u","14","lijieupdate"],
"mysql-bin.00001000000000000000090465",1517194459643,"d","14","lijiedelete"]
}
],
"schema": {
"fields": [
{
"is_pk": false,
"name": "__ums__id__",
"nullable": false,
"type": "long"
},
{
"is_pk": false,
"name": "__ums__ts__",
"nullable": false,
"type": "long"
},
{
"is_pk": false,
"name": "__ums__op__",
"nullable": false,
"type": "string"
},
{
"is_pk": false,
"name": "id",
"nullable": false,
"type": "int(11)"
},
{
"is_pk": false,
"name": "name",
"nullable": false,
"type": "varchar(50)"
}
],
"kudu_table_name": "mytest01",
"namespace": "mysql.lijie"
}
}
(4)自定义一个Writer而后须要重写kafka的partition,对库名+表名进行hash分组,保证对于一个表的操做只能在一个partition里面而且是有序的(若是不这样的话,可能一个表的操做会被负载到多个partition里面,消费端拿到的数据就是非顺序性的)。
2.对kafka中转换好的数据进行操做
以下图所示:
(1)自定义一个kafka的Reader拉取以前转换好的数据。
(2)对拉取到的json数据进行解析字段filter和操做filter顺序不要紧
字段filter就是:一个表可能有10个字段,可是我只须要其中的6个字段就能够本身对字段过滤。可能mysql进行了alter操做,加了字段等操做这些暂时不考虑固然也能够自动处理,如今想的是若是有字段变动须要dba那边提早通知,咱们手动处理。
操做filter就是:可能会有重复的数据,须要先用一个操做集合的全部ums_id进行批量查询(kudu支持批量查询),若是查询到数据就将集合中查询到的ums_id所有抛弃掉,只操做没有查询到的,防止重复操做。
(3)最后就是将信息转换成kudu相应的增删改操做了。
第一次同步的时候采用sqoop,而后再增量,若是中间遇到失败或者什么的异常状况,还有待讨论。