去年和今年年初,我们开源了MongoShakeRedisShake分别用于MongoDB和Redis的迁移、同步、备份等多种需求。最近,我们的shake系列又进一步壮大,我们推出了一款dynamodb迁移的工具:dynamo-shake。目前支持从dynamodb迁移到MongoDB,后续我们还会考虑支持多种通道,比如直接文件备份、迁移至kafka,或者迁移到别的数据库如cassandra,redis等。
下载地址:目前暂时不对外,请联系烛昭。

DynamoShake基本功能

DynamoDB支持全量和增量的同步,进程启动后会先进行全量同步,全量同步结束后进入增量同步的阶段。
全量同步分为数据同步和索引同步两部分,数据同步用于同步数据,数据同步结束后将会进行索引的同步,索引同步会同步默认的primary key,用户自建的索引GSI如果MongoDB是副本集支持,集群版目前暂时不支持同步。
增量同步只同步数据,不同步增量同步过程中产生的索引。
此外,全量和增量同步阶段不支持对原来的库表进行DDL操作,比如删表,建表,建索引等。

断点续传

全量同步不支持断点续传功能,增量同步支持断点续传,也就是说如果增量断开了,一定时间内恢复是可以只进行增量的断点续传。但在某些情况下,比如断开的时间过久,或者之前的位点(参考下文)丢失,那么都会导致重新触发全量同步。

同步数据

所有源端的表会写入到目的的一个库(默认是dynamo-shake)的不同表中,比如用户有table1,table2,那么同步完后,目的端会有个dynamo-shake的库,库里面有table1和table2的表。
在原生的dynamodb中,协议是包裹了一层类型字段,其格式是“key: type: value”格式,例如用户插入了一条{hello: 1},那么dynamodb接口获取的数据是{"hello": {"N": 1}}的格式。
Dynamo所有的数据类型:

  • String
  • Binary
  • Number
  • StringSet
  • NumberSet
  • BinarySet
  • Map
  • List
  • Boolean
  • Null

那么我们提供2种转换方式,raw和change,其中raw就是按照裸的dynamodb接口获取的数据写入:

rszz-4.0-2:PRIMARY> use dynamo-shake
switched to db dynamo-shake
rszz-4.0-2:PRIMARY> db.zhuzhao.find()
{ "_id" : ObjectId("5d43f8f8c51d73b1ba2cd845"), "aaa" : { "L" : [ { "S" : "aa1" }, { "N" : "1234" } ] }, "hello_world" : { "S" : "f2" } }
{ "_id" : ObjectId("5d43f8f8c51d73b1ba2cd847"), "aaa" : { "N" : "222" }, "qqq" : { "SS" : [ "h1", "h2" ] }, "hello_world" : { "S" : "yyyyyyyyyyy" }, "test" : { "S" : "aaa" } }
{ "_id" : ObjectId("5d43f8f8c51d73b1ba2cd849"), "aaa" : { "L" : [ { "N" : "0" }, { "N" : "1" }, { "N" : "2" } ] }, "hello_world" : { "S" : "测试中文" } }

change表示剥离类型字段:

rszz-4.0-2:PRIMARY> use dynamo-shake
switched to db dynamo-shake
rszz-4.0-2:PRIMARY> db.zhuzhao.find()
{ "_id" : ObjectId("5d43f8f8c51d73b1ba2cd845"), "aaa" : [ "aa1", 1234 ] , "hello_world" : "f2" }
{ "_id" : ObjectId("5d43f8f8c51d73b1ba2cd847"), "aaa" : 222, "qqq" : [ "h1", "h2" ] , "hello_world" : "yyyyyyyyyyy", "test" : "aaa" }
{ "_id" : ObjectId("5d43f8f8c51d73b1ba2cd849"), "aaa" : [ 0, 1, 2 ], "hello_world" : "测试中文" }

用户可以根据自己的需求制定自己的同步类型。

位点

增量的断点续传是根据位点来实现的,默认的位点是写入到目的MongoDB中,库名是dynamo-shake-checkpoint。每个表都会记录一个checkpoint的表,同样还会有一个status_table表记录当前是全量同步还是增量同步阶段。

