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

MongoDB分片集群部署理论(三)

MongoDB 彭东稳 9年前 (2016-03-30) 30043次浏览 已收录 0个评论

1)配置集群

启动shard节点

一个shard节点可以是一个单独的mongod或者是一个replica set。在生产环境中,每个分片都应该是一个复制集。参见部署复制集将每个分片部署为复制集。

启动config server节点

配置服务器必须开启1个(会有警告)或则3个,开启2个则会报错。另外从MongoDB3.2开始,config servers可以部署为一个复制集,但这个复制集中的config server节点必须是WiredTiger存储引擎。

Mongodb3.2支持复制集Config Server,其配置也非常简单,先在三个Config Server节点上配置复制集及config server,基本参数如下:

然后连接到一个mongo shell开始使用rs.initiate方式初始化复制集即可。

启动mongos节点

mongos实例是轻量服务,并且不需要数据目录,所以只需要记录一下日志即可。你可以将 mongos 运行在已经部署了其他服务的系统中,比如应用服务器或者运行了mongod的机器上,默认运行在 27017 端口上。

对于非复制集Config Servermongos启动语法:

#Mongos节点指定配置服务器地址,但必须是1个或则3个配置服务器

对于复制集Config Servermongos启动语法:

2)向集群中添加分片

步骤一、连接到mongos

步骤二、Add Shard(操作对象有单个数据库实例和复制集两种)

添加shard可以使用数据库命令添加也可以使用sh.addShard()方法添加。下面是使用 sh.addShard() 添加分片的例子:假设一个分片使用了复制集,复制集名字为ywnds,有一个mongodb0.example.net且端口为27017的成员,使用以下命令添加这个分片:

添加mongodb0.example.net端口为27017的单机mongod分片,需要执行以下命令:

步骤三、为集群数据库开启分片

在对集合进行分片之前,必须开启数据库的分片。对数据库开启分片不会导致数据的重新分配,但这是对这个数据库中集合进行分片的前提。一旦为数据库开启了分片,MongoDB就会为这个数据库指定一个primary shard,所有未分片的数据都会存储在这个分片上。

使用sh.enableSharding()需要指定要开启分片的数据的名字,语法如下:

你也可以使用enableSharding命令对数据库开启分片,语法如下:

步骤四、对一个集合进行分片

1.如果集合中已经包含有数据,需要使用db.collection.ensureIndex()在片键上创建索引。如果集合是空的,MongoDB会在sh.shardCollection()过程中自动创建索引。

2.首先选择一个片键(shard key),选择片键的好坏很大程度上影响集群的性能,容量和功能。

3.使用mongo shellsh.shardCollection()方法对集合进行分片,语法如下:

<database>.<collection>字符串换成你数据库的ns。由数据库的全名,一个点( . ),和集合的全名组成,hard-key-pattern换成你的片键,名字为创建索引时指定的名字。如下示例:

按照顺序解释(前两种都属于基于范围分片,后一种属于基于哈希分片):

a)  records数据库中的people集合使用{ “zipcode”: 1, “name”: 1 }片键开启分片.

这个集合使用zipcode字段作为重新分配数据块的片键(1表示索引升序),如果很多文档都有相同的zipcode值,那么Chunk会按照name片键的值进行分裂。

b)  people数据库中的addresses集合使用片键{ “state”: 1, “_id”: 1 }.

这个集合使用state字段作为重新分配数据块的片键,如果很多文档都有相同的state值,那么Chunk会按照_id片键的值进行分裂。

c)  events数据库中的alerts集合使用{ “_id”: “hashed” }做片键.

这个集合使用state字段作为重新分配数据块的片键,MongoDB为散列索引计算_id的值,可以保证集群中数据的均衡。MongDB2.4版本开始支持基于哈希的分片了。分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。对于基于哈希的分片,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有”相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。

如何选择一个合适的片键?

一些人并不能真正理解或者信任MongoDB自动分配数据的方式,数据的分配方式是根据片键而决定的,所以一个集合中片键的设计非常重要。下面我们看一看几个比较坑的片键设计方式,然后对比着好的片键设计,让我们能够真正理解片键设计对分片的影响。

第一种:小基数片键

一个小基数片键的设计会带来的问题就是数据块持续增长,容易形成大的数据块,导致数据分片的不均衡以及数据块持续变大。一般由于片键值数量有限,我们称之为小基数片键(low-cardinality shard key)。下面用实例说说为什么小基数片键会导致数据块持续变大?

实例:Apache日志

