第5回 试试MongoDB的Sharding

翻译自: http://gihyo.jp/dev/serial/01/mongodb/0005

第5回 试试MongoDB的Sharding

 

前言

 

这次我将说明MongoDB的sharding。

 

Sharding是指将数据分散到多个服务器中的功能。这次我将先说明sharding,之后是sharding的概要,之后将解说在sharding中登场的几个关键词。第二章之后将解说sharding的架构顺序。

 

Sharding在MongoDB功能之中是很重要而复杂的。用手边的环境来架构的话,对sharding的理解有很大帮助,请一定好好参考本文再进行架构。

 

sharding的优点

 

Sharding通过将MongoDB进行水平Scaling的功能,有以下优点。

 

因为分散负荷所以可以提高性能

 

通过将数据分散到多个服务器中,可以减少CPU以及I/O的负荷。虽然是复述,但MongoDB可以用key的范围数据。通过设定合适的范围,可以将负荷进行水平Scaling。

 

由于资源分散导致性价比的提高。

 

近年因为内存以及磁盘的价格变得非常便宜了,尺寸越大内存模块价格越高,另外价格的上升幅度也会增多。要举内存的例子的话,共计需要64GB的内存时,比起与16个4GB内存Module的价格,4个16GModule的价格一般而言会更贵。内存以及磁盘在一个服务器中能够累计的Unit数是有极限的。在这样的背景下,在多个服务器中,使得数据分散,可以使得性价比提高。在MongoDB因为内存与性能直接相关,我推荐要保证有充足尺寸的内存。

 

MongoDBsharding的概要

 

关于sharding,我将说明数据分散以及自动Balancing这两个特征。

 

 

根据Shard key的范围的数据分散

 

MongoDB的sharding采用范围分区(※1)。通过指定Shard key,可以决定个服务器中存储的数据的范围。服务器间不拥有重复数据,一个数据被存储的数据库是根据Shard key的范围来决定。

用图表示Sharding的image的话,就如图1所示。在图一中出现的术语我稍后说明。首先请随我对全体内容有个大致印象。

图1 sharding的概要图

图:右图例:mongos服务器 Shard Chunk 数据

 

1

 

仅仅观察数据分散这个点的话,几乎与MySQL的范围指定的分区几乎相同。

 

 自动balancing

 

Key范围的调整以及伴随调整的服务器间的数据的移动,MongoDB有将其全部自动进行balancing的功能。被设计不去在意由于自动balancing导致的服务器间的数据的偏差也行的形式。另外追加新的服务器时,自动调整会使得执行数据移动的偏差渐渐消失。

2

在设定中可关闭自动balancing

 

重要关键词的说明

 

现在我将说明在sharding中出现的关键词。

 

 shard

这是指数据被实际存储的mongod进程。1个文件存在一个shard中,无法在shard之间执行数据的复制。虽然不是必要的,但我推荐为每一个shard都要配置replication复制。

 

config服务器

这是指管理sharing的Metadata的mongod进程。因为会成为单一故障点,所以我推荐用复数的config服务器来构成。

 

mongos服务器

这是指在sharing中的路由进程Routing process。可以使shard以及客户端合作。必要的话,可以做多个mongos服务器。因为不是mongod进程,所以是无状态的,也不存放数据。

 

Shard key

这是指分散数据的key。可以进行复数指定。Key上哪个范围的数据被存储在那个shard中由MongoDB管理,根据数据的偏差进行自动调整。

 

chunk

 

chunk是指分散的数据单位。具体来说,在某个collection的连续范围的数据中,会变成复数的文件。达到chunk的最大尺寸的话,就会被分割,shard对应拥有的chunk数,必要的话会移动到其他shard中。可以变更chunk的最大尺寸。

 

至此我们说明的的关键词,一下子记不住也没关系。我们将通过在下一章中通过实际构造sharding环境,来加深理解。

试试sharding(前半)

 

在这一章与下一章中,我们将实际制作sharding结构。

 

这次,我们将在一台机器中区分节点,做5个服务器。具体来说就是,config服务器、mongos服务器、以及3个shard(node0,node1,node2)。3个mongod分别变成其他shard(参考图2)

 

 

 

构筑系统的服务器准备

首先制成数据direct以及日志direct。顺序按MongoDB的解压direct执行。