rszz-4.0-2:PRIMARY> use dynamo-shake42-checkpoint
switched to db dynamo-shake42-checkpoint
rszz-4.0-2:PRIMARY> show collections
status_table
zz_incr0
zz_incr1
rszz-4.0-2:PRIMARY>
rszz-4.0-2:PRIMARY>
rszz-4.0-2:PRIMARY> db.status_table.find()
{ "_id" : ObjectId("5d6e0ef77e592206a8c86bfd"), "key" : "status_key", "status_value" : "incr_sync" }
rszz-4.0-2:PRIMARY> db.zz_incr0.find()
{ "_id" : ObjectId("5d6e0ef17e592206a8c8643a"), "shard_id" : "shardId-00000001567391596311-61ca009c", "father_id" : "shardId-00000001567375527511-6a3ba193", "seq_num" : "", "status" : "no need to process", "worker_id" : "unknown-worker", "iterator_type" : "AT_SEQUENCE_NUMBER", "shard_it" : "", "update_date" : "" }
{ "_id" : ObjectId("5d6e0ef17e592206a8c8644c"), "shard_id" : "shardId-00000001567406847810-f5b6578b", "father_id" : "shardId-00000001567391596311-61ca009c", "seq_num" : "", "status" : "no need to process", "worker_id" : "unknown-worker", "iterator_type" : "AT_SEQUENCE_NUMBER", "shard_it" : "", "update_date" : "" }
{ "_id" : ObjectId("5d6e0ef17e592206a8c86456"), "shard_id" : "shardId-00000001567422218995-fe7104bc", "father_id" : "shardId-00000001567406847810-f5b6578b", "seq_num" : "", "status" : "no need to process", "worker_id" : "unknown-worker", "iterator_type" : "AT_SEQUENCE_NUMBER", "shard_it" : "", "update_date" : "" }
{ "_id" : ObjectId("5d6e0ef17e592206a8c86460"), "shard_id" : "shardId-00000001567438304561-d3dc6f28", "father_id" : "shardId-00000001567422218995-fe7104bc", "seq_num" : "", "status" : "no need to process", "worker_id" : "unknown-worker", "iterator_type" : "AT_SEQUENCE_NUMBER", "shard_it" : "", "update_date" : "" }
{ "_id" : ObjectId("5d6e0ef17e592206a8c8646a"), "shard_id" : "shardId-00000001567452243581-ed601f96", "father_id" : "shardId-00000001567438304561-d3dc6f28", "seq_num" : "", "status" : "no need to process", "worker_id" : "unknown-worker", "iterator_type" : "AT_SEQUENCE_NUMBER", "shard_it" : "", "update_date" : "" }
{ "_id" : ObjectId("5d6e0ef17e592206a8c86474"), "shard_id" : "shardId-00000001567466737539-cc721900", "father_id" : "shardId-00000001567452243581-ed601f96", "seq_num" : "", "status" : "no need to process", "worker_id" : "unknown-worker", "iterator_type" : "AT_SEQUENCE_NUMBER", "shard_it" : "", "update_date" : "" }
{ "_id" : ObjectId("5d6e0ef27e592206a8c8647e"), "shard_id" : "shardId-00000001567481807517-935745a3", "father_id" : "shardId-00000001567466737539-cc721900", "seq_num" : "", "status" : "done", "worker_id" : "unknown-worker", "iterator_type" : "LATEST", "shard_it" : "arn:aws:dynamodb:us-east-2:240770237302:table/zz_incr0/stream/2019-08-27T08:23:51.043|1|AAAAAAAAAAGsTOg0+3HY+yzzD1cTzc7TPXi/iBi7sA5Q6SGSoaAJ2gz2deQu5aPRW/flYK0pG9ZUvmCfWqe1A5usMFWfVvd+yubMwWSHfV2IPVs36TaQnqpMvsywll/x7IVlCgmsjr6jStyonbuHlUYwKtUSq8t0tFvAQXtKi0zzS25fQpITy/nIb2y/FLppcbV/iZ+ae1ujgWGRoojhJ0FiYPhmbrR5ZBY2dKwEpok+QeYMfF3cEOkA4iFeuqtboUMgVqBh0zUn87iyTFRd6Xm49PwWZHDqtj/jtpdFn0CPoQPj2ilapjh9lYq/ArXMai5DUHJ7xnmtSITsyzUHakhYyIRXQqF2UWbDK3F7+Bx5d4rub1d4S2yqNUYA2eZ5CySeQz7CgvzaZT391axoqKUjjPpdUsm05zS003cDDwrzxmLnFi0/mtoJdGoO/FX9LXuvk8G3hgsDXBLSyTggRE0YM+feER8hPgjRBqbfubhdjUxR+VazwjcVO3pzt2nIkyKPStPXJZIf4cjCagTxQpC/UPMtcwWNo2gQjM2XSkWpj7DGS2E4738biV3mtKXGUXtMFVecxTL/qXy2qpLgy4dD3AG0Z7pE+eJ9qP5YRE6pxQeDlgbERg==", "update_date" : "" }
{ "_id" : ObjectId("5d6e1d807e592206a8c9a102"), "shard_id" : "shardId-00000001567497561747-03819eba", "father_id" : "shardId-00000001567481807517-935745a3", "seq_num" : "39136900000000000325557205", "status" : "in processing", "worker_id" : "unknown", "iterator_type" : "AT_SEQUENCE_NUMBER", "shard_it" : "arn:aws:dynamodb:us-east-2:240770237302:table/zz_incr0/stream/2019-08-27T08:23:51.043|1|AAAAAAAAAAFw/qdbPLjsXMlPalnhh55koia44yz6A1W2uwUyu/MzRUhaaqnI0gPM8ebVgy7dW7dDWLTh/WXYyDNNyXR3Hvk01IfEKDf+FSLMNvh2iELdrO5tRoLtZI2fxxrPZvudRc3KShX0Pvqy2YYwl4nlBR6QezHTWx5H2AU22MGPTx8aMRbjUgPwvgEExRgdzfhG6G9gkc7C71fIc98azwpSm/IW+mV/h/doFndme47k2v8g0GNJvgLSoET7HdJYH3XFdqh4QVDIP4sbz8X1cpN3y8AlT7Muk2/yXOdNeTL6tApuonCrUpJME9/qyBYQVI5dsAHnAWaP2Te3EAvz3ao7oNdnA8O6uz5VF9zFdN1OUHWM40kLUsX4sHve7McEzFLgf4NL1WTAnPN13cFhEm9BS8M7tiJqZ0OzgkbF1AWfq+xg/O6c57/Vvx/G/75DZ8XcWIABgGNkWBET/vLDrgjJQ0PUZJZKNmmbgKKTyHgSl4YOXNEeyH7l6atuc2WaREDjbf7lnQO5No11sz4g3O+AreBcpGVhdZNhGGcrG/wduPYEZfg2hG1sfYiSAM8GipUPMA0PM7JPIJmqCaY90JxRcI1By24tpp9Th35/5rLTGPYJZA==", "update_date" : "" }