对于上面这样两个虚拟主机访问日志的存储分片,如果我们选用“node:1”字段作为片键的话。集合开始于某一个分片的初始块上,所有的插入和读取都落在这一个块上。一旦它变得足够大时,就会被划分成两个块根据这两个“ywnds.com”,“sdnwy.com”不同的主机名(如果有第三个主机名的话,就会以中间的那个主机名为区间进行划分)。

然后随着更多日志文档被添加进来,那么每条日志node字段的值无非就是这两个主机名,也就是说片键不会发生变化,集合最终也就变成2个块。MongoDB无法再根据node进行分割这些块了!那么块只能变得越来越大(块默认大小64MB),当磁盘空间出现问题时除了买容量更大的磁盘,你什么也做不了。

这个规则使用于任何取值个数有限的键,所以,如果在某集合中一个键有N个值,那就只能有N个数据块,因此也只能有N个分片。那么比较适合的片键选择可以这么做。

用“node”作为片键,而“time”这个变化的字段作为一个复合片键来做。

第二种:单调递增的升序片键

升序片键或导致的问题就是数据写操作不能够均匀分布,一些片键会使应用程序能够达到集群能够提供的最大的写性能,有一些则不能,比如使用默认的_id做片键的情况。如果我们使用一个单调递增的“_id”或时间戳作为片键,在插入文档时,MongoDB会生成一个全局唯一的ObjectId标识符_id。不过,需要注意的一点是,这个标识符的前几位代表时间戳,这意味着_id是以常规的并且可预测的方式增长,即使_id有大的基数,在使用_id或者任意其他单调递增的数据作为片键时,所有的写入操作都会集中到最后一个分片中。导致性能不能够很好地均摊,这样也违背了分片的最初目的。不过,如果你的写入频率很低或者大多都是update()操作,单调递增的片键不会对性能有很大影响,一般来说,选择的片键要同时具有较大的基数与将请求分布在整个集群中,两个特性。

比方说我们有一个类似微博的服务,其中每个文档都包含一个条短消息、发送人以及发送时间。我们按发送时间字段来分片,取值为自公元元年起经过的秒数。

实例:微博

和往常一样,集合开始于某一个分片的初始块上,所有的插入和读取都落在这一个块上。一旦它变得足够大时,然后开始分裂。比如说(?,1455755673)和(1455755673,?)。由于是从片键中点把块分开来的,所以在我们分割块的那一刻,时间戳很可能已经远大于1455755673了。这意味着再往后所有的插入都会落到第二个块上,不会再有插入操作命中第一个块。一旦第二个块填满了,它就会分裂成(1455755673,1455759834)和(1455759834,?)两个块。但是因为从现在起时间都在1455759834之后,所以新的插入都会被添加到区间为(1455759834,?)的块上。而这个模式会持续下去:所有数据总是被添加到“最后”一个数据块上,即所有数据都会被添加到一个分片上。这种片键创造了一个单一且不可分散的热点。

同样更好的选择就是使用:

用“auth”作为片键,而“time”这个变化的字段作为一个符合的复合片键来做。

第三种:随机片键

有时为了避免热点,会采用一个取值随机的字段来做分片,采用这种片键一开始还不错,但是随着数据量越来越大,他会越来越慢。比如我们在分片集合中存储照片缩略图,每个文档包含了照片的二进制数据,二进制数据的MD5散列值,以及描述等字段,我们决定在MD5散列值上做分片。

随着集合的增长,我们最终会得到一组均匀分布于各分片的数据块。目前一切正常。现在假设我们非常忙而Shard2上的一个块填满并分裂了,配置服务器注意到Shard2Shard1多出了10个块并判定应该抹平分片间的差距,这样MongoDB就需要随机加载5个块的数据到内存中并发给Shard1,考虑到数据序列的随机性,一般情况下这些数据可能不会出现在内存中,所以此时的MongoDB会给RAM带来更大的压力,而且还会引发大量的磁盘IO。另外,片键上必须有索引,因此如果选择了从不依据索引查询的随机键,基本上可以说浪费了一个索引,另一方面索引的增加会降低写操作的速度,所以降低索引量也是非常必要的。

说了这么多到底该使用哪一种偏见,这个真的没有一个准确的答案,需要根据自己的应用程序以及需求来定义片键。但在选定一个片键时,不防先想一想下面这些问题的答案?

1.写操作是怎么样的,有多大?

2.系统没小时会写多少数据,每天呢,高峰期呢?

3.那些字段是随机的,那些是增长的?

4.读操作是怎么样的,用户在访问那些数据?

5.数据索引做了吗?应不应该索引呢?

6.数据总量有多少?

总的来说,在进行分片前,你需要清楚的了解你的数据。


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

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