如何在MongoDB上用一个Ubuntu 12.04 VPS创建一个分片集群


发布者 goyoo  发布时间 1397635288483
关键字 技术 
如何在MongoDB上用一个Ubuntu 12.04 VPS创建一个分片集群


 

简介

MongoDB是一个NoSQL文档数据库系统,水平扩展性很好,通过一个键-值系统执行数据存储。对于网络应用程序和网站来说,MongoDB是很受欢迎的,在语法上执行和进入都很容易。

 

MongoDB通过一项被称为“分片”的技术实现扩展。分片的过程是:跨不同的服务器写入数据,来分布读写负载和数据存储需求。

 

在之前的一个教程中,我们讲述过如何在Ubuntu 12.04虚拟专用服务器(VPS)上安装MongoDB,我们基于此开始来讲:如何跨很多不同节点执行分片。

 

MongoDB 分片结构


分片通过3个单独的组件来完成,每个部分都有特定的功能:


配置服务器:每个生产分片的执行一定包含3个配置服务器,这是为了确保冗余和高可用性。配置服务器是用来存储元数据,元数据连接着请求数据和包含请求数据的分片。配置服务器可以组织数据,这样就能够获得可靠一致的信息。

查询路由器:你的应用程序实际上连接到的是查询路由器,这些机器负责与配置服务器进行交流,来搞清楚请求数据的存储位置。然后,它会进入适当的分区并返回数据,每个查询路由器运行“mongos”命令

分区服务器:分区负责实际的数据存储操作。在生产环境中,一个单个分区通常由一个复制集合组成,而非一个单独的机器,这是为了保证在一个主分区服务器脱机时,数据仍然可以被访问到。执行复制集合已经超出这篇教程的范畴,所以我们会将我们的分区配置成单个机器而不是复制集合。如果你想配置复制集合,可以自己简单修改一下。

 

初始设置

如果你留意了上面的内容,可能注意到了这样的配置需要不少机器才能完成,在这篇教程里,我们会配置一个示例分区群,它包括了:


· 3个配置服务器 (生产环境中需要)

· 2个查询路由器 (至少有1个是必要的)

· 4个分区服务器 (至少有2个是必要的)


这意味着你将需要9个VPS实例向下进行。实际中,一些功能可以重叠(例如,你可以在相同的当做配置服务器使用的VPS上运行一个查询路由器),这样只需要一个查询路由器和最少2个分区服务器。


我们将使用最少的机器以便演示给每种类型添加多重组件,我们也会将这些组件作为独立的机器,这样会更简单清晰。

 


设置初始的基础图像

开始时,使用这个引导在Ubuntu系统安装并配置一个初始MongoDB服务器。我们会用这个服务器引导剩下的分区组件。

 

完成了你的第一个服务器安装后,关闭该实例,输入命令:

sudo shutdown -h now


现在我们给配置好的droplet照个快照,并将它用在创建其他的VPS实例上。


在你的DigitalOcean控制面板上,选择droplet。点击"Snapshots" 选项。输入一个snapshot名字并点击"Take Snapshot"


你的快照会被录入,初始服务器将会重启。


 

在图象基础上创建VPS实例

既然我们已经保存了通过snapshot获得的照片,就可以使用它作为其他MongoDB组件的一个基础。


在控制面板上点击“Create”按钮。输入一个名字,这个名字需描述出你的droplet在分区配置中的目的:

选择droplet大小和位置。最好是给你所有的组件选择同样的位置。


在“Select Image”区域,点击“My Images”按钮并选择刚创建的MongoDb快照。

添加任意你需要的SSH秘钥,并选择你喜欢使用的设置。点击“Create Droplet”来创建你的新VPS实例。


每个分区组件都要重复这一步骤。谨记,要进行这个教程的步骤(不是必须,只是演示说明),你需要3个配置服务器,2个查询服务器和4个分区服务器。

 


为每个组件配置DNS子域入口(可选)

MongoDB文档建议你通过一个DNS可解析名字而不是一个具体的IP地址,来指向所有组件。这很重要,因为这样允许你更改服务器或重新部署某些组件而不用重启与它相关的每个服务器。

为了便于使用,我建议你在希望使用的域名基础上给每个服务器一个子域名。可以用这个向导学习如何用DigitalOcean控制面板设置DNS子域名

出于这篇教程的目的,我们将参考通过下面这些子域名也可以进入的组件:


配置服务器

·         config0.example.com

·         config1.example.com

·         config2.example.com

查询路由器

·         query0.example.com

·         query1.example.com

分区服务器

·         shard0.example.com

·         shard1.example.com

·         shard2.example.com

·         shard3.example.com

 

