• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

MongoDB分片集群工作原理(一)

MongoDB 彭东稳 8年前 (2016-03-30) 29088次浏览 已收录 1个评论

一、什么是分片?

分片(sharding)是MongoDB用来将大型集合分割到不同服务器(或者说一个集群)上所采用的方法。尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事。和MySQL分区方案相比,MongoDB的最大区别在于它几乎能自动完成所有事情,只要告诉MongoDB要分配数据,它就能自动维护数据在不同服务器之间的均衡。

  • 横向扩展带来性能的提升

高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。当出现这种情况时就不得不考虑一个现实问题那就是性能。目前两种比较常用的两种方式就是“纵向扩展”和“横线扩展”。一般纵向扩展是一个比较粗暴的解决方法是通过升级你的机器来提升单机性能:通过加大内存和添加硬盘来应对越来越大的负载。但是随着数据量的增大,投入在升级机器上的钱将将不会产生原来那么大的效果。这时候你就必须考虑把数据同步到不同的机器上,利用横向扩展来分担访问压力。

横向扩展的目标是达到线性的效果,即如果你增加一倍的机器,那么负载能力应该也能相应的增加一倍。其主要需要解决的问题是如何让数据在多台机器间分布。数据分片技术实际上就是将对数据和读写请求在多个机器节点上进行分配的技术,分片技术在很多NoSQL系统中都有实现,比如CassandraHBaseVoldemortRiak等等,最近MongoDBRedis也在做相应的实现。而有的项目并不提供内置的分片支持,比如CouchDB更加注重单机性能的提升。对于这些项目,通常我们可以借助一些其它的技术在上层来进行负载分配。

  • 分片机制提供了如下三种优势

1. 对集群进行抽象,让集群“不可见”

MongoDB自带了一个叫做mongos的专有路由进程。mongos就是掌握统一路口的路由器,其会将客户端发来的请求准确无误的路由到集群中的一个或者一组服务器上,同时会把接收到的响应拼装起来发回到客户端。

2. 保证集群总是可读写

MongoDB通过多种途径来确保集群的可用性和可靠性。将MongoDB的分片和复制功能结合使用,在确保数据分片到多台服务器的同时,也确保了每分数据都有相应的备份,这样就可以确保有服务器换掉时,其他的副本可以立即接替坏掉的部分继续工作。

3. 使集群易于扩展

当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量。 

下面让我们来整理一些通用的概念,对于数据分片和分区,我们可以做同样的理解。对机器,服务器或者节点我们可以统一的理解成物理上的存储数据的机器。最后,集群或者机器环都可以理解成为是组成你存储系统的集合。

MongoDB分片集群工作原理(一)

分片为应对高吞吐量与大数据量提供了方法使用分片减少了每个分片需要处理的请求数,因此,通过水平扩展,集群可以提高自己的存储容量和吞吐量。举例来说,当插入一条数据时,应用只需要访问存储这条数据的分片.

使用分片减少了每个分片存储的数据。例如,如果数据库1tb的数据集,并有4个分片,然后每个分片可能仅持有256 GB的数据。如果有40个分片,那么每个切分可能只有25GB的数据。

二、MongoDB的分片

MongoDB通过配置集群支持分片,下图简要说明了MongoDB分片所需要的节点。

MongoDB分片集群工作原理(一)

集群拥有三个节点: 分片(sharding),分发路由(query routers)和配置服务器 config server

  • Shard

分片是存储了一个集合部分数据的MongoDB实例,每个分片是单独的 mongod 或者是复制集,在生产环境中,所有的分片都应该是复制集。

  • Mongos

Mongos起到一个路由的功能,供程序连接。本身不保存数据,在启动时从配置服务器加载集群信息,开启mongos进程需要知道配置服务器的地址,指定configdb选项。

  • Config Server

配置服务器是一个独立的mongod进程,保存集群和分片的元数据,即各分片包含了哪些数据的信息。最先开始建立,启用日志功能。像启动普通的mongod一样启动配置服务器,指定configsvr选项。不需要太多的空间和资源,配置服务器的1KB空间相当于真是数据的200MB。保存的只是数据的分布表。

MongoDB分片由这三个节点构成,大概工作模式就是应用程序通过mongos路由到Config Server中取元数据,然后通过元数据到Shard中去数据并返回。

三、数据区分