其中status_table表中"status_value" : "incr_sync"表示进入了增量阶段。增量的每个shard都会记录一个checkpoint,关于具体shard分裂的规则可以参考dynamodb的guan'fa官方文档。下面是增量表checkpoint的各个字段的说明:

  • _id:mongodb自带主键id
  • shard_id:shard的id,每个shard有一个唯一的id
  • father_id:父shard的id,shard可能有一个父shard。
  • seq_num: 目前处理到的shard内部的sequence number,这个是主要的位点信息。
  • status: 目前同步的阶段,一共有以下几个状态:

    • "not process": 未处理
    • "no need to process": 没有必要处理
    • "prepare stage":准备处理
    • "in processing": 处理中
    • "wait father finish":等待父节点处理完毕再进行处理
    • "done": 处理完毕
  • worker_id:处理的worker id,目前暂未启用
  • iterator_type:shard的遍历方式
  • shard_it:shard的迭代器地址,次要位点信息。
  • update_date:checkpoint更新的时间戳

索引

根据默认的primary key创建一个唯一索引,并且根据partition key创建shard key。用户自己的索引gsi目前不进行创建。

DynamoShake内部架构

本小节主要介绍DynamoShake的部分架构细节

全量同步

下图是基本的一个table的数据同步架构图(dynamo-shake会启动多个并发线程tableSyncer进行拉取,用户可控并发度),fetcher线程从源端dynamodb拉取数据后将数据推入队列,紧接着parser线程从队列中拿取数据并进行解析(dynamo协议转bson),executor负责聚合部分数据并写入mongodb。

  • fetcher。目前fetcher线程只有1个,用的是协议转换驱动是aws提供的driver。fetcher的原理是调用driver进行批量抓取源库的数据,抓到了就塞入队列中,直到抓完当前table的所有数据。fetcher单独分离出来主要是出于网络IO考虑的,目前拉取受网络影响,会比较慢。
  • parser。parser可以启动多个,默认目前是2个,用户可以通过FullDocumentParser进行控制。其主要就是从队列中读取数据,并解析成bson结构。parser解析后,数据按条写入executor的队列。parser线程单独独立出来主要是出于解析比较耗CPU资源考虑。
  • executor。executor也可以启动多个,默认目前是4个,用户可以通过FullDocumentConcurrency进行控制。executor从队列中拉取,并进行batch聚合(聚合上限16MB,总条数1024)后写入目的mongodb。
    当前所有表的数据写入完后,tableSyncer将会退出。

