MongoDB分片集群技术详解与应用

引言

在大数据和云计算时代,随着数据量的快速增长,单一数据库实例的性能和容量往往无法满足应用的需求。MongoDB作为一个流行的NoSQL数据库,提供了分片(Sharding)技术来解决这一问题。分片允许将数据水平分割到多个数据库实例上,从而提高了系统的整体性能和存储容量。本博客将详细介绍MongoDB分片集群的概述、构建部署方法、应用方法以及常见的错误和解析。

一、MongoDB分片集群概述

问题一:对于一个后续肯定会扩容的分片集群。建议最开始分片数多点后续主要扩机器配置?还是最开始机器配置好点后续主要扩分片?

答:初始分片多点。原因如下:

①纵向扩容相对于横向扩容肯定是更容易的,而且很有可能避免数据的搬迁。如果后续扩分片的话数据搬迁不可避免。

②分片键合理的话,数据写入被均衡到更多的分片上(机器上),集群整体读写性能更优。

③单个分片内的数据少,遇到故障时(机器故障)的回复时间也相对短一些,备份/回档等维护操作也能更充分的利用分片间的并发。

一、mongodb分片与各角色与概念介绍
1、Mongodb的分片机制是mongodb数据库的核心机制,也是其可用性高,扩展性好的原因,分片—sharding 的意思就是将数据库数据分散存贮到不同的服务器上,来缓解高并发访问,均衡负载。 举例来说一下,比如说一个collection有TB级别的数据,在传统方式下如果有两个线程要访问其中的数据,即使这个线程访问的数据是不同的,为保证同步需要排队等待,分片机制可以很好的解决这个问题,分片机制其实就是分布式的集群,比如现在有五台服务器作为集群,mongodb可以将一个collection的数据分割成5个片---chunk 分别存放到5个server并且mongodb还会记录下每一条数据的位置,这样一来当用户访问集合里面的文档时,mongodb可以根据请求条件来查找到对应数据所在的服务器然后返回数据,这样下来可以将多请求的负责分摊到各个服务器的分片上,大大提高数据库系统的吞吐量。

注意:MongoDB在集合维度进行数据的分片,即将集合的数据分布到集群的不同分片上。参见官网。

MongoDB shards data at the collection level, distributing the collection data across the shards in the cluster.

关于分片和副本集的含义就是下面这两句话。我们接下来重点看看关于mongodb分片的其他知识。

分片:一份数据被分开保存在N台机器上,N个机器上的数据组合起来是一份数据。

副本集:同一份数据被保存在N台机器上,每台机器上都有一份数据。

2、分片的优点:Sharding — MongoDB Manual

这里相当于是从各个维度在罗列一下,主要还是参考官网。

(1)读写方面。①读写的水平扩展。分片之后mongodb就可以分布这些读写的负载到集群中的不同分片,允许每一个分片去处理集群操作的一个子集。这样不管是读负载还是写负载都可以通过添加更多的分片来实现水平扩展。②读写的针对性路由。对于包含分片键或者复合分片键前缀的查询,mongos可以将查询直接路由到指定的分片或者一组分片上,这种针对性的操作通常是比向集群中每个分片进行广播请求要高效得多。

(2)存储能力。通过分片可以将数据分布在整个集群,这样每个分片只包含整个集群数据的一个子集。随着数据集的增长,通过新增分片就可以加大集群的存储能力。

(3)高可用。Config Server和分片作为副本集的部署提供了更高的可用性。即使一个或多个分片副本集变得完全不可用,分片集群也可以继续执行部分读写。也就是说,虽然无法访问不可用分片上的数据,但针对可用分片的读写仍然可以成功。

3、分片前需要考虑的点:Sharding — MongoDB Manual

1)分片集群的复杂性要求使用方需要仔细的规划和维护;——维护复杂。

(2)一旦一个集合被shard后,mongodb没有办法对一个sharded集合进行unshard操作;——操作不可逆。
 注:MongoDB5.0支持在业务运行的情况下,通过reshardCollection命令按需更改集合的分片键(Shard key)。整个过程数据库服务无需停机或进行复杂的迁移,操作简单高效。reshardCollection — MongoDB Manual

3)为了保证集群的性能和效率,分片键的选择需要慎重,可以参见 如何选择分片键 ;——仔细选择分片键。

(4)分片有一定的操作要求和限制,这里 有分片集群的一些操作限制;——分片后有些功能会受限。(验证下)

(5)4.4及之后才支持多字段作为分片键,之前版本都仅支持单字段用做分片键。这里  腾讯云到目前为止还只是支持到4.2版本,唉!!