如果你没有设置子域名,仍然可以进行这些步骤,但你的配置将不会很强大。如果你希望这样,只要用你的droplet的IP地址替换子域名说明就可以了。

 


初始化配置服务器

首先必须要设置的组件是配置服务器,在查询路由器或分区配置之前,这些必须在线操作。


作为根用户登陆进入你的第一个配置服务器。

首先我们需要创建一个数据目录,供配置服务器存储与位置和内容有关的元数据:

mkdir /mongo-metadata


现在,我们只需要用合适的参数启动配置服务器。提供配置服务器的服务被称为mongod。这个组件的默认端口号是27019.

我们可以用下面的命令启动配置服务器:

mongod --configsvr --dbpath /mongo-metadata --port 27019


服务器将会开始输出信息,并开始监听来自其他组件连接。


在另外两个配置服务器上重复这个过程。端口号在所有3个服务器上应该是一样的。



配置查询路由器实例

这时候,你所有的3个配置服务器需要运行起来,并监听连接。在继续下一步之前他们必须是可运行的。


作为根用户登陆进入你的第一个查询服务器。

我们需要做的首先是:如果mongodb早已经运行起来,在这个实例上停止mongodb进程。查询路由器使用数据锁定,它与MongoDB主进程是冲突的。

service mongodb stop


接下来,我们需要用一个特定的配置字符串,来启动查询路由器服务。配置字符串对于你配置的每一个查询路由器(包括参数顺序),必须完全相同。它由两部分组成:每个配置服务器的地址和其运行的端口号,以逗号隔开。


查询路由器被称为mongos。这一过程的默认端口号是27017(但是这个配置中的端口号指的是配置服务器,默认是27019)。

最终的结果是查询路由器服务用这样的一个字符串启动:

mongos --configdb config0.example.com:27019,config1.example.com:27019,config2.example.com:27019


你的第一个查询路由器应该开始去连接到3个配置服务器。在其他查询路由器上重复这些步骤。记住mongodb服务一定要在输入命令之前停止。


同样要牢记的是:启动每一个查询路由器使用的命令必须完全相同,否则会导致错误。



添加分区到集群

我们已经配置好了配置服务器和查询路由器,现在我们可以开始添加实际的分区服务器到我们的集群。这些分区每一个将会分担所有数据的一部分。


作为根用户登陆到你的一个分区服务器。


正如我们开始时提到的,在这篇引导中,我们只会用单独分区而不是复制集合,这是为了演示的简洁和简单。在生产环境中,一个复制集合是被广泛推荐的,因为它可以保证数据的完整性和可用性。点击查看这里的引导:在MongoDB配置复制集合


要想向集群添加分区,我们需要通过查询路由器,查询路由器现在已经配置好并作为连接到集群的接口。我们可以通过连接到任一个像这样的查询路由器,来做这件事:

mongo --host config0.example.com --port 27017


这样会连接到适合的查询路由器并打开一个mongo prompt。我们将从这个prompt添加所有的分区服务器。


添加我们的第一个分区,输入:

sh.addShard( "shard0.example.com:27017" )


一会你可以在这一相同的接口添加剩下的分区droplet。不用单个地登陆进入每一个分区服务器。

sh.addShard( "shard1.example.com:27017" )
sh.addShard( "shard2.example.com:27017" )
sh.addShard( "shard3.example.com:27017" )

如果你在配置一个生产集群,用复制集完成,你必须要指定复制集名称以及一个复制集成员,来建立每一个集作为不同的分区。语法是像这样的东西:

sh.addShard( "rep_set_name/rep_set_member:27017" )


如何让数据库集合实现分区

MongoDB组织信息到数据库,在每一个数据库里,通过“集合”(collections)数据进一步被划分。集合类似于传统型关系数据库模型里的表格(table)。

在这个部分,我们会再次使用查询路由器操作。如果你仍然没有连接到查询路由器,你可以使用在最后一段中用过的相同的mongo命令来再次访问进入。



在数据库层实现分区

我们首先在数据库层实现分区,要完成这步,我们将会创建一个测试数据库,名字叫做test_db。

要创建这个数据库,我们只需要更改一下。当首次输入数据时,它将会标记为我们的当前数据库并动态创建。

use test_db


我们可以核实一下当前正在用的数据库就是刚创建的,输入:

db
test_db

我们可以看到所有可用的数据库,输入:

show dbs

你可能注意到了我们刚创建的数据库并没有出现,因为它里面没有数据,还不是很真实的数据库。

我们可以通过发出下面这条命令,在这个数据库上实现分区:

sh.enableSharding("test_db")

再一次,如果我们输入命令show dbs,将不会看到新数据库。但如果我们转换到自动生成的配置服务器,并发出一个 find()命令,就会返回我们的新数据库:

