本文将主要讲数据库同步与迁移两个话题,然后介绍阿里云开源的基于MongoDB与Redis的数据同步与迁移工具MongoShake与RedisShake,以及最后介绍一些用户用例。1、同步现在大部分数据库都支持集群版数据,也就是说在一个逻辑单元中有多个db节点,不同节点之间的数据同步通常是通过复制来实现的。比如MySQL的基于Binlog的主从同步,Redis的基于Sync/Psync机制的AOF主从同步,MongoDB的基于oplog的主从同步等等。这些机制支持数据冗余高可用和一个单元下的读写分离负载分担。但是对于很多业务来说,仅仅一个逻辑单元内的数据同步往往是不够的,很多业务都需要跨逻辑单元的数据同步能力。例如:同城多个机房同步,异地多个数据中心同步等。容灾和多活是最常见的两种业务场景,还可以扩展到包括读写分离,负载分担等场景。现在越来越多的企业选择将自己的基础业务部署在云上,因此需要云厂商提供这些能力来提高服务质量,更好的服务于用户的业务。此外,混合云场景最近也很火。造成这种情况的原因有很多,包括云厂商的原因和用户业务变化的原因。云供应商之间相互同步的能力。1.1容灾容灾通常用于数据备份,备份数据不对外提供服务,或者只提供读服务。当机房发生故障时,备份数据库代替原有数据库对外提供服务。一般来说,业务的服务管理模块发现原有服务不可用后,会选择代理层进行流量转发,或者使用DNS进行流量切换。机制基本类似,发往原库的流量转发给备库。容灾系统对RPO(故障恢复时间)和RTO(数据恢复时间点)都有要求。例如:故障要求3分钟内恢复,恢复后的数据最多可以容忍1秒丢失。这些都需要数据同步和服务治理发现的配合。如果RPO和RTO要求很高,那么通常不建议进行全量备份。因为全量备份通常是有规律的,比如每天一次,如果在全量备份之前发生故障,意味着将近一天的数据丢失。数据只能恢复到一天前。所以这就需要实时增量的热备能力。因为全量和增量数据是不断同步的,比如:RedisRDB文件和AOF文件,如果Redis服务失败,可以将这些RDB+AOF文件恢复到一个数据库中继续提供服务。再比如:MongoDB的全量Snapshot+Oplog增量也可以还原成一个完整的数据库。此外,备份还可以分为逻辑备份和物理备份。逻辑备份通常是指通过全文件+Binlog文件实现数据库逻辑层面的备份,而物理备份通常是指引擎层面的备份。如上所述,备份的分区有很多种。比如:冷热备份、冷热恢复、物理、逻辑备份、全量、增量备份等,如果对这些概念不清楚的可以自行搜索。另外,我们在本节开头提到,备份也可以提供读服务。对于这种场景,备份的数据不能只是一些文件,比如:RDB文件,AOF文件等,而需要是一个完整可用的服务数据库,所以在这种场景下,实时增量热备份是通常需要。1.2多活Multi-active对于数据库层面来说,是指多个跨逻辑单元的数据库同时对外提供服务,这些不同单元的数据库有部分或全部相同的数据。这通常用于解决区域网络的传输水平带来的问题,即异地多人居住。比如:在业务层面,如果在北京机房写了一条数据,要求在上海机房也能读到这条数据;反之亦然,在上海机房写的数据在北京也能读到。多活模式看起来不错,可以解决很多业务层面的问题,但同时也存在很多问题和挑战:如何解决双向复制的问题?两个数据库相互同步数据,数据难免会形成环路,造成风暴。例如:如果数据库A和数据库B同步,我在数据库A中插入一条数据:insertx。然后这个数据会通过同步链接同步到B数据库,此时B数据库也会插入这个数据:insertx。并且由于反向同步链路的存在,这条数据会被同步回A数据库:insertx。长此以往,数据就会形成一个循环。我们应该做什么?答案是,这种双向复制如果只靠通道层面来解决的话,基本上是行不通的。通常需要配合数据库内核或者业务层面来解决,比如:数据库内核增加一个全局唯一的字段来标识数据产生的地方,通道拉取数据的时候只拉取id所在的数据生成的数据=当前数据库id,断成环。或者在链接级别添加过滤,比如只从源库拉取db=a和b的数据,只从目的库拉取db=c的数据。这样就保证了数据不会形成循环,源数据库和目的数据库都有db=a??,b,c三个数据库的全集数据。然后通过业务层面的配合,将db=a和b库的写入数据分发到源库,将db=c库的写入数据分发到目标库来实现。对于开源数据库模式,第二种模式也是最常见的模式。这个模式我们在后面介绍MongoShake和RedisShake的时候会详细介绍。如何解决数据重写问题?也就是说,如何保证一条数据同时在两个数据库上进行操作,结果是正确的。同样的例子,假设在源数据库A和目的数据库B中有一条数据x=1,我先在A数据库上操作setx=2,然后在B数据库上setx=3,然后最后两个能保证库的结果都是3吗?答案是否定的,因为数据同步就是最终一致性,而最终一致性必然有一个时间窗口。B库操作setx=3时,A库之前的setx=2语句可能还没有同步,所以先setx=3,再setx=2,destination变为2,而source同步setx=3结果变成3,所以数据不一致。当然,我这里只是举个例子,还有很多不一致的地方。所以解决这个问题也需要两个手段,一个是内核层面,一个是业务合作。内核层面的解决方案,比如CRDT,是基于向量时钟来处理消息分发的,所以向量时钟也会造成冲突。处理冲突需要一些手段,比如lastwritewin,clientresolution等等。但是很多数据库对CRDT的支持不够,所以这个方案有比较大的局限性。第二种类似于我在Howtosolvetwo-wayreplication的问题中提到的,将业务流量切分,业务保证一条数据不会同时操作在同步通道的两边.目前数据库层面的多活大多是伪多活,通常需要业务层面的配合来解决。接下来,在MongoShake和RedisShake的章节中,我将讨论这种业务合作方式。2.迁移从广义上讲,迁移应该看作是一种同步模式。同步侧重于增量,而迁移侧重于全量。迁移通常是指数据库的搬迁。源数据库被重定位到目标数据库。迁移后,目标数据库取代源数据库继续提供服务。源数据库可以选择下线或者继续提供服务。迁移的场景有很多,比如:同一个数据库下的异构schema迁移。例如:Redis的主从版本迁移到集群版本;可以是数据库升级迁移,比如:MongoDB从3.0升级到4.0;迁移到云等等。迁移时,源数据库可以是可写的,也可以是不可写的。如果没有,它会更容易一些。迁移往往不需要增量迁移,只需要做全量迁移,但这通常需要业务暂停,这对很多业务来说是无法接受的。如果能写出来,更适合大部分业务场景,但是对于迁移的环节,需要具备全量+增量迁移的能力。迁移完成后,用户可以验证迁移后的数据,发现没有问题。等待某个业务时间点闪断断流,将流量分发到目的端,数据迁移完成,源库可以offlineup。上面我提到了迁移可以用于混合云场景从云外到云上,从云上到云外的迁移。但是现在对于很多云厂商来说,从云到云的迁移可能比从云到云的迁移更难,因为从云到云的同步需要能够从云端拉取数据。对于某些数据库,这些拉取权限可能未打开。比如Redis的Sync迁移,需要源端开放Sync/Psync权限,但是很多云厂商从安全角度来说并不支持。这对迁移工具提出了另一个挑战,应对这一挑战的方法是云供应商支持这种模型,或者另一种迁移方法。对于阿里云,开放了用户的复制权限,允许用户通过Sync/Psync拉取数据。此外,RedisShake本身还支持其他绕过Sync/Psync的同步迁移方式,后面会介绍。3、MongoShake&RedisShake同步迁移工具阿里云开源了MongoShake和RedisShake,可用于MongoDB和Redis的同步迁移,进一步满足用户容灾多活的需求。3.1MongoShakeMongoShake的同步是基于Oplog实现的,v1.5版本支持全量同步??。其内部实现细节可以参考我在云栖社区写的文章。本节主要介绍功能和应用场景。项目地址:http://t.cn/AiTChwoV首先介绍一下主要功能:全量同步:从源端拉取全量数据,写入到目的端。增量同步:从源端拉取增量数据,写入到目的端。MongoShake作为一个同步迁移工具,最主要的功能肯定是数据同步。实现数据同步的基础是全量和增量的相加。我们上面提到的容灾和多活功能就是基于这两者实现的。过滤:MongoShake支持按库按表过滤数据,这样就可以实现我们前面提到的双向同步场景。并行复制:MongoShake内部采用并行复制策略,最高QPS可达40W。压缩:MongoShake支持数据压缩,通常用于远距离传输场景。HA:MongoShake可以支持集群HA切换,用户可以启动多个MongoShake来启动主备集群。断点续传:MongoShake使用持久化的checkpoint信息支持同步链路断开重连后的数据重连和传输。灵活的多通道支持:MongoShake不仅支持将源数据写入目标数据库,还支持TCP/RPC/File/Kafka等多种通道,满足用户的不同需求。例如:MongoShake可以将数据写入Kafka,用户可以从Kafka中拉取数据,再接入流式计算平台,满足实时计算需求。用户甚至可以自定义频道类型以满足特殊的业务需求。下面简单罗列一下。我们可以用这些功能做什么?或者说,应用场景是什么?MongoDB集群的异步复制减少了业务重写的开销。MongoDB集群的容灾、多活、读写分离等业务部署。基于MongoDBOplog的日志分析平台。基于MongoDBOplog的日志订阅。用户可以从例如Kafka通道中拉取日志,然后订阅感兴趣的日志。MongoDB集群的数据路由。根据业务需要,结合日志订阅和过滤机制,获取感兴趣的数据,实现数据路由的功能。基于日志的集群监控。缓存反向同步。通过日志分析的结果,可以知道哪些缓存可以淘汰,哪些缓存可以预加载,从而反向推动缓存的更新。以上只是简单列举了几种应用场景。如果您有不同的玩法或不同的业务需求,请联系我。MongoShake产品还在迭代更新中,未来会有很多好用有趣的功能。添加。3.2RedisShakeRedisShake的同步是基于向源Redis发送Sync/Psync命令,然后实现全量+增量拉取和回放。同样的,具体可以参考我在云栖社区发表的博客。本文主要从功能的角度进行介绍。项目地址:http://t.cn/E6hqgijRedisShake目前有以下五大功能:Dump:从源Redis下载全量RDB文件。Decode:解析指定的RDB文件。Restore:目标数据库根据指定的RDB进行全量恢复。Sync:支持数据同步。源可以是单机Redis、主从Redis、集群Redis,也支持Codis。目的地也可以是各种模式下的Redis。Rump:支持源码扫描和全量迁移。这主要是为了应对部分云厂商未开放Sync/Psync权限时全量迁移的场景。RedisShake的Sync模式是目前使用最广泛的模式,它使用RDB进行全并发同步。以及增量异步写入来提高同步性能,理论上可以实现毫秒级的同步延迟。此外,用户还可以根据redis-full-check在数据同步后进行一致性校验,保证数据的正确性。RedisShake的场景主要是基于同步。如果用户有特殊需求,请告诉我们,比如像MongoShake这样的离线计算场景。目前RedisShake处于开源阶段,功能点迭代比较快。欢迎大家关注。4.使用案例本节主要介绍用户基于我们的MongoShake和RedisShake的使用案例4.1高德地图高德地图App是国内领先的地图导航应用,阿里云MongoDB数据库服务为部分功能提供存储。application支持和存储亿级数据。目前,高德地图采用国内双中心策略,通过地理位置等信息路由最近的中心,提升服务质量。业务端(高德地图)通过用户路由到三个城市数据中心。取决于计算。这三个城市在地理上从北到南横跨整个中国,这对如何复制和容灾多个数据中心提出了挑战。如果某个区域的机房或网络出现问题,流量可以平滑切换到另一个地方,让用户几乎毫无察觉?我们目前的策略是拓扑采用两个机房互联的方式,每个机房的数据都会同步到另外两个机房。然后通过高德的路由层,将用户的请求路由到不同的数据中心,所有的读写都发送到同一个数据中心,保证一定的事务性。然后通过MongoShake,将两个数据中心的数据进行双向异步复制,使每个数据中心都有全量的数据(保证最终一致性)。如下图所示:如果任意一个机房出现问题,另外两个机房中的一个机房切换后可以提供读写服务。下图是市1和市2机房的同步情况。当遇到某个单元无法访问的问题时,通过MongoShake对外开放的Restful管理接口,可以获取各个机房的同步偏移量和时间戳,判断异步复制是否在某个点通过判断采集写入值的时间已经完成。然后配合业务方的DNS流量切换,切断单元流量,保证原单元的请求可以在新单元读写,如下图。4.2某跨境电商某跨境电商在国内和海外分别部署了两套MongoDB。海外主库提供读写服务。同时,用户希望将海外数据拉到国内进行离线计算,并承担部分读写服务。流量,以下是用户使用MongoShake搭建的链路解决方案:4.3某知名游戏厂商某知名游戏厂商使用MongoShake搭建异地容灾链路。用户在两个机房部署了两套应用。一般情况下,用户流量只是通过北向DNS/SLB访问主应用,然后访问主MongoDB。两个机房的数据库之间通过MongoShake进行数据同步。机房1不可用,DNS/SLB将用户流量切换到备用服务器,然后继续对外提供读写服务。4.4基于Shake的开源多活解决方案下面介绍我们基于Shake创建多活的解决方案,包括MongoShake和RedisShake。上面我们介绍了在开源的MongoDB下,可以通过控制流量分布来实现多活需求。比如下图中,用户需要写一个Proxy来分发流量(红框)部分流量。比如a库和b库的写操作分发到左边的DB,c库的写操作分发到右边的DB,源库去目标库的Shake链接只同步库a和b(MongoShake和RedisShake都提供库过滤功能),目标库到源库的Shake链接只同步库c。这样就解决了循环复制的问题。综上所述,写流量通过proxy固定策略分发,而读流量可以随意分发到任意DB。4.5使用Shake的级联同步方案这是一个全球部署用户使用Shake构建的全球混合云级联方案示例图。有的数据库在云端,有的在云端。Shake提供了混合云中不同云环境的同步,也可以直接级联集群同步。5.小结小结主要介绍了数据库同步和迁移的场景,然后结合功能和应用场景介绍了我们的两款开源Shake工具。目前我们的Shake工具还在不断的功能迭代中。欢迎关注我们的Github。如果您有任何问题,请在Github上提问。也欢迎您分享您的使用场景,帮助我们更好地改进我们的产品。