增量同步

增量整体架构如下:

Fetcher线程负责感知stream中shard的变化,Manager负责进行消息的通知,或者创建新的Dispatcher进行消息的处理,一个shard对应一个Dispatcher。Dispatcher从源端拉取增量数据,并通过Batcher进行数据解析和打包整合,然后通过executor进行写入到MongoDB,同时会更新checkpoint。另外,如果是断点续传,那么Dispatcher会从旧的checkpoint位点开始拉取,而不是从头开始拉。

DynamoShake的使用

启动:./dynamo-shake -conf=dynamo-shake.conf,配置参数在dynamo-shake.conf中指定,以下是各个参数的意义:

  • id: 修改会影响MongoDB上目的库的名字
  • log.file:日志文件,不配置将打印到标准输出
  • log.level: log级别。推荐默认。
  • log.buffer: 打印是否带缓存。推荐默认。
  • system_profile:打印内部堆栈的端口号。推荐默认。
  • http_profile:暂未启用
  • sync_mode:同步模式,all表示全量+增量,full表示仅全量,incr表示仅增量(目前不支持)
  • source.access_key_id: dynamodb连接配置参数
  • source.secret_access_key: dynamodb连接配置参数
  • source.session_token: dynamodb连接配置参数,没有可以留空
  • source.region: dynamodb连接配置参数
  • filter.collection.white:过滤白名单,只同步指定的表
  • filter.collection.black:过滤黑名单,不通过指定的表。
  • qps.full:全量阶段限速,1秒钟发送多少个请求
  • qps.full.batch_num:全量阶段限速,1个请求最多包括多少个item。
  • qps.incr:增量阶段限速,1秒钟发送多少个请求
  • qps.incr.batch_num:增量阶段限速,1个请求最多包括多少个item。
  • target.type:目的端配置,目前仅支持mongodb
  • target.address: 目的端mongodb的连接串地址。
  • target.mongodb.type: mongodb是replica还是sharding
  • target.mongodb.exist:如果目的库同名表存在,执行什么行为。drop表示删除,rename表示重命名,留空表示不处理。
  • full.concurrency:全量同步的线程个数,1个线程对应1个表
  • full.document.concurrency:全量同步1个表内的并发个数。
  • full.document.parser:1个表内parser线程的个数
  • full.enable_index.primary:是否同步dynamodb的primary key。
  • full.enable_index.user:是否同步用户自建的索引,目前不支持
  • convert.type:写入的模式,raw表示裸写入, change表示解析类型字段后写入,参考上述文档。
  • increase.concurrency:增量同步并发参数,1次最多抓取的shard个数
  • checkpoint.address = checkpont的存储地址,默认不配置与目的库一致。
  • checkpoint.db = checkpoint写入的db的名字,默认是$db-checkpoint。

