
郑涔,花名明俨,阿里云数据库组高级技术专家,主要关注分布式存储、Nosql数据库等技术领域,目前主要参与MongoDB云数据库的研发,致力于让开发者用上最好的MongoDB云服务
8月30号,受邀在DBAplus118期线上分享了《从零开始搭建MongoDB数据库服务》。以下为分享主要内容。 分享大纲 什么是『数据库即服务』 MongoDB 如何搭建一个MongoDB『数据库即服务』 什么是『数据库即服务』 首先介绍一下『数据库即服务』。『数据库即服务』其实是『Database-as-a-service』的中文翻译,我们看看它在维基百科中的定义:『数据库即服务』是这样一种服务模式,它使得应用开发者不再需要自己安装和维护数据库,而是由专门的数据库服务提供商来做,应用开发者可以根据自己的需要去直接使用数据库服务,并为使用量支付费用即可。我们知道,现在有很多的as-a-service,比如Infrastructure as a Service(IaaS)、Platform as a Service(Paas)还有Software as a Service(Saas)。他们到底都是什么呢?有什么区别? 这张图应该可以很好的解释这些X-aaS。最左边是传统企业的IT,所有的活都要自己干,从数据中心服务器到操作系统数据库再到上层业务系统。IaaS开始就进入云计算的范畴了,最基础的是云服务器,不需要再关心机房啊硬件拉,直接就可以用。然后再往右客户需要关注的越来越少,脏活累活都交给服务提供商来干。那么『数据库即服务』的情况是怎么样呢? 『数据库即服务』其实可以认为是PaaS的一种变种,主要关注点在数据库上,客户不再需要去自己部署数据库,而是只需要按需使用由服务提供商提供的数据库即可,数据库的维护都交给服务提供商来完成,这样客户只需关注应用本身即可。 我们来具体看一下使用『数据库即服务』和原来有什么不同,这里除了列举传统全部DIY的方式之外,还对比了一种利用IaaS来自建数据库的方式,这也是现在比较常见的一种做法。我们看到传统方式,需要做很多事情,这当中还需要涉及多个团队来协作,非常不容易。然后看看第二种方式,利用IaaS来自建,这里以阿里云的云服务器ECS为例,这种方式和刚刚相比,省了不少事,但是仍然是比较麻烦的,也可能还需要涉及跨团队协作。我们再来看看如果是使用『数据库即服务』呢?只需要点下页面上的部署按钮,就可以等着用了,已经进化为完全自助服务了。从时间上来看,第一种方式可能需要花费数月,第二种可能需要花费数天,第三种则只需要数小时即可。可见『数据库即服务』的优势还是很明显的。 所以说为什么要『即服务』,其实是一个进化的趋势。我们经常说人不能太懒,但是懒这个字用在程序猿身上可能并不是不好的东西,因为懒,促使我们会去自动化。最早我们通过人肉操作,下载软件,编译部署,然后配置。有一天我们发现经常需要这么干很累很浪费时间,就开始写脚本来完成这些操作,生产力开始提高。等到规模更大的时候,比如要同时管理数十台数百台机器,这时候可能分发脚本也嫌麻烦了,开始写一些自动化的工具来做这个事情。到最高级阶段,就是完全实现自助服务,这是懒的最高境界。 MongoDB简介 说完了『即服务』以及其重要性,接下来我们看一下今天的另一个主角:MongoDB,因为有些同学可能对这个不了解,所以还是简单介绍一下。 首先,MongoDB是什么呢,它是一个Document Store,文档型数据库,也是我们经常说的NoSQL。根据DB-Engines的数据库排名,MongoDB长期霸占着NoSQL老大的地位,现在是数据库界一位重量级选手。 事实上,MongoDB可以称为是一种NewSQL,它融合了传统关系型数据库和NoSQL的一些优点。最左边的3个能力是来自于关系型数据库。首先,它具备丰富的查询语句和二级索引。通过这点,用户可以以足够复杂的方式来访问和组织数据。第二点,强一致性。MongoDB支持一个灵活的一致性模型。你可以选择使用强一致性,或最终一致性,取决于你的业务场景。第三点,MongoDB能很好的集成到多企业现有技术架构中。右边3个能力 来自NoSQL,首先是灵活的数据模型,MongoDB的文档模型允许动态修改schema,不用担心有任何的性能影响。其次是高性能和高可扩展性,MongoDB可以轻松进行水平扩展,从而带来更高的吞吐和更低的延迟。最后,是全球部署,也就是高可用。接下来我们就来具体讲下MongoDB的几个关键特性。 MongoDB的关键特性主要是3个,第一个就是灵活动态的文档模型,第二个是高可用副本集,第三个是MongoDB的水平扩展,也就是sharding。 MongoDB以一种叫做BSON(二进制JSON)的存储形式将数据作为文档存储。具有相似结构的文档通常被组织成集合。可以把集合看成类似于关系数据库中的的表:文档对应的是行,字段对应的是列。MongoDB将一条记录的所有数据聚合在一个文档中,而在关系数据库中则倾向于将数据分布在多个表中。这样做有几个好处,一是由于数据聚集,减少了多表JOIN的需求,这样只需要读一次就可以读到所有数据,在性能上会有很大优势。另外,这种模型更加接近我们平时编程语言中的对象结构,可以方便开发者进行数据映射。最后就是这种模型是schema-less的,也就是在MongoDB中不需要像关系数据库一样去事先定义每个表的schema。MongoDB一个集合内的文档之间可以拥有不同的结构,可以轻松为一个新的文档添加和减少字段,不会有任何的性能影响。这个特性非常适合开发一些新产品,可以快速迭代。当然,过于灵活就可能导致混乱。有时候我们想要求文档必须要有某些字段,某些字段必须要有固定的类型。为此,MongoDB提供了一个文档验证功能来对文档的格式进行约束。 接下来说MongoDB的第二个关键特性,高可用副本集(也可以翻译成复制集)。Mongodb副本集由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点,Mongodb Driver(客户端)的所有数据都写入Primary,Secondary从Primary同步写入的数据,以保持副本集内所有成员存储相同的数据集,提供数据的高可用。 上图是一个典型的Mongdb副本集,包含一个Primary节点和2个Secondary节点。副本集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化,初始化后各个成员间开始发送心跳消息,并发起Primary选举操作,获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。这里『大多数』的定义是副本集内可投票成员的一半以上,当副本集内存活成员数量不足大多数时,整个副本集将无法选举出Primary,此时副本集将无法提供写服务,处于只读状态。通常建议将副本集成员数量设置为奇数,因为偶数个节点能容忍的节点失效和比他少1个节点的奇数个节点是一样的,但是可以节省一个节点的数据存储成本。 除了初始化的时候会进行选举,MongoDB副本集的高可用服务体现在,当副本集中没有Primary节点时,选举都会进行。比如当Primary节点宕机时,剩下的Secondary节点中会选举出新的Primary(只需要满足大多数成员存活的条件)。选举使用的算法是基于Raft协议,但是可以通过为节点配置选举优先级对选举结果进行控制。 此外需要提一下,有一些比较常见的特殊的Secondary。一个是Hidden,Hidden节点和普通的Secondary的区别是它是对Driver隐藏的节点,也就是客户端无法访问到Hidden,另外就是它的选举优先级是0,也就是它不能被选举为Primary。Hidden节点上拥有数据,因此通常会用来作一些运维任务,如数据备份、计算分析等。另外还有一个是Arbiter,Arbiter是只参与投票,但是不存储数据的节点,这可以用在对可用性有要求,又要严格控制成本的场景。此外还有如Priority0节点、Delayed节点等。 MongoDB提供了一种水平扩展的方式,叫做sharding,通过这种方式对数据库进行扩容,对应用是透明的。通过sharding,可以将一个集合的数据散到多个shard节点上。这里每个shard都可以是一组副本集。应用程序通过一个路由节点(mongos)来访问sharding集群的数据。有了sharding,mongodb就可以突破单机的限制,比如磁盘、内存和IOPS等,从而提供更强大的服务能力。 Sharded cluster由Shard、Mongos和Config server 3个组件构成。Mongos本身并不持久化数据,Sharded cluster所有的元数据都会存储到Config Server,而用户的数据则会分散存储到各个shard。Mongos启动后,会从config server加载元数据,开始提供服务,将用户的请求正确路由到对应的Shard。Sharded cluster支持将单个集合的数据分散存储在多个shard上,用户可以指定根据集合内文档的某个字段即shard key来分布数据,目前主要支持2种数据分布的策略,范围分片(Range based sharding)或hash分片(Hash based sharding)。范围分片下,文档是根据其shard key的值进行分片。shard key的值相邻近的文档比较有可能会被放在同一个shard上,这种方式适用于需要使用范围查询的业务。哈希分片下,文档根据其shard key的hash值进行分片。这会保证数据分布比较均匀,但是不利于范围查询。随着数据量的增多,MongoDB也会自动在后台对数据以chunk为单位进行负载均衡。 如何搭建一个MongoDB『数据库即服务』 接下来介绍一下今天的重点内容,如何搭建一个MongoDB数据库即服务。 首先,在我看来,数据库即服务,应该具备这些特性:自动化、按需服务、弹性、安全、高可用和可量化。第一个,自动化,这是非常关键的,是实现自助服务的基础,所有可以被自动化操作的流程都应该被自动化,不需要人工干预。第二个,按需服务,数据库即服务应该是由用户驱动的,后台应该要有一个工作流的机制来对需求进行响应。第三个,弹性,可以按需动态扩缩容。第四个,安全,这是毋庸置疑的。第五个,高可用,宕机自动切换。第六个,可量化,服务的使用量可以被衡量、报告并且是可控的。 此图为数据库即服务应具备的功能大图。主要包括生命周期管理、容灾体系、监控报警、数据管理和增值服务。生命周期管理包括数据库实例的新建、释放、扩缩容等,这是数据库即服务最基础的功能。容灾体系包括高可用、备份恢复,甚至更高级的如异地容灾/多活等等。监控报警一方面就是服务使用量的监控,另一方面则是报警,包括服务不可用的报警,以及一些监控数据异常的报警。数据管理指的就是可以方便的对数据进行管理,如可以提供一些图形化界面等。增值服务包括审计、诊断服务等。其中审计是数据库的一个非常重要的功能,一方面可以帮助查证问题,另一方面可以为一些数据分析或诊断提供数据源。诊断服务一方面跟踪服务的资源使用量,为是否需要扩缩容提供决策依据,另一方面主要为慢查询提供优化建议。 数据库即服务的核心架构就是工作流引擎,这是实现自动化及按需服务的基础。 生命周期管理功能包括数据库实例的新建、释放、扩缩容以及迁移。新建一个数据库实例包括分配资源(主要是主机资源)、安装数据库、初始化配置。对MongoDB来说,副本集涉及多个节点,涉及到资源的分配策略,Sharding更多。另外副本集还需要一些初始化工作,sharding需要有一个各组件的组合。释放实例比较简单,主要是资源的回收。扩缩容可以分为本地和跨机的扩缩容,其实跨机的扩缩容就是迁移。对于MongoDB来说,迁移可以直接利用MongoDB的添加节点自动同步的特性,还是比较方便的。 生命周期管理功能主要涉及这几个组件,包括资源管理、规格及配置管理、软件栈管理和负载均衡。资源管理主要是指主机资源的管理,这里主机可以是物理机,也可以是虚拟机,如云主机等。资源管理主要负责资源的分配和回收,此外还包括如何实施资源隔离。规格及配置管理一个是需要为数据库实例制定一些规格,以方便扩容和缩容,另一方面是负责数据库相关配置的维护。软件栈管理则包括数据库软件以及其依赖的软件的安装维护等,包括操作系统。除了这些,还需要一个负载均衡组件来保证数据库实例在资源上的分布的均衡,当有主机资源需要下线的时候,能够做到自动对其上的数据库实例进行迁移。 对于MongoDB来说,在实施资源分配策略时需要注意的一点是需要保证不要破坏副本集原本的高可用特性。虽然MongoDB副本集自带了高可用,但是如果你把副本集的所有节点都分布在一台物理机上,那如果这个物理机挂了,整个副本集都没用了。所以一个起码的原则是要保证MongoDB多副本的主机安全性,尽可能够做到机架安全。 现在我们的MongoDB数据库即服务的架构可以稍微扩充一下了,多了资源服务、规格及配置服务、软件栈服务以及负载均衡服务这几个组件。 接下来看一下容灾体系,这包括高可用、备份/恢复、异地容灾/多活。高可用需要有一个负责健康检查的巡检服务,另外还需要有一个容灾切换的组件。备份/恢复也是容灾体系非常重要的一环,这里有一个很容易被忽视的事情是需要做备份的有效性验证。如果备份不是有效的,那等于没有备份。异地容灾/多活是比较高级的容灾能力,实施起来比较复杂,有兴趣的同学可以参考我之前做过的一个分享 MongoDB异地容灾多活实践 MongoDB副本集自带了高可用,我们还需要做什么工作呢?主要是需要保证容灾切换的一个可控。以一个经典的3节点P/S/H副本集为例,一方面我们可以通过配置选举优先级的方式来保持Primary和Secondary的角色稳定性。另一方面,我们希望在任意时刻,用户都可以有两个节点是可访问的,因此我们需要对节点宕机后的副本集做一些reconfig操作,保证宕机节点最终都会变成Hidden,然后统一对Hidden进行处理,比如重搭等。 容灾体系第二个比较重要的点就是备份恢复。备份主要需要做的是需要提供自动/手动的备份方式以及支持一些灵活的备份策略制定,如备份周期/备份保留时间等。恢复主要是看对恢复的形态做成什么样,是覆盖原来的实例还是克隆出一个新的实例来,还有就是恢复的粒度,这取决于备份能力,是只能恢复到某个全量备份,还是可以恢复到任意时间点。关于备份存储,我们要求的最要 能力是高可靠性。另外就是刚刚提过的备份有效性验证,不能等到火烧眉毛了才发现备份不可用,需要防范于未然。 关于MongoDB的备份方法,相关的文档和分享已经有很多了,这里再简单提一下。全量备份从实施方式上可以分为两种,逻辑备份和物理备份。其中逻辑备份主要使用官方提供的mongodump/mongorestore工具。物理备份则可以在文件系统或是更底层的逻辑卷、块设备这层去做。 从各个指标上对比逻辑备份和物理备份,在备份和恢复效率上,物理备份的优势比较明显,不过逻辑备份在兼容性上会比较好。 MongoDB的增量备份主要通过持续的抓取oplog来实现。有了全量备份加增量备份,就可以实现恢复到任意时间点。 至此,我们的MongoDB数据库即服务的架构又可以得到一个比较大的扩充,主要增加了高可用以及备份相关的一些服务。 接下来看下数据库的监控报警,性能监控主要涉及性能数据的采集、存储和展示。采集粒度越细越好,最好能做到秒级。报警则可以分为可用性的报警和性能数据的报警。 具备监控报警能力后的架构图已经有点满了,这里报警服务可以通过巡检服务和性能数据存储收集相关数据。 来看最后一个增值服务,一个是审计,主要涉及审计日志的采集、存储和分析。另一个是诊断服务,一个是资源使用量上的诊断,另外一个是慢查询的诊断,可以做一些索引推荐等。 这就是我们的MongoDB数据库即服务的完整架构,可以看到组件还是比较多的,做一个数据库即服务还不是那么容易哈。 最后做一下总结,我认为数据库即服务的核心特性有两点,一个是资源池化,另外一个是服务可量化。资源池化后才可以进行资源的自动管理,而我们需要的服务是要能够被量化的,并且是可控的。现在回顾一下之前的一键安装数据库,其实背后有许多工作要做。这里顺便做下广告,如果觉得自己搭建一个数据库即服务太麻烦,可以考虑使用现成的云服务,比如阿里云MongoDB数据库服务:)你想要的全都有!
背景 数据迁移是数据库运维中一个很常见的场景。数据迁移分为全量和增量。为了追求速度,通常我们会采用并发的方式对数据进行全量迁移。在全量导出数据时,通常都会选择做到记录级的并发,因此通常会涉及到对需要导出的某个表(集合)按照并发度进行切分(分区)的过程。现有常用做法是通过若干个skip加limit来找到一些分区点,然后就可以并发同时导出多个分区。事实上MongoDB还有一个SplitVector命令特别适合用来做集合的分区。本文将介绍一下如何利用这个命令来对集合做分区,实现并发数据迁移。 命令简介 SplitVector命令原是在sharding中chunk分裂时需要用的一个内部命令,是mongos在准备分裂某个chunk前发给这个chunk所在shard以计算分裂点(SplitPoint)时使用的。但是这个命令也可以用于普通的副本集,我们可以把副本集中的集合看作一个唯一的chunk,利用这个命令来为这个chunk计算分裂点,从而达到为某个集合进行分区的目的。 SplitVector命令的使用在官方文档中没有介绍,只说明了其实一个内部命令,但是使用命令的Help却可以看到: db.runCommand({splitVector:"test.test", help:1}) { "help" : "help for: splitVector Internal command.\nexamples:\n { splitVector : \"blog.post\" , keyPattern:{x:1} , min:{x:10} , max:{x:20}, maxChunkSize:200 }\n maxChunkSize unit in MBs\n May optionally specify 'maxSplitPoints' and 'maxChunkObjects' to avoid traversing the whole chunk\n \n { splitVector : \"blog.post\" , keyPattern:{x:1} , min:{x:10} , max:{x:20}, force: true }\n 'force' will produce one split point even if data is small; defaults to false\nNOTE: This command may take a while to run", "lockType" : 0, "ok" : 1 } 从帮助文档中可以大致看到,这个命令大致是这么使用的: db.runCommand({splitVector:"blog.post", keyPattern:{x:1}, min{x:10}, max:{x:20}, maxChunkSize:200}) 接下来介绍一下各个参数及其含义。 字段 类型 描述 splitVector string splitVector的操作对象集合名 keyPattern document chunk分裂使用的分区键,必须拥有索引,在sharding中就是shard key,在副本集中通常就指定成主键_id索引 min document 可选参数,分区数据集的最小值,如果没有指定,那么使用MinKey max document 可选参数,分区数据集的最大值,如果没有指定,那么使用MaxKey maxChunkSize integer 可选参数,和『force』参数二者必须指定一个。分区后每个chunk的最大大小 maxSplitPoints integer 可选参数,分裂点个数上限 maxChunkObjects integer 可选参数,分区后每个chunk最大包含的记录数,默认为250000 force boolean 可选参数,和『maxChunkSize』参数二者必须指定一个。默认情况下如果当前chunk的数据大小小于maxChunkSize则不会进行分裂。如果指定了『force』为true,那么会强制在当前chunk的中位点进行分裂,返回一个分裂点。默认为false。 这么多参数到底怎么用呢?我怎么知道出来的结果是怎样的?没有更详细的文档,只有啃源码了。 原理 SplitVector的原理是遍历指定的『keyPattern』索引,根据指定的『maxChunkSize』找到满足以下条件的n个分裂点:分裂后的每个新的chunk的大小约为『maxChunkSize』的一半。如果集合当前大小比『maxChunkSize』小或者集合记录数为空,那么返回一个空的分裂点集合。如果指定了『force: true』,那么会忽略传入的『maxChunkSize』参数,并强制在集合的中位点进行分片,这时候只会产生一个分裂点。在寻找分裂点时首先会根据集合的平均文档大小计算一个分裂后每个chunk所包含的文档数: keyCount = maxChunkSize / (2 * avgObjSize) 如果指定了『maxChunkObjects』参数,并且『maxChunkObjects』比keyCount小,会使用『maxChunkObjects』作为keyCount。接下来就是遍历索引,每遍历keyCount个key,就得到一个分裂点(第keyCount+1个key),直到达到『maxSplitPoints』(若有指定)或遍历结束。因此其实每个partition会包含keyCount个key,最终得到的分裂点个数: partitionCount = keyTotalCount / (keyCount + 1) splitPointCount = partitionCount - 1 其中keyTotalCount为索引的key总数。 使用案例 知道了原理后,就知道如何去传参数了,如果要精确控制得到的分裂点个数(以便控制并发数),这里可以给出一个公式及推导过程。现在我们有以下公式: 1. splitPointCount = partitionCount - 1 2. splitPointCount = keyTotalCount / (keyCount + 1) 3. keyCount = maxChunkSize / (2 * avgObjSize) 由上述公式可以推导出 maxChunkSize = (keyTotalCount / partitionCount - 1) * 2 * avgObjSize 由于所有集合都有_id字段上的唯一索引,并且每个文档都有_id字段,因此我们可以直接利用集合文档的个数docCount作为索引key的个数。文档个数和avgObjSize都可以通过collStats命令得到。注意参数中的『maxChunkSize』是以MB为单位的,最终传到命令的时候需要转换一下,并且在服务端中事实上会将『maxChunkSize』做个向下取整,因此最终计算出来的keyCount可能比我们设想的要小,这样就会导致最终得到的分裂点个数比我们想要的多。为了达到我们的需求,最好加上『maxSplitPoints』这个可选参数对分裂点进行限制,这样我们允许最后一个分区比其他分区包含更多的文档数。 接下来举个具体的例子,假设现在需要将某个集合分成10个分区以支持10个并发同时对外导出数据,这个集合共有10240条文档,avgObjSize是1024,那么根据上述公式可以计算得到: maxChunkSize = (10240 / 10 - 1) * 2 * 1024 = 2MB 这样我们执行如下命令: db.runCommand({splitVector:"test.test", keyPattern:{_id:1}, maxChunkSize:2, maxSplitPoints:9}) 这样就会只得到9个分裂点。
5月21号,我在上海参加DBAplus社群举办的云数据库架构设计与实践沙龙,做了《MongoDB异地容灾多活实践》的分享,本文是分享的PPT。
云数据库 MongoDB 版 基于飞天分布式系统和高性能存储,提供三节点副本集的高可用架构,容灾切换,故障迁移完全透明化。并提供专业的数据库在线扩容、备份回滚、性能优化等解决方案。 了解更多 最近数据库真是多灾多难,前段时间刚刚爆出MongoDB数据库安全问题(见MongoDB黑客赎金事件解读及防范)。这两天又被炉石传说数据库故障给刷屏了。 看到这个消息,我的第一反应是重新翻出尘封已久的ipad,装上炉石准备上线领补偿。等等,作为一个数据库行业从业人员,是不是还应该干点什么?恩,很有必要再重新审视一下我们的数据库有没有做好容灾,否则,今天你看别人热闹,明天可能就别人看你热闹了。借此机会我想给大家普及一下MongoDB数据库的备份和恢复手段(当然炉石传说应该不一定是使用MongoDB作为数据库),以帮助大家做好容灾,过个好年。同时,我也为我们阿里云MongoDB服务做下广告,我们的MongoDB服务拥有完善的自动备份/恢复功能,灵活的备份策略,好用的恢复到任意时间点功能。我们的备份存放了多份副本,可靠性达10个9,请大家放心使用! MongoDB数据库备份手段 全量逻辑备份/恢复 Mongodump/Mongorestore 对于数据量比较小的场景,使用官方的mongodump/mongorestore工具进行全量的备份和恢复就足够了。mongodump可以连上一个正在服务的mongod节点进行逻辑热备份。其主要原理是遍历所有集合,然后将文档一条条读出来,支持并发dump多个集合,并且支持归档和压缩,可以输出到一个文件(或标准输出)(对原理感兴趣可以参见我之前写的两篇文章Mongodump的archive(归档)模式原理解析以及Mongorestore的archive(归档)模式恢复原理解析)。同样,mongorestore则是连上一个正在服务的mongod节点进行逻辑恢复。其主要原理是将备份出来的数据再一条条写回到数据库中。 对性能的影响 mongodump执行过程由于会遍历所有数据,因此会对MongoDB性能有影响,最好在备节点执行(最好是hidden,需检查备节点数据同步是否正常)。 获取一致的数据快照 在mongodump执行过程中由于数据库还有新的修改,直接运行dump出来的结果不是一个一致的快照,需要使用一个『--oplog』的选项来将这个过程中的oplog也一块dump下来(使用mongorestore进行恢复时对应要使用--oplogReplay选项对oplog进行重放)。而由于MongoDB的oplog是一个固定大小的特殊集合,当oplog集合达到配置的大小时旧的oplog会被滚掉以为新的oplog腾出空间。在使用『--oplog』选项进行dump时,mongodump会在dump集合数据前获取当时最新的oplog时间点,并在集合数据dump完毕之后再次检查这个时间点的oplog是否还在,如果dump过程很长,oplog空间又不够,oplog被滚掉就会dump失败。因此在dump前最好检查一下oplog的配置大小以及目前oplog的增长情况(可结合业务写入量及oplog平均大小进行粗略估计),确保dump不会失败。目前我们阿里云MongoDB服务针对oplog做了弹性扩缩容的优化,能够确保在逻辑备份过程中oplog不被滚掉,一定能够备份成功。 索引的备份和恢复 对于集合数据,mongodump出来的结果是一个个bson文件。而对于集合的索引,则是描述在一个metadata的json文件里,里面还包含创建集合时所使用的选项。在使用mongorestore进行恢复时,会在集合数据恢复完毕之后进行对应的索引创建。 全量物理备份/恢复 对于数据量很大的场景,如果使用mongodump/mongorestore进行备份和恢复,需要的时间可能会很长。对于备份来说,最主要的问题就是备份所需时间越长,oplog被滚掉的几率就越大,备份失败的几率也就越大。而对于恢复来说,由于恢复过程还涉及到索引的创建,如果除了数据量大,还有很多索引,所需花费的时间就更长了。遇到像炉石这种数据灾难,恢复时间当然是越短越好,毕竟在游戏行业分分钟的流水都很可观。这时候就需要物理备份出场了,物理备份,顾名思义就是通过物理拷贝数据文件实现备份。在恢复时可以直接使用物理备份拷贝出来的数据文件,直接启动mongod。物理备份最大的好处是速度快,恢复时也不需要再建索引。 实施方法 物理备份通过拷贝数据文件来实现,这要求所有被拷贝的数据文件必须是一个一致的数据快照。因此物理备份的实施方法和MongoDB采用的存储引擎有关,并且,根据是否配置MongoDB打开了Journal,在实施的细节上会有一些不同,具体可参考官方文档。不管使用何种存储引擎,在3.2版本之后,都可以用以下方法实现物理备份: 通过mongoshell执行以下命令以确保所有的写操作都flush到磁盘并禁止新的写入: db.fsyncLock(); 利用底层文件系统层或逻辑卷的快照功能对MongoDB的数据目录做快照,或直接通过cp、scp、tar等命令拷贝数据目录。 还是在刚才的mongoshell上(这里需要保证和刚刚是同一个连接),执行以下命令以重新允许新的写入: db.fsyncUnLock(); 由于执行db.fsyncLock()会加数据库的全局写锁,这时数据库会处于一个不可访问的状态,因此物理备份最好也在备节点上执行(最好是hidden,注意同样需要确保物理备份完成之后节点的oplog能追上主节点)。目前我们阿里云MongoDB团队已经研发出了无需停写服务的物理热备份手段,相信很快就可以让大家用上,尽请期待! 增量备份 MongoDB的增量备份可以通过持续抓取oplog来实现,这个目前没有现成的工具可以利用,需要自己代码实现。抓取oplog主要的难题也和使用mongodump进行全量备份一样,需确保要抓取的oplog不被滚掉。目前我们阿里云MongoDB服务实现了自动增量备份的功能,结合全量备份可以实现任意时间点恢复功能。 Sharding的备份/恢复 炉石是不分服的,因此它后面也有可能是使用分布式数据库。对于分布式数据库来说,备份和恢复比单机数据库更加复杂。分布式数据库包含多个节点,并且通常包含不同角色的节点。以MongoDB的Sharding集群为例,它包含一个保存元数据的config server以及若干个保存数据的shard。其中最主要的元数据就是数据在shard之间的分布情况。对于多个节点的备份,其中一个难题是保证所有节点备份的数据是同一个时间点的,常规采用的手段是停止外部写入后进行备份,这在互联网服务中显然不可接受。退而求其次,可以在停止接受同步的备节点上进行备份,这样可以得到一个时间大致接近的备份。另外一个难题是各数据节点之间通常存在数据迁移,而数据迁移就涉及到起码2个以上数据节点的数据修改以及元数据节点的数据修改,如果在备份过程中发生数据迁移,很难保证备份出来的数据和元数据是一个一致的状态。因此通常在备份过程中需要关闭数据迁移。MongoDB官方的文档指导步骤就是采用这个思路,先关闭负责数据迁移的balancer,然后依次在config server和各个shard的备节点上进行备份。关闭数据迁移最大的问题是关闭期间集群无法实现数据均衡,除了会影响集群的访问性能外,还造成资源的浪费,这在数据量较大,所需备份时间较长时可能造成比较大的影响。 针对这两大难题,我们阿里云MongoDB团队研发了不需要停外部写,并且无需关数据迁移的Sharding备份手段,能够实现『任意』时间点恢复,这个功能将伴随着即将推出的Sharding形态一起推出,尽情期待! 阿里云MongoDB备份服务 阿里云MongoDB服务提供自动备份/恢复功能,默认每天为数据进行全量备份,并且自动抓取oplog进行增量备份。用户可以在控制台自定义备份策略以及进行恢复。 恢复时可以选择某一个备份集或某一个时间点克隆出一个新的实例,可以在新实例上进行数据校验,等校验没问题后切换到新实例。此外,全量备份的数据还提供下载功能,用户也可以选择下载备份集到本地后恢复到一个临时实例进行数据校验。 总结 说了这么多,大家应该对MongoDB的备份/恢复手段有了一个大概的认识。如果要图省心,还是建议直接使用阿里云MongoDB服务,我们有自动化的备份/恢复服务,灵活的备份策略,只需点点鼠标就能用上任意时间点恢复、物理热备份、Sharding备份/恢复这些黑科技,从此不再为数据库容灾发愁,业务你们做,锅有我们来背,还等什么呢,快来试用吧! 云数据库 MongoDB 版 基于飞天分布式系统和高性能存储,提供三节点副本集的高可用架构,容灾切换,故障迁移完全透明化。并提供专业的数据库在线扩容、备份回滚、性能优化等解决方案。 了解更多
在上篇Mongodump的archive(归档)模式原理解析中介绍过,Mongodump的archive(归档)模式产生的文件是将多个集合的数据通过一个Multiplexer多路复用混合在一起,因此对应在恢复的时候就需要有一个Demultiplexer来将数据进行解析,是一个多路复用的逆过程。对应于mongodump,MongoDB官方提供了mongorestore这个恢复工具。 归档文件的格式 复习一下归档文件的格式,其最前面有4个字节的magic number,然后是元数据部分(prelude),描述这个归档文件包含哪些集合、索引等信息,最后是body部分,由一个个slice组成,每个slice有一个header、若干个body和一个terminator,其中header和body都是一个bson,terminator是一个4字节的标记。如下图所示: 流程 从一个mongodump备份的归档文件中恢复的过程包括读取归档文件,解析成对应集合的数据,然后再恢复到目标MongoDB。为了同时支持非归档模式和归档模式,这里mongorestore做了一些抽象:主要包括Intent、file接口和DemuxOut。 Intent、file接口 Intent是对备份文件的抽象,一个Intent有可能是某一个集合的数据的备份文件(在非归档模式下是一个BSON文件),也有可能是某一个集合的元数据文件(在非归档模式下是一个JSON文件,其中包含集合创建时指定的option以及索引信息等,以.metadata.json结尾)。访问一个Intent需要通过file接口的Open()、Read()、Write()和Close()方法: type file interface { io.ReadWriteCloser Open() error } 在非归档模式下,对应的file接口的实现是realBSONFile和realMetadataFile。这两个实现的Open()方法调用os.Open()打开对应的文件,使用返回的File进行读取数据。 在归档模式下,mongorestore定义了几个特定的file接口实现,来实现边读取并解析归档文件边恢复。 DemuxOut 之前介绍过,对于归档文件,由于是各个集合的数据按条(slice)混合在一起的,这样我们在顺序读取归档文件时就需要有一个Demultiplexer来将数据进行解混合(甚至进行分发以便可以实现多个集合并发恢复)。DemuxOut也是一组接口定义,主要负责定义Demultiplexer解析归档文件后如何输出数据: type DemuxOut interface { Write([]byte) (int, error) Close() error } 常规集合的Demux恢复流程 主协程通过读取归档文件的prelude,得到需要恢复的集合信息,然后为每个集合创建Intent。之后启动一个Demultiplexer协程(以下简称Demux协程)负责读取归档文件的body部分并进行解析,同时会启动N个Restore协程(根据指定的集合恢复并发度)进行恢复。Demux协程解析出某个集合的数据后调用DemuxOut的Write()方法将数据输出去。Restore协程从Intent的file接口读取数据,并执行恢复到目标MongoDB。 对于需要恢复的常规集合(包括oplog集合),mongorestore定义了一个RegularCollectionReceiver实现了file接口,定义了一个regularCollectionSender实现了DemuxOut接口。regularCollectionSender的Write()方法将这次要发送的数据长度通过一个readLenChan发送出去。RegularCollectionReceiver的Read()方法则等待在这个readLenChan上,等待新数据的通知,当收到数据长度通知时发送一个buf给它。Demux协程会将数据拷贝到这个buf中,再次通过readLenChan通知已拷贝的数据长度,然后继续往下解析。Restore协程收到数据拷贝完毕的通知后,将这些数据恢复到目标MongoDB。恢复的速度通常来说跟不上Demux解析的速度,因此RegularCollectionReceiver在必要时会将Demux发送过来的数据缓存起来慢慢消费。这样Demux协程就可以不停的往下解析,并且可以实现多集合并发恢复。 特殊集合的处理 有一些集合在恢复过程中是需要特殊处理的,这里所说的特殊处理,主要是需要在恢复的特定阶段进行处理。比如对于admin.system.version集合,需要在恢复常规集合之前进行auth版本是否兼容的判断。再比如用户和角色集合,是在恢复的最后阶段进行处理的。对于这些特殊集合,mongorestore的处理方法是,如果在归档文件读取并解析的过程中读取到了,会先缓存在一个buffer中,等到需要的时候再进行处理。在实现上,mongorestore针对这些集合定义了特殊的DemuxOut接口的实现:SpecialCollectionCache。这个实现包含一个bytes.Buffer,利用其Write()方法和Read()方法(这也是其对应的file接口的实现)实现集合数据的暂存和读取。为此,mongorestore为每个Intent都维护了一个对应的DemuxOut,以便可以特殊处理。 如何实现恢复的过程中过滤某些集合 有些集合是不需要进行恢复的,包括system.profile、索引以及不满足用户指定的filter条件的集合等。索引之所以不需要恢复是因为索引是统一通过集合的元数据文件中的描述在集合数据恢复完毕后进行重建的。对于这些不需要恢复的集合,对应的DemuxOut接口的实现是MutedCollection。这个实现的Write()、Close()方法都不干任何事情。这样就实现了过滤恢复。 多集合并发时的恢复优先级 如果在恢复的时候指定了多集合并发进行恢复,mongorestore会在恢复前初始化一个集合恢复优先级调度器。在非归档模式下,会使用一个MultiDatabaseLTF的优先级调度器。这个优先级调度器会在优先恢复大集合的同时兼顾不同数据库集合的并发恢复。在归档模式下,基本就是按照集合的数据在归档文件中的顺序进行恢复。在Demux协程发现一个新的需要恢复的常规集合后,会通过namespaceChan通知主协程,并由主协程转发给归档模式下的优先级调度器,在这里调用RegularCollectionReceiver的Open()方法进行相关初始化,以及注册对应的DemuxOut,建立Demux协程和Restore协程的数据流通通道。
--replSet 后面只要跟副本集名称就可以了 不用再把/ip加上
限制用户的权限
不会有副作用
应该都可以
如果数据量很大,对查询的性能要求也高,可以在用户资料更新的时候运行,再保存到一个另外的表,每次查询从那里直接取
你可以是用JavaScript来干这个事,比如在mongo shell里是db.test.find( { $where: "this.x - this.y > 1" })
如果用到很多多表连接查询,mongodb不大适合
从问题看应该是addShard时没有使用副本集连接串