$ cd (MongoDB相关目录)

$ mkdir -p data/config

$ mkdir data/node0

$ mkdir data/node1

$ mkdir data/node2

$ mkdir log

 

启动shard

$ bin/mongod --shardsvr --port 30000 --dbpath data/node0 --logpath log/node0.log --fork  
$ bin/mongod --shardsvr --port 30001 --dbpath data/node1 --logpath log/node1.log --fork  
$ bin/mongod --shardsvr --port 30002 --dbpath data/node2 --logpath log/node2.log --fork

 

Mongod命令中,由于指定shardsvr的option,这个mongod会变成shard。

 

启动config服务器

 

$ bin/mongod --configsvr --port 20001 --dbpath data/config --logpath log/config.log --fork

 

通过在mongod命令中,指定configsvr的选项,这个mongod会变成config。

mongos服务器启动

$ bin/mongos --configdb localhost:20001 --port 20000 --logpath log/mongos.log --chunkSize 1 --fork

 

通过mongos命令,可以启动mongos服务器(不是mongod)。——在configdb中指定config服务器。mongos服务器是仅仅在内存上存在的进程,所以没有必要指定dbpath。chunkSize是chunk的尺寸。默认是64M,但这次想确认chunk分割的东西,所以这次设定1MB。

确认

在ps命令中可以看见5个进程就没问题了。

$ ps -ef | grep mongoroot 
$ bin/mongod --shardsvr --port 30000 --dbpath data/node0 --logpath log/node0.logroot 1235 
$ bin/mongod --shardsvr --port 30001 --dbpath data/node1 --logpath log/node1.logroot 1236 
$ bin/mongod --shardsvr --port 30002 --dbpath data/node2 --logpath log/node2.logroot 1239 
$ bin/mongod --configsvr --port 20001 --dbpath data/config --logpath log/config.logroot 1241 
$ bin/mongos --configdb localhost:20001 --port 20000 --logpath log/mongos.log --chunkSize 1

 

在mongos服务器中追加shard

用mongo shell,连接到mongos服务器的admin数据库。

$ bin/mongo localhost:20000/admin

 

用sh.addShard方法追加shard。

 

mongos> sh.addShard("localhost:30000")    // 追加{ "shardAdded" : "shard0000", "ok" : 1 }   
mongos>  sh.addShard("localhost:30001")   // 追加{ "shardAdded" : "shard0001", "ok" : 1 }   
mongos> sh.addShard("localhost:30002")    // 追加{ "shardAdded" : "shard0002", "ok" : 1 }

 

因为在「sh」这个object中有简化sharding的设定的方法。

 

用sh.status方法确认追加的shard是否正确追加了。

mongos> sh.status()

--- Sharding Status --- 
 sharding version: { "_id" : 1, "version" : 3 } 
 shards:        {  "_id" : "shard0000",  "host" : "localhost:30000" }        
 {  "_id" : "shard0001",  "host" : "localhost:30001" }        
 {  "_id" : "shard0002",  "host" : "localhost:30002" }  
 databases:        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }

 

投入数据

 

然后,将通过mongos服务器投入数据。

 

在连接mongos服务器的状态下,制作logdb这个数据库。

mongos> use logdb
switched to db logdb

 

然后,在logs这个collection中投入10万件数据。因为在mongoshell中可以使用javascript的语法。通过for循环插入数据。

 

mongos> for(var i=1; i<=100000; i++) 
db.logs.insert({"uid":i, "value":Math.floor(Math.random()*100000+1)})     
 mongos> db.logs.count()100000                   
 //插入了10万个document

 

 

最后在uid中展开index。理由是,在此后将collection进行sharding化的情况下,需要对应shard key制成index。

mongos> db.logs.ensureIndex( { uid : 1 } );

 

在这个时间点,sharding还没有变成有效的。仅仅是单纯地在最开始的节点中插入10万个document

 

图3 sharding有效前的image图

 

最右:mongos服务器 shard 数据

 

要确认这个状态,观察mongo shell中mongos服务器的config数据库就行了。让我们来试着对config数据库的chunks collection加入询问。

 

 

试着sharding吧

sharding的有效化

 

那么接下来,让我们对sharding就行有效化,试着分散数据吧。要将sharding有效的话,需要在sh object的enableSharding方法中指定数据名。

 