DynamoFullCheck

DynamoFullCheck是一个用于校验DynamoDB和MongoDB数据是否一致的工具,目前仅支持全量校验,不支持增量,也就是说,如果增量同步阶段,那么源和目的是不一致的。
DynamoFullCheck只支持单向校验,也就是校验DynamoDB的数据是否是MongoDB的子集,反向不进行校验。
另外,还支持抽样校验,支持只校验感兴趣的表。
校验主要分为以下几部分:

  • 轮廓校验。首先,校验两边的表中数目是否一致;接着,校验索引是否一致(目前没做索引校验)。注意,如果表中数目不一致,将会直接退出,不会进行后续的校验。
  • 精确校验。精确校验数据,原理是从源端拉取数据并解析,如果有唯一索引,那么根据唯一索引查找MongoDB的doc,并对比一致性;如果没有唯一索引,那么会根据整个doc在MongoDB中进行查找(比较重)。
    抽样原理:

精确校验的时候,如果启用抽样,那么会对每个doc进行抽样,判断当前doc是否需要抽样。原理比较简单,比如按30%抽样,那么再0~100中产生一个随机数,如果是0~30的就校验,反之不校验。
DynamoFullCheck由于从源DynamoDB拉取也需要经过fetch,parse阶段,所以一定程度上,该部分代码复用了DynamoShake,不同的是DynamoFullCheck内部各个fetcher, parser, executor线程并发度都是1。

使用参数

full-check参数稍微简单点,直接用的命令行注入,例如:./dynamo-full-check --sourceAccessKeyID=BUIASOISUJPYS5OP3P5Q --sourceSecretAccessKey=TwWV9reJCrZhHKSYfqtTaFHW0qRPvjXb3m8TYHMe --sourceRegion=ap-east-1 -t="10.1.1.1:30441" --sample=300

Usage:
dynamo-full-check.darwin [OPTIONS] Application Options:
-i, --id= target database collection name (default: dynamo-shake)
-l, --logLevel=
-s, --sourceAccessKeyID= dynamodb source access key id
--sourceSecretAccessKey= dynamodb source secret access key
--sourceSessionToken= dynamodb source session token
--sourceRegion= dynamodb source region
--qpsFull= qps of scan command, default is 10000
--qpsFullBatchNum= batch number in each scan command, default is 128
-t, --targetAddress= mongodb target address
-d, --diffOutputFile= diff output file name (default: dynamo-full-check-diff)
-p, --parallel= how many threads used to compare, default is 16 (default: 16)
-e, --sample= comparison sample number for each table, 0 means disable (default: 1000)
--filterCollectionWhite= only compare the given tables, split by ';'
--filterCollectionBlack= do not compare the given tables, split by ';'
-c, --convertType= convert type (default: raw)
-v, --version print version Help Options:
-h, --help Show this help message

其他

我们DynamoShake要不要开源,嗯……这个还没定,敬请期待。

本文作者:烛昭

原文链接

本文为云栖社区原创内容,未经允许不得转载。