唯一索引(Unique Indexes)之于分片集群:MongoDB不支持跨分片的唯一索引,除非唯一索引包含完整的分片键作为索引的前缀。这种情况下,mongodb将强制整个key的唯一性,而不是单个字段。

举个例子:如果kfuin被用作分片键那么要求必须要建立一个( { kfuin: 1, unique_id: 1}, { unique: true } )的复合字段唯一索引。验证ing

注:如果是两个字段的唯一索引,这个索引也是能加速搜索性能的(很好理解)。

例如{key:1,subkey:1},{unique:true}即能用于保证唯一性,也能用于db.coll_0.find({key:"aaa",subkey:"bbb"})

(5)如果查询操作不包括分片键或复合分片键的前缀,mongos将执行广播操作,查询shard集群中的所有shard。这些分散/聚集查询可以是长时间运行的操作;——查询要包括分片键。(验证下

如果分片了,该怎么查数据了呢?

关于第四点比较重要,有必要测一下看看实际效果。

测试一:

#首先往shardtest集合中插入一条数据()
db.shardtest.insert({"unique_id":"11111111", "name" : "shuozhuo", "age" : 28, "gender" : "female", "friend" : "null"})
 
#查看一下这个集合是没有分片的
db.shardtest.stats().sharded
 
#查看一下索引是只有默认的_id索引
db.shardtest.getIndexes()
 
#这个时候我们对unique_id字段创建唯一索引是ok的
db.shardtest.createIndex({"unique_id":1},{unique:true})
 
#查看一下果然有我们新建的唯一索引
db.shardtest.getIndexes()
 
#先删除这个唯一索引
db.shardtest.dropIndex("unique_id_1")
 
#check下确实删掉了
db.shardtest.getIndexes()
 
#然后我们跳转到admin数据库对上面shardtest库表以_id为分片键进行了范围分片
db.runCommand({shardcollection: "db_msg_track.shardtest", key: { _id: 1}})
 
#check一下这个时候确实为true了
db.shardtest.stats().sharded
 
#这个时候再尝试对unique_id字段创建唯一索引发现不行了.截图如下:
db.shardtest.createIndex({"unique_id":1},{unique:true})

测试二:然后我们drop掉这个shardtest集合,然后以unique_id字段作为分片键重新测试下发现就实现了我们想要的效果了。

注意:执行分片指令前要对分片键字段创建索引(注:_id不用创建是因为已经有了)。这里直接就对unique_id字段创建了唯一索引:db.shardtest.createIndex({"unique_id":1},{unique:true})。
 

也就是说能够达到预期效果(以unique_id作为唯一键同时作为分片的分片键)的流程是:首先对shardtest集合的unique_id字段创建了唯一索引,然后我们再以unique_id为分片键对shardtest这个集合进行分片。

测试三:能不能投机取巧一波。即先创建关于unique_id字段的唯一索引,然后我们再以_id作为分片键来进行分片。

结果:显然是不行的。而且告诉你“不能够以被用于唯一索引的字段作为分片键,唯一性不能够保持,除非分片键是前缀。”

4、首先看一下mongodb分片集合的一张示意图:

  关于以上各个角色介绍如下:     

(1)Client:最上面的小人就是客户端。对于客户端来说你数据库分不分片和我没关系,我只关心存取数据别的没什么好说的。

(2)mongos:即最上面的Router,mongos作为分片集群的入口所有的请求都由mongos来路由、分发、合并,这些动作对客户端驱动都是透明的。用户连接mongos就像是连接mongod一样使用。mongos通过缓存config server里面的元数据(metadata)确定每个分片有哪些数据,然后将读写请求分发到相应的某个或者某些shard。

1)mongos的数量与部署。

①为保证集群的高可用,在集群中一般不只一个mongos入口。②一种常用的模式是在每个应用服务器上部署一个mongos;这样可以减少应用程序和Router之言的网络延迟 ③另外还有一种更适应于大型集群的模式。 即将mongos router放到专用的主机上,这样带来的好处是可以将应用服务器的数量与mongos实例的数量分离,进而可以更好的控制mongod实例所服务的连接数。④一般来讲集群中的mongos数量没有什么数量上的限制。但是由于mongos经常会与Config Severs通信,因此在增加路由服务mongos的时候应该密切关注Config Servers的性能。如果发现性能有明显下降,在集群中适当限制mongos的数量可能是有益的。