mongos> use admin
switched to db admin
mongos> sh.enableSharding("logdb"){ "ok" : 1 }

 

然后用sh.shardCollection方法指定shard化的collection。第一参数是(数据库名).(collection名)的文字列,第二参数是作成index时的hash。

 

mongos> sh.shardCollection("logdb.logs" , { uid : 1 }){ "collectionsharded" : "logdb.logs", "ok" : 1 }

 

 

sharding的状态确认

 

在此,用sh.status方法表示sharding的状态的话,我们可以知道在3个shard服务器中,分别可以作成chunk。

mongos> sh.status()
--- Sharding Status ---  
sharding version: { "_id" : 1, "version" : 3 }  
shards:    {  "_id" : "shard0000",  "host" : "localhost:30000" }    
{  "_id" : "shard0001",  "host" : "localhost:30001" }    
{  "_id" : "shard0002",  "host" : "localhost:30002" }  
databases:    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }    
{  "_id" : "logdb",  "partitioned" : true,  "primary" : "shard0000" }      
logdb.logs chunks:          
shard0001       1    // ←shard0001的chunk数          
shard0002          

{ "uid" : { $minKey : 1 } } -->> { "uid" : 10083 } on : shard0001 Timestamp(2000, 0)        
{ "uid" : 10083 } -->> { "uid" : 20166 } on : shard0002 Timestamp(3000, 0)

 

在上述的输出中,shard0000的chunk数有8个,shard0001的chunk数是1个,然后shard0002的chunk数是1个

 

这样chunk数发生偏移的情况是因为数据还在移动中的原因。请稍等后,在此执行sh.status。

 

--- Sharding Status ---  
sharding version: 
{ "_id" : 1, "version" : 3 }  
shards:    {  "_id" : "shard0000",  "host" : "localhost:30000" }    
{  "_id" : "shard0001",  "host" : "localhost:30001" }    
{  "_id" : "shard0002",  "host" : "localhost:30002" }  
databases:    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }   
{  "_id" : "logdb",  "partitioned" : true,  "primary" : "shard0000" }      
logdb.logs chunks:        
shard0001       3     // ←shard0001的chunk数 
shard0002       3     // ←shard0002的chunk数 
shard0000       4     // ←shard0000的chunk数 
{ "uid" : { $minKey : 1 } } -->> 
{ "uid" : 10083 } on : shard0001 Timestamp(2000, 0)          
{ "uid" : 10083 } -->> { "uid" : 20166 } on : shard0002 Timestamp(3000, 0)

 

 

稍等后,chunk数就均等地变成3.3.4了(参考图4)

 

 

Chunk数均分的image图

图:最右 mongos服务器、shard、chunk、数据

数据数和chunk数是image。

另外,各输出的后半中加入的各chunk中的shard key的范围被输出了,

{ “uid” : 10083 } –>> { “uid” : 20166 } on : shard0002 Timestamp(3000, 0)

在上述例子中,我们可以看出shard0002的chunk被储存在uid的范围是10083≦uid<20166这个collection中。

$minKey被看做比所有值都小,$maxKey被看做比所有值都大。

另外也有别的确认方法——观察mongos服务器的config表的方法。

 

mongos> use config
switched to db config
mongos> db.chunks.count()
10
mongos> db.chunks.findOne()

 

各shard服务器的状态确认

 

在被sharding的状态下,确认各shard服务器会变成怎样。做法很简单,只要在各shard服务器中用mongoshell连接就性了,首先试着访问node1吧。

$ bin/mongo localhost:30000/logdb
> db.logs.count()
39503
> exit

 

 

这样,就可以参照在各shard服务器中加入的collection数。另外用find方法等也能参照collection数。其他的shard服务器也是相同的。

 

$ bin/mongo localhost:30001/logdb
> db.logs.count()
30248> exit 
$ bin/mongo localhost:30002/logdb
> db.logs.count()
30249
> exit

 

下次的主题

 

这次我介绍了mongoDB的数据分散功能,sharding。通过使用sharding功能,可以分散负荷以及资源从而实现减少成本。另外我也介绍了自动balancing这种MongoDB的特有的功能。

下次,我介绍上次介绍过的复制以及这次介绍的sharding来进行组合构成介绍。

 

Comment

*

沪ICP备14014813号-2

沪公网安备 31010802001379号