使用DynamoShake从dynamodb迁移到mongodb的更多相关文章

  1. SQL Server GUID 数据迁移至MongoDB后怎样查看?

    关键字:SQL Server NEWID():BSON:MongoDB UUID 1.遇到的问题和困惑 SQL Server中的NEWID数据存储到MongoDB中会是什么样子呢?发现不能简单的通过此 ...

  2. 由数据迁移至MongoDB导致的数据不一致问题及解决方案

    故事背景 企业现状 2019年年初,我接到了一个神秘电话,电话那头竟然准确的说出了我的昵称:上海小胖. 我想这事情不简单,就回了句:您好,我是小胖,请问您是? "我就是刚刚加了你微信的 xx ...

  3. 将MySQL一张表的数据迁移到MongoDB数据库的Java代码示例

    Java代码: package com.zifeiy.snowflake.handle.etl.mongodb; import java.sql.Connection; import java.sql ...

  4. Python迁移MySQL数据到MongoDB脚本

    MongoDB是一个文档数据库,在存储小文件方面存在天然优势.随着业务求的变化,需要将线上MySQL数据库中的行记录,导入到MongoDB中文档记录. 一.场景:线上MySQL数据库某表迁移到Mong ...

  5. 利用 AWS DMS 在线迁移 MongoDB 到 Amazon Aurora

    将数据从一种数据库迁移到另一种数据库通常都非常具有挑战性,特别是考虑到数据一致性.应用停机时间.以及源和目标数据库在设计上的差异性等因素.这个过程中,运维人员通常都希望借助于专门的数据迁移(复制)工具 ...

  6. 如何将DynamoDB的数据增量迁移到表格存储

    Amazon DynamoDB是一个完全托管的NoSQL数据库服务,可以提供快速的.可预期的性能,并且可以实现无缝扩展.由于DynamoDB并可以根据实际需求对表进行扩展和收缩,这个过程既不需要停止对 ...

  7. mongodb 数据块的迁移流程介绍

    1. 基本概念 1.1 Chunk(数据块) 表示特定服务器上面,连续范围的分片键值所包含的一组数据,是一个逻辑概念. 例如,某数据块记录如下: { "_id" : "c ...

  8. 使用Tapdata一步搞定关系型数据库到MongoDB的战略迁移

      摘要:数据库作为最关键的基础设施,随着互联网时代的信息高速增长,关系型数据库因其高门槛.高成本以及扩展性差等原因导致的局限性逐渐浮出水面,如今更是面临诸多问题和挑战,Tapdata 专注新一代实时 ...

  9. Linux系统下安装MongoDB 指南

    1.首先连接到Linux系统. 2.到MongoDB官网下载合适的MongoDB安装压缩包. 下载页面:https://www.mongodb.org/downloads#production. 这里 ...

随机推荐

  1. Hibernate与数据库交互方式和Hibernate常用的几个方法

    第一种,适合sql语言水平比较高的人用 HQL(Hibernate Query Language) 面向对象的查询语言,与SQL不同,HQL中的对象名是区分大小写的(除了JAVA类和属性其他部分不区分 ...

  2. js代码触发事件

    /*** * 需要触发谁的点击事件 * @param how_id 节点的id 如:<input id='test'/> 则how_id=test * @param how_this 这个 ...

  3. layui弹框文件导入

    lr.ajax({ type : "post", data :formFile, url : importUrl, contentType: false,// 且已经声明了属性en ...

  4. 概率dp——cf518D

    通过最后的概率求最终的期望 #include<bits/stdc++.h> using namespace std; ; double p,dp[maxn][maxn]; int n,t; ...

  5. JavaScript中的文档对象模型

    1. DOM基本介绍1 什么是DOMDOM的英语全称为Document Object Model,翻译成中文就 是文档对象模型.也就是说,将整个文档看作是一个对象.而一个文档又是由很多节点组成的, 那 ...

  6. Heartbeat基本介绍----HA / vmware HA FT

    Heartbeat是High-Availability Linux Project (Linux下的高可用性项目)的产物,是一套提供防止业务主机因不可避免的意外性或计划性宕机问题的高可用性软件.Hea ...

  7. PostMan授权认证使用

    Authorization 对于很多应用,出于安全考虑我们的接口并不希望对外公开.这个时候就需要使用授权(Authorization)机制. 授权过程验证您是否具有访问服务器所需数据的权限. 当发送请 ...

  8. 06_mybatis关系映射

    1.数据库表分析 表与表之间的业务关系: ​ 在分析表与表之间的业务关系时需要建立 在某个业务意义基础上去分析; ​ 先分析数据级别之间有关系的表之间的业务关系; usre和orders: ​ use ...

  9. <每日一题>题目11:以文件夹名称作为参数,返回该文件夹下所有文件的路径

    ''' 分析: 1.知道文件夹名称(假设是形如:E:\\software\\Notepad++),很显然可以通过OS模块去求 2.OS.listdir(sPath),列出文件夹内所有的文件和文件夹,以 ...

  10. android ListView 获取点击的选项

    需要调用listView的setOnItemClickListener方法 重写OnItemClickListener类的onItemClick 方法,onItemClick 方法有三个参数 @Ove ...