片键(shard key

MongoDB中数据的分片是、以集合为基本单位的,集合中的数据通过片键(Shard key)被分成多部分。其实片键就是在集合中选一个键,用该键的值作为数据拆分的依据。所以一个好的片键对分片至关重要。片键必须是一个索引,通过 sh.shardCollection 加会自动创建索引(前提是此集合不存在的情况下)。一个自增的片键对写入和数据均匀分布就不是很好,因为自增的片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。随机片键对数据的均匀分布效果很好。注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos 会对结果进行归并排序。

对集合进行分片时,你需要选择一个片键,片键是每条记录都必须包含的,且建立了索引的单个字段或复合字段,MongoDB按照片键将数据划分到不同的数据块中,并将数据块均衡地分布到所有分片中。为了按照片键划分数据块,MongoDB使用基于范围的分片方式或者基于哈希的分片方式,其实本质上都是范围。

  • 以范围为基础的分片

对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。假设有一个数字的片键:想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点。MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块(Chunk),每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中,拥有”相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中。

MongoDB分片集群工作原理(一)

  • 基于哈希的分片

分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。对于基于哈希的分片,MongoDB先根据片键值计算哈希值,然后把哈希值按范围划分,只是比范围法多了一步哈希。在使用基于哈希分片的系统中,拥有”相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些,使用哈希法可以使得键值分布更加均匀。

MongoDB分片集群工作原理(一)

基于范围的分片方式与基于哈希的分片方式性能对比

基于范围的分片方式提供了更高效的范围查询,给定一个片键的范围,分发路由可以很简单地确定哪个数据块存储了请求需要的数据,并将请求转发到相应的分片中。不过,基于范围的分片会导致数据在不同分片上的不均衡。有时候,带来的消极作用会大于查询性能的积极作用。比如,如果片键所在的字段是线性增长的,一定时间内的所有请求都会落到某个固定的数据块中,最终导致分布在同一个分片中。在这种情况下,一小部分分片承载了集群大部分的数据,系统并不能很好地进行扩展。

与此相比,基于哈希的分片方式以范围查询性能的损失为代价,保证了集群中数据的均衡。哈希值的随机性使数据随机分布在每个数据块中,因此也随机分布在不同分片中。但是也正由于随机性,一个范围查询很难确定应该请求哪些分片,通常为了返回需要的结果,需要请求所有分片。

四、数据均衡的维护

新数据的加入或者新分片的加入可能会导致集群中数据的不均衡,即表现为有些分片保存的数据块数目显著地大于其他分片保存的数据块数。对于这种情况,MongoBD使用两个过程维护集群中数据的均衡:分裂和均衡器。

数据块(Chunk

MongoDB分片后,是利用Chunk来存储数据的,默认每一个Chunk的大小为64MB。Chunk(块),理解这个概念很重要,块是比分片更小的单位,每一个shard分片都是由若干个chunk构成的,chunk有以下一些属性:

  • min和max,该chunk里最小和最大的片键,也就是这个chunk的数据范围,这其实是对应了我们之前说的片键区间。片键的每一个区间段都会对应一个chunk。每一个shard的配置信息就会包括其中所有的chunk块的范围,最终mongos就可以根据这些值来判定应该走哪一个切片的哪一个chunk。
  • maxSize,chunk大小的上限,这个和后面的内容有关,是一个阈值,会触发chunk的分割。

你可以调整数据块的大小,但要注意到这有可能会集群造成性能影响。

  1. 数据块大小较小时可以使得分片间的数据更均衡,但是是以频繁的迁移为代价的,会对 mongos 造成压力。
  2. 数据块大小较大时会使得均衡较少,这从网络传输与mongos的角度来说更高效,但是,这种高效是通过数据不均衡的加重为代价的。

在很多情况下,以分片间数据略微的不均衡来防止频繁的迁移或者无效的迁移是合理的。

限制

修改数据块大小影响数据块的分裂,但他的影响受到其他的一些限制。

  1. 自动分裂只在数据插入与更新时发生,因此如果你减小了数据块的大小,需要花费一些时间使所有数据块分裂成新大大小。
  2. 分裂不能被回滚,如果你增加了数据块大小,现有的数据块只有通过插入与更新才能逐渐达到新的设定值。

分裂

分裂是防止某个数据块过大而进行的一个后台任务。当一个数据块的大小超过设定的数据块(chunk)大小时,MongoDB会将其一分为二,插入与更新触发分裂过程。分裂改变了元信息,但是效率很高。进行分裂时,MongoDB不会迁移任何数据,对集群性能也没有影响。

MongoDB分片集群工作原理(一)

均衡

均衡器是一个后台进程,负责管理块(chunk)迁移。会周期性的检查分片是否存在不均衡,如果存在则会进行块的迁移。注意均衡器不会考虑数据块的大小(分割考虑块大小),而是根据数据块的多少进行迁移的。均衡器可以运行在一个集群中的任何实例上。当集群中数据的不均衡发生时,均衡器会将数据块从数据块数目最多的分片迁移到数据块最少的分片上,举例来讲:如果集合users在分片 1 上有100个数据块,在分片 2 上有50个数据块,均衡器会将数据块从分片1一直向分片2 迁移,一直到数据均衡为止。最后对Config Server上块的位置的元数据进行更新。

MongoDB分片集群工作原理(一)

过程如下:balancer会给要迁移的shard发出一个move命令,该shard就会发送自己的数据给目标shard,目标shard就会同步数据,一旦同步完,源shard就会删除数据,最后config里面的配置信息也会被更新。

所以,MongoDB的shard集群为用户提供了一种水平扩展数据的功能,还提供了健壮性和可用性,健壮性是我们可以使用复制集技术来增加容错性,可用性是我们可以随意修改shard集群的节点,shard集群内部会做相应的balance,让每一个shard的空间都均摊所有的数据,其实这个还是很重要的,因为虽然我们可以部署很多机器,但是一旦最后的数据分布-不均匀,全部到了一台机器上,导致这个机器负载太高,装不下数据,应用肯定会挂,但是其余的机器却是空的,所以这里的balance功能非常重要。

实现思路大致是,首先保证每一个shard的chunk数目相当,但是光有这一点还不行,因为可能某一些chunk很大,所以还必须保证每一个chunk的大小均匀,这是通过split来实现,基于这两点,就可以保证每一个shard的大小相当。为什么shard的数据要以一个一个的chunk来组织?如果不以chunk来组织,那么如果要平衡数据,只能以shard为视角,把shard的一部分数据移到另一个shard,表面上好像也没什么,甚至更简单,但是实际上,比较难做到,因为当我们想把一个shard的一部分切下来时,该如何修改shard的片键范围呢?这是比较复杂的问题,切分的大小和片键的范围没有一一对应的关系,所以MongoDB就想出了让shard以chunk来做平衡的单位,每一个chunk会对应一个子区间,chunk本身有自己的大小管理,比如分裂,所以每一个shard里面的chunk基本可以认为大小一样,这样shard做平衡的依据就是chunk块的大小,如果数目差异较大,直接移动chunk即可,最后shard维护自己的片键范围也简单了很多。所以引入chunk的概念是一个很好的缓冲。

其次MongoDB的路由信息是基于范围的,这样在做balancing以后修改路由信息很简单,只需要修改shard的范围即可,如果采用除模等运算,还需要涉及一致性哈希等问题。基于范围和基于哈希函数或者除模运算的不同在于,范围的路由信息与插入的数据有关,不同的数据集可能会得到不同的范围集,是随着数据的插入而动态生成的。哈希函数这种有点静态的味道,只要给一个片值,不管有没有数据,不管其他数据如何,结果都是固定的。所以,基于范围可能在插入数据时需要维护路由信息,但是在修改时很容易,使用哈希法,虽然插入数据时不需要做事情,但是在动态修改拓扑结构以后,路由信息就会出问题。具体使用哪一个看场景看需求了,MongoDB要做balancing,所以肯定选择范围法更好。

在集群中增加或者删除分片

在集群中增加分片时,由于新的分片上并没有数据块,会造成数据的不均衡。此时MongoDB会立即开始向新分片迁移数据,但集群达到数据均衡的状态需要花费一些时间。当在集群中删除一个分片时,均衡器需要将被删除的分片上的数据全部迁移到其他分片上,在全部迁移结束且元信息更新完毕之后,你可以安全地将这个分片移除。

五、数据块分裂到平衡的过程

MongoDB分片集群工作原理(一)

   进入分裂过程,应用程序通过mongos把数据写入到Shard1节点.

   这时Shard1节点的某数据块大小就会增长.

   当这个数据块增长到Chunk的阈值的时候就会触发拆分.

   此时Chunk已经拆分成了两个较小的单元.

   接下来就会通知Config Server,也就是把元数据信息记录到Config Server进行保存.

MongoDB分片集群工作原理(一)

   进入到均衡过程,当发现Shard1里面有很多个Chunk块时.

   这个时候会开启一个平衡.

   首先会将Shard1Chunk迁移到Shard2节点上面.

   继续将Shard1Chunk迁移到其他的节点上面.

   直到整个集群达到一个平衡状态即可.

   最后对Config Server上块的位置的元数据进行更新.


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (8)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!

(1)个小伙伴在吐槽
  1. 博主大大,您好,我想请问下分片键的数量最多能有多少个呢?
    hongxin2018-12-02 10:54 Linux | Chrome 67.0.3396.99