注:mongos应该是只有分片集群才会有的,一个普通的不分片的数据库实例客户端会直接连上mongod即可。

(3)Config Servers:配置服务器是保存集群中元信息的特殊mongod。换句话说就是给路由器(mongos)提供分片线索(理解为不同分片的索引表)。mongos通过查询configserver就知道当前存取的数据究竟在哪个或哪些分片(shard),然后直接去访问对应的shard就好了。

从3.4开始config server必须部署为一个副本集。

(4)mongod:一个服务器上的mongodb数据库实例通常我们称为mongod,在分片集群中一个mongod其实就对应一个分片(shard).

(5)Shard:数据库真正的数据就存放于shard上面,在分片集群中一个分片(shard)可以是一个单独的mongod或者一个副本集(replica set)。

1)注意mongos和mongod之间的关系,这是两个不同的入口。mongos可以统筹管理集群中的所有数据,mongod则是代表了“当前”数据库。举个例子,对于一个有5个服务器的mongo集群,通过mongos入口可以访问5个服务器的全部数据,通过mongod则只能访问到当前服务器的数据。

2)注意mongos路由不止一个。原因很简单,一个高可用的分布式集群方案必须保证服务时刻都可以正常高效运行,这里配置多个同样的mongos路由是为了预防当前路由出现故障而备用的。同样configserver也需要完全相同的副本分布在不同的服务器上;shard也一样可以设置多副本。

3)在生产环境中所有的分片都应该是一个副本集(Replica Set).

二、几个重要概念
1、chunk(块) —— 参见 这里

(1)对于一个shard mongodb也将数据划分成了更小的chunk(块)。每个chunk都唯一独占分片键的某段范围,片键值落在这个范围的文档也就落在这个chunk上。

①mongodb会把片键的值(或者hash过后的片键值)分割成互不重叠的若干个区段,每个区段都对应一个chunk。②mongodb会尽可能的把这些chunk均分到集群的不同shards中。③一个chunk默认的大小是64M,其范围在1-1024M之间,用户是可以设置的,不过不推荐。④mongodb会对超过配置大小的chunk进行分割,插入和更新操作都有可能触发chunk分割。

(2)用户可以设置chunk的大小,但是不推荐这么做。

MongoDB 允许你对 chunk 的大小进行设置,你也可以把一个 chunk 切分成若干个小 chunk,或者合并多个 chunk。一般不建议手动操作 chunk 的大小或者在 mongos 层面切分或合并 chunk。原因主要是两点:①在数据不断插入到我们的集群中时,mongodb 中的 chunk 大小会发生很大的变化。当一个 chunk 的大小超过了最大值,mongo 会根据 shard key 对 chunk 进行切分,必要时一个 chunk 可能会被切分成两个甚至多个小 chunk。大多数情况下这种自动行为已经满足了我们日常的业务需求,无需进行手动操作。②另一点原因是当进行 chunk 切分后,直接的结果会导致数据分配的不均匀,此时 balancer 会被调用来进行数据重新分配,很多时候这个操作会运行很长时间,对负载和性能都有一定程序的影响

(3)chunk 是 MongoDB 在多个 shard 集群中迁移数据的最小单元。

Balancer(均衡器)会在数据分配不均匀的时候自动运行。Balancer 是如何决定什么情况下需要进行数据迁移呢?答案是 Migration Thresholds,当 chunk 的数量在不同 shard replica 之间超过一个定值时,balancer 会自动运行,这个定值根据你的 shard 数量不同而不同。而chunk就是mongodb在shard间迁移数据的最小单元。

(4)chunk的大小会影响那些东西?

1)如果chunk过小可能会导致chunk数量的激增。他可以保证你的数据均匀的分布在 shard 集群中但是可能会导致频繁的数据迁移。这将加重 mongos 层面上的操作。

2)大的 chunk 会减少数据迁移,减轻网络负担,降低在 mongos 路由层面上的负载,但弊端是有可能导致数据在 shard 集群中分布的不均匀。

(5)块划分(Chunk Splits)

Data Partitioning with Chunks — MongoDB Manual

Spliting是一个防止块(chunk)过大的进程。当块(chunk)增长超过指定的块大小(chunk size)时,或者如果块中的文档数超过上限,MongoDB将根据块表征的分片键值分割块。必要时可以将一个块拆分为多个块。插入和更新可能会触发拆分。Spliting是一种有效的metadata更改。

 

拆分(Spliting)可能会导致集合的块在分片上的分布不均匀。在这种情况下,均衡器会在分片之间重新分配块(chunk)。有关跨分片平衡的更多详细信息,请参阅群集平衡器。