use config
db.databases.find()
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "test_db", "partitioned" : true, "primary" : "shard0003" }


当MongoDB添加一些数据到新数据库后, show dbs命令会让数据库出现。

 


在集合层实现分区

因为我们的数据库被标记为可用于分区,我们可以在一个具体集合中实现分区。


这时候,我们需要确定一个分区策略。分区工作的实现是:在它被存储的文档里,在一个被指定为shard key的特定字段的基础上,通过组织数据到不同的类别。它把有匹配分区钥匙的所有文档放到同一个分区。


例如,如果你的数据库是在一个公司存储员工信息,而且你的分区钥匙是基于最喜欢的颜色,MongoDB会将最喜欢颜色字段为blue的所有员工放到一个单独的分区。但如果大家都喜欢少数几种颜色的话,就会导致存储不成比例。


对于一个分区钥匙的更好选择是可以保证存储更加平均分布的东西。例如,在一个大型公司,一个生日(月和日)字段也许会相当平均地分布。


以防你不确定数据会怎样分布,或没有合适的字段,你可以在一个现存字段基础上创建一个“hashed”分区钥匙。这就是我们为我们的数据将会做的。


我们可以创建一个集合叫做test_collection并打乱它的"_id"字段。确保我们在使用我们的test_db数据库,然后发出命令:

use test_db
db.test_collection.ensureIndex( { _id : "hashed" } )

然后我们可以将集合分开,方法是通过发出这一命令:

sh.shardCollection("test_db.test_collection", { "_id": "hashed" } )

这会跨所有可用分区,将集合分区。



插入测试数据到集合

通过使用一个loop来创建一些objects,我们可以看到我们的分区正在进行。这个loop直接来自于MongoDB网站,可以生成数据。


我们可以用一个单个循环(simple loop)来插入数据到集合:

use test_db
for (var i = 1; i <= 500; i++) db.test_collection.insert( { x : i } )

这会创建500个单独文档(仅有一个ID字段和一个包含一个数字的“x”字段),并分布给不同的分区。查看结果可以通过输入:

db.test_collection.find()
{ "_id" : ObjectId("529d082c488a806798cc30d3"), "x" : 6 }
{ "_id" : ObjectId("529d082c488a806798cc30d0"), "x" : 3 }
{ "_id" : ObjectId("529d082c488a806798cc30d2"), "x" : 5 }
{ "_id" : ObjectId("529d082c488a806798cc30ce"), "x" : 1 }
{ "_id" : ObjectId("529d082c488a806798cc30d6"), "x" : 9 }
{ "_id" : ObjectId("529d082c488a806798cc30d1"), "x" : 4 }
{ "_id" : ObjectId("529d082c488a806798cc30d8"), "x" : 11 }
. . .

想得到更多值,输入:

it
{ "_id" : ObjectId("529d082c488a806798cc30cf"), "x" : 2 }
{ "_id" : ObjectId("529d082c488a806798cc30dd"), "x" : 16 }
{ "_id" : ObjectId("529d082c488a806798cc30d4"), "x" : 7 }
{ "_id" : ObjectId("529d082c488a806798cc30da"), "x" : 13 }
{ "_id" : ObjectId("529d082c488a806798cc30d5"), "x" : 8 }
{ "_id" : ObjectId("529d082c488a806798cc30de"), "x" : 17 }
{ "_id" : ObjectId("529d082c488a806798cc30db"), "x" : 14 }
{ "_id" : ObjectId("529d082c488a806798cc30e1"), "x" : 20 }
. . .

想得到关于特定分区的信息,可以输入:

sh.status()
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "version" : 3,
    "minCompatibleVersion" : 3,
    "currentVersion" : 4,
    "clusterId" : ObjectId("529cae0691365bef9308cd75")
}
  shards:
    {  "_id" : "shard0000",  "host" : "162.243.243.156:27017" }
    {  "_id" : "shard0001",  "host" : "162.243.243.155:27017" }
. . .

这会提供MongoDB在不同分区分布的关于块(chunks)的信息。

 


总结

这篇教程的最后,你应该能够执行你自己的MongoDB分区配置。你的服务器具体配置和为每个集合选择的分区钥匙,将会对集群性能产生很大影响。

选择的字段要有最好的分布特性,并最能代表逻辑群组,这将会在你的数据库里反映出来。如果MongoDB只要一个单个分区获取数据,它的数据返回会非常快。







回复 (1)
  • #
  • #1 c52u 1397654553868

    看不大明白

微信扫码 立即评论




  开源的 OurJS
OurJS开源博客已经迁移到 OnceOA 平台。

  关注我们
扫一扫即可关注我们:
OnceJS

OnceOA