注:某些情况下即使块的大小已经超过了限制但是依然不能被划分,最常见的场景就是整个chunk只代表了一个shard key(即所有的数据片键值相同),显然这个是无法划分的。那么他就会称为一个Jumbo chunk(大象chunk),随着数据量的增长,这个chunk将会称为性能瓶颈。

这意味着分片键选取的时候要注意分片键取值是否足够的“散”,这一点非常重要!!!
按照一个chunk默认64M、一条数据占用500字节,这意味着一个chunk只能存放13.4万条数据(64M×1024×1024/500=13.4万条数据);也就是说你的分片键单个取值下对应的数据量最好不要超过13.4万条。极端情况偶尔超过貌似也问题不大,但是超过太多就不好了。我们可以通过getShardDistribution()指令查看每个chunk对应的数据量的情况。

db.collection_event_track_986.getShardDistribution()

MongoDB的分片集群由多个组件组成,包括分片(Shards)、路由(Mongos)、配置服务器(Config Servers)和副本集(Replica Sets)。

  • Shards:分片是存储实际数据的数据库服务器,数据在这里被水平分割并存储。
  • Mongos:路由是MongoDB的分片集群的入口点,它负责将客户端的请求路由到正确的分片上。
  • Config Servers:配置服务器存储了集群的元数据和配置信息,例如分片键、数据分布等。
  • Replica Sets:副本集是MongoDB的高可用性和数据冗余解决方案,每个分片都可以是一个副本集,以确保数据的可靠性和容错性。

二、分片集群构建部署

构建MongoDB分片集群需要遵循一定的步骤和配置。以下是一个简化的部署流程:

  1. 准备硬件和网络:确保有足够的硬件资源(如CPU、内存、存储)和网络带宽来支持分片集群的运行。
  2. 安装MongoDB:在每个服务器上安装MongoDB数据库软件。
  3. 配置副本集:首先,将每个分片配置为一个副本集,以提高可用性和数据冗余。
  4. 配置配置服务器:选择一个或多个服务器作为配置服务器,并初始化配置服务器副本集。
  5. 初始化分片集群:使用mongos命令启动路由进程,并连接到配置服务器,初始化分片集群。
  6. 添加分片:将之前配置的副本集添加到分片集群中,并指定分片键和数据分布策略。
  7. 测试集群:通过插入、查询和更新数据来测试分片集群的性能和可靠性。

三、分片集群应用方法

在使用MongoDB分片集群时,需要注意以下几点:

  1. 选择合适的分片键:分片键是用于将数据分割到不同分片的字段。选择合适的分片键对于实现负载均衡和数据分布至关重要。
  2. 监控和调优:使用MongoDB提供的监控工具和指标来监控集群的性能和状态,并根据需要进行调优。
  3. 备份和恢复:定期备份分片集群的数据,并测试恢复流程,以确保在发生故障时可以快速恢复数据。
  4. 考虑扩展性:随着数据量的增长,可能需要添加更多的分片和副本集来扩展集群的容量和性能。在设计分片策略时,要考虑未来的扩展需求。

四、常见的错误和解析

在使用MongoDB分片集群时,可能会遇到一些常见的错误和问题。以下是一些示例及其解析:

  1. 数据分布不均:这可能是由于分片键选择不当或数据插入模式不均匀导致的。可以通过重新选择分片键或调整数据插入策略来解决此问题。
  2. 性能瓶颈:在某些情况下,分片集群的性能可能受到瓶颈的限制,例如网络带宽、磁盘I/O或CPU资源不足。可以通过优化硬件资源、调整查询策略或增加副本集成员来提高性能。
  3. 数据一致性问题:在分布式系统中,数据一致性是一个挑战。MongoDB通过副本集和写关注(Write Concern)机制来保证数据的一致性。但是,在某些情况下,可能会出现数据不一致的情况。这可能是由于网络分区、节点故障或配置错误导致的。解决这些问题的方法包括修复网络问题、替换故障节点和检查配置设置。

五、总结

MongoDB的分片集群技术为处理大规模数据集提供了强大的支持。通过构建和部署分片集群,可以实现数据的水平扩展和负载均衡,提高系统的整体性能和存储容量。然而,在使用分片集群时,需要注意选择合适的分片键、监控和调优集群性能、定期备份数据以及解决可能出现的错误和问题。通过遵循最佳实践和不断学习和实践,可以充分利用MongoDB分片集群的优势来处理大规模数据集并满足应用的需求。

  • 49
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值