前言
MongoDB是一个由C++语言编写的基于分布式文件存储的数据库,是当前NoSQL数据库中比较热门的一种,旨在为Web应用提供可扩展的高性能数据存储解决方案。本文介绍MongoDB复制集及数据分片。
MongoDB
简介
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。支持的数据结构非常松散,因此可以存储比较复杂的数据类型。最大的特点是其支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
特点及功能特性
特点:高性能、易部署、易使用,存储数据非常方便
主要功能特性有:
面向集合存储,易存储对象类型的数据
模式自由
支持动态查询
支持完全索引,包含内部对象
支持查询
支持复制和故障恢复
使用高效的二进制数据存储,包括大型对象(如视频等)
自动处理碎片,以支持云计算层次的扩展性
支持Ruby,Python,Java,C++,PHP等多种语言
文件存储格式为Bson(一种Json的扩展)
可通过网络访问
优缺点
与关系型数据库相比,MongoDB的优点:
弱一致性(最终一致),更能保证用户的访问速度
文档结构的存储方式,能够更便捷的获取数据
内置GridFS,支持大容量的存储
内置Sharding
第三方支持丰富(这是与其他的NoSQL相比,MongoDB也具有的优势)
性能优越
与关系型数据库相比,MongoDB的缺点:
不支持事务操作
占用空间过大
没有成熟的维护工具
MongoDB复制集
复制集
工作特性:
至少三个,且应该为奇数个节点,可使用arbiter(仲裁者)来参与选举
复制集可实现失效自动转移(通过选举方式实现)
复制集的中特殊类型的节点:
0优先级的节点:冷备节点,不会被选举成为主节点,但可以参与选举
被隐藏的从节点:首先是一个0优先级的从节点,且对客户端不可见
延迟复制的从节点:首先是一个0优先级的从节点,且复制时间落后于主节点一个固定时长
arbiter: 仲裁者
复制集架构
<a href="http://s3.51cto.com/wyfs02/M01/6F/A3/wKiom1WjeEzBZzG-AAFIL7klLVc870.jpg" target="_blank"></a>
实验拓扑
1
2
<code>#系统环境:CentOS6.6</code>
<code>#各节点时间已同步</code>
配置过程
安装所需软件
3
4
5
6
7
<code>[root@node1 ~]</code><code># cd mongodb/</code>
<code>[root@node1 mongodb]</code><code># ls</code>
<code>mongodb-org-server-2.6.10-1.x86_64.rpm mongodb-org-tools-2.6.10-1.x86_64.rpm</code>
<code>mongodb-org-shell-2.6.10-1.x86_64.rpm</code>
<code>[root@node1 mongodb]</code><code># yum install *.rpm -y #3个包都安装</code>
<code>#所有节点都执行以上安装操作</code>
编辑配置文件
8
9
10
11
12
<code>[root@node1 ~]</code><code># vim /etc/mongod.conf </code>
<code>logpath=</code><code>/var/log/mongodb/mongod</code><code>.log</code>
<code>logappend=</code><code>true</code>
<code>fork=</code><code>true</code>
<code>dbpath=</code><code>/mongodb/data</code> <code>#数据位置</code>
<code>pidfilepath=</code><code>/var/run/mongodb/mongod</code><code>.pid</code>
<code>#bind_ip=127.0.0.1 #默认监听本机,注释掉监听所有</code>
<code>httpinterface=</code><code>true</code> <code>#开放web</code>
<code>rest=</code><code>true</code>
<code>replSet=testSet </code><code>#复制集名,可自定义</code>
<code>replIndexPrefetch=_id_only</code>
同步配置文件至各节点
<code>[root@node1 ~]</code><code># scp /etc/mongod.conf node3:/etc</code>
<code>root@node3's password: </code>
<code>mongod.conf 100% 1567 1.5KB</code><code>/s</code> <code>00:00 </code>
<code>[root@node1 ~]</code><code># scp /etc/mongod.conf node4:/etc</code>
<code>root@node4's password: </code>
<code>mongod.conf 100% 1567 1.5KB</code><code>/s</code> <code>00:00</code>
创建数据目录
<code>[root@node1 ~]</code><code># mkdir /mongodb/data -pv</code>
<code>mkdir</code><code>: created directory `</code><code>/mongodb</code><code>'</code>
<code>mkdir</code><code>: created directory `</code><code>/mongodb/data</code><code>'</code>
<code>[root@node1 ~]</code><code># chown -R mongod.mongod /mongodb</code>
<code>#各节点都执行以上操作</code>
启动服务
<code>[root@node1 ~]</code><code># service mongod start</code>
<code>Starting mongod: [ OK ]</code>
<code>[root@node1 mongodb]</code><code># ss -tnl | grep 27017</code>
<code>LISTEN 0 128 *:27017 *:* </code>
<code>#各节点启动服务,启动过程中需要初始化数据,故启动较慢</code>
连接数据库做初始化
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<code>[root@node1 ~]</code><code># mongo</code>
<code>MongoDB shell version: 2.6.10</code>
<code>connecting to: </code><code>test</code>
<code>Welcome to the MongoDB shell.</code>
<code>For interactive help, </code><code>type</code> <code>"help"</code><code>.</code>
<code>For </code><code>more</code> <code>comprehensive documentation, see</code>
<code> </code><code>http:</code><code>//docs</code><code>.mongodb.org/</code>
<code>Questions? Try the support group</code>
<code> </code><code>http:</code><code>//groups</code><code>.google.com</code><code>/group/mongodb-user</code>
<code>> rs.initiate()</code>
<code>{</code>
<code> </code><code>"info2"</code> <code>: </code><code>"no configuration explicitly specified -- making one"</code><code>,</code>
<code> </code><code>"me"</code> <code>: </code><code>"node1.scholar.com:27017"</code><code>,</code>
<code> </code><code>"info"</code> <code>: </code><code>"Config now saved locally. Should come online in about a minute."</code><code>,</code>
<code> </code><code>"ok"</code> <code>: 1</code>
<code>}</code>
<code>#初始化成功,查看状态信息</code>
<code>> rs.status()</code>
<code> </code><code>"set"</code> <code>: </code><code>"testSet"</code><code>,</code>
<code> </code><code>"date"</code> <code>: ISODate(</code><code>"2015-07-13T12:33:27Z"</code><code>),</code>
<code> </code><code>"myState"</code> <code>: 1,</code>
<code> </code><code>"members"</code> <code>: [</code>
<code> </code><code>{</code>
<code> </code><code>"_id"</code> <code>: 0,</code>
<code> </code><code>"name"</code> <code>: </code><code>"node1.scholar.com:27017"</code><code>,</code>
<code> </code><code>"health"</code> <code>: 1,</code>
<code> </code><code>"state"</code> <code>: 1,</code>
<code> </code><code>"stateStr"</code> <code>: </code><code>"PRIMARY"</code><code>,</code>
<code> </code><code>"uptime"</code> <code>: 1111,</code>
<code> </code><code>"optime"</code> <code>: Timestamp(1436790736, 1),</code>
<code> </code><code>"optimeDate"</code> <code>: ISODate(</code><code>"2015-07-13T12:32:16Z"</code><code>),</code>
<code> </code><code>"electionTime"</code> <code>: Timestamp(1436790737, 1),</code>
<code> </code><code>"electionDate"</code> <code>: ISODate(</code><code>"2015-07-13T12:32:17Z"</code><code>),</code>
<code> </code><code>"self"</code> <code>: </code><code>true</code>
<code> </code><code>}</code>
<code> </code><code>],</code>
<code>testSet:PRIMARY> </code>
<code>#已成为主节点</code>
添加节点
44
45
46
47
48
49
50
51
52
53
54
55
56
<code>testSet:PRIMARY> rs.add(</code><code>"172.16.10.125"</code><code>)</code>
<code>{ </code><code>"ok"</code> <code>: 1 }</code>
<code>testSet:PRIMARY> rs.add(</code><code>"172.16.10.126"</code><code>)</code>
<code>#查看各节点状态</code>
<code>testSet:PRIMARY> rs.status()</code>
<code> </code><code>"date"</code> <code>: ISODate(</code><code>"2015-07-13T12:41:07Z"</code><code>),</code>
<code> </code><code>"uptime"</code> <code>: 1571,</code>
<code> </code><code>"optime"</code> <code>: Timestamp(1436791178, 1),</code>
<code> </code><code>"optimeDate"</code> <code>: ISODate(</code><code>"2015-07-13T12:39:38Z"</code><code>),</code>
<code> </code><code>},</code>
<code> </code><code>"_id"</code> <code>: 1,</code>
<code> </code><code>"name"</code> <code>: </code><code>"172.16.10.125:27017"</code><code>,</code>
<code> </code><code>"state"</code> <code>: 2,</code>
<code> </code><code>"stateStr"</code> <code>: </code><code>"SECONDARY"</code><code>,</code>
<code> </code><code>"uptime"</code> <code>: 98,</code>
<code> </code><code>"lastHeartbeat"</code> <code>: ISODate(</code><code>"2015-07-13T12:41:06Z"</code><code>),</code>
<code> </code><code>"lastHeartbeatRecv"</code> <code>: ISODate(</code><code>"2015-07-13T12:41:05Z"</code><code>),</code>
<code> </code><code>"pingMs"</code> <code>: 1,</code>
<code> </code><code>"syncingTo"</code> <code>: </code><code>"node1.scholar.com:27017"</code>
<code> </code><code>"_id"</code> <code>: 2,</code>
<code> </code><code>"name"</code> <code>: </code><code>"172.16.10.126:27017"</code><code>,</code>
<code> </code><code>"uptime"</code> <code>: 89,</code>
<code> </code><code>"lastHeartbeatRecv"</code> <code>: ISODate(</code><code>"2015-07-13T12:41:06Z"</code><code>),</code>
创建数据,验证是否同步
<code>#主节点</code>
<code>testSet:PRIMARY> use testdb</code>
<code>switched to db testdb</code>
<code>testSet:PRIMARY> db.students.insert({name: </code><code>"ZhangSan"</code><code>,age: </code><code>"21"</code><code>})</code>
<code>WriteResult({ </code><code>"nInserted"</code> <code>: 1 })</code>
<code>#从节点</code>
<code>testSet:SECONDARY> rs.slaveOk() </code><code>#每个从节点首先申明从节点准备完毕才可同步</code>
<code>testSet:SECONDARY> use testdb</code>
<code>testSet:SECONDARY> show collections</code>
<code>students</code>
<code>system.indexes</code>
<code>testSet:SECONDARY> db.students.findOne()</code>
<code> </code><code>"_id"</code> <code>: ObjectId(</code><code>"55a3b494ebcafd9edbdfce4d"</code><code>),</code>
<code> </code><code>"name"</code> <code>: </code><code>"ZhangSan"</code><code>,</code>
<code> </code><code>"age"</code> <code>: </code><code>"21"</code>
<code>#验证从节点是否可写</code>
<code>testSet:SECONDARY> db.classes.insert({class: </code><code>"2"</code><code>,numstu: </code><code>"50"</code><code>})</code>
<code>WriteResult({ </code><code>"writeError"</code> <code>: { </code><code>"code"</code> <code>: undefined, </code><code>"errmsg"</code> <code>: </code><code>"not master"</code> <code>} })</code>
<code>#由此可见,只有主节点才可写</code>
以上便是复制集的相关配置,如果主节点故障,从节点会自动选举出新的主节点,这里就不再演示
数据分片
分片缘由
分片(sharding)是MongoDB用来将大型集合分割到不同服务器(集群)上所采用的方法。当单台服务器CPU,Memory,IO等无法满足需求,就需要将数据分片存放,减缓服务器压力。
分片架构
<a href="http://s3.51cto.com/wyfs02/M01/6F/A2/wKioL1WjvPuQ0HVeAAG3WbZrFqw217.jpg" target="_blank"></a>
<a href="http://s3.51cto.com/wyfs02/M02/6F/A2/wKioL1WjwLeTs5aJAACNKqEdgL8258.jpg" target="_blank"></a>
因为以上做过实验我们首先来清理一下数据
<code>[root@node1 ~]</code><code># service mongod stop</code>
<code>Stopping mongod: [ OK ]</code>
<code>[root@node1 ~]</code><code># rm -rf /mongodb/data/*</code>
<code>#各节点都执行以上操作,若第一次做可忽略</code>
Config Server配置
<code>#安装所需包</code>
<code>[root@node2 mongodb]</code><code># ls</code>
<code>[root@node2 mongodb]</code><code># yum install *.rpm -y</code>
修改配置文件
<code>[root@node2 ~]</code><code># vim /etc//mongod.conf </code>
<code>dbpath=</code><code>/mongodb/data</code>
<code>configsvr=</code><code>true</code> <code>#开启config server </code>
<code>#bind_ip=127.0.0.1 </code>
<code>httpinterface=</code><code>true</code>
<code>[root@node2 ~]</code><code># mkdir /mongodb/data -pv</code>
<code>[root@node2 ~]</code><code># chown -R mongod.mongod /mongodb</code>
<code>[root@node2 ~]</code><code># service mongod start</code>
<code>[root@node2 ~]</code><code># ss -tnl | grep 27019</code>
<code>LISTEN 0 128 *:27019 *:* </code>
<code>#监听端口已发生改变,不再是27017</code>
Mongos配置
<code>#安装所需包,Mongos节点只装此包即可,无需装第一次实验中的mongod的相关包</code>
<code>[root@node1 ~]</code><code># yum install mongodb-org-mongos-2.6.10-1.x86_64.rpm -y</code>
<code>[root@node1 ~]</code><code># mongos --configdb=172.16.10.124 --fork --logpath=/var/log/mongodb/mongos.log</code>
<code>2015-07-13T22:22:47.404+0800 warning: running with 1 config server should be </code><code>done</code> <code>only fo</code>
<code>r testing purposes and is not recommended </code><code>for</code> <code>production</code>
<code>about to fork child process, waiting </code><code>until</code> <code>server is ready </code><code>for</code> <code>connections.</code>
<code>forked process: 3583</code>
<code>child process started successfully, parent exiting</code>
<code>#--configdb指定config server --logpath指定日志位置 --fork后台运行</code>
Shard配置
<code>#以为我们第一次实验安装过软件了,下面直接修改配置文件</code>
<code>[root@node3 ~]</code><code># vim /etc/mongod.conf </code>
<code>dbpath=</code><code>/mongodb/data</code>
<code>[root@node3 ~]</code><code># service mongod start</code>
<code>#两个shard节点都执行以上操作</code>
Mongos节点添加Shard节点
<code>[root@node1 ~]</code><code># mongo --host 172.16.10.123</code>
<code>connecting to: 172.16.10.123:27017</code><code>/test</code>
<code>mongos> sh.addShard(</code><code>"172.16.10.125"</code><code>)</code>
<code>{ </code><code>"shardAdded"</code> <code>: </code><code>"shard0000"</code><code>, </code><code>"ok"</code> <code>: 1 }</code>
<code>mongos> sh.addShard(</code><code>"172.16.10.126"</code><code>)</code>
<code>{ </code><code>"shardAdded"</code> <code>: </code><code>"shard0001"</code><code>, </code><code>"ok"</code> <code>: 1 }</code>
<code>mongos> sh.status()</code>
<code>--- Sharding Status --- </code>
<code> </code><code>sharding version: {</code>
<code> </code><code>"_id"</code> <code>: 1,</code>
<code> </code><code>"version"</code> <code>: 4,</code>
<code> </code><code>"minCompatibleVersion"</code> <code>: 4,</code>
<code> </code><code>"currentVersion"</code> <code>: 5,</code>
<code> </code><code>"clusterId"</code> <code>: ObjectId(</code><code>"55a3c9ba131b83ff44e19435"</code><code>)</code>
<code> </code><code>shards:</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"shard0000"</code><code>, </code><code>"host"</code> <code>: </code><code>"172.16.10.125:27017"</code> <code>}</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"shard0001"</code><code>, </code><code>"host"</code> <code>: </code><code>"172.16.10.126:27017"</code> <code>}</code>
<code> </code><code>databases:</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"admin"</code><code>, </code><code>"partitioned"</code> <code>: </code><code>false</code><code>, </code><code>"primary"</code> <code>: </code><code>"config"</code> <code>}</code>
对所需对象启用分片功能
<code>#对数据库启用sharding功能</code>
<code>mongos> sh.enableSharding(</code><code>"testdb"</code><code>)</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"test"</code><code>, </code><code>"partitioned"</code> <code>: </code><code>false</code><code>, </code><code>"primary"</code> <code>: </code><code>"shard0000"</code> <code>}</code>
<code> </code><code>{ </code><code>"_id"</code> <code>: </code><code>"testdb"</code><code>, </code><code>"partitioned"</code> <code>: </code><code>true</code><code>, </code><code>"primary"</code> <code>: </code><code>"shard0000"</code> <code>}</code>
<code>#指定需要分片的Collection及索引</code>
<code>mongos> sh.shardCollection(</code><code>"testdb.students"</code><code>,{</code><code>"age"</code><code>: 1})</code>
<code>{ </code><code>"collectionsharded"</code> <code>: </code><code>"testdb.students"</code><code>, </code><code>"ok"</code> <code>: 1 }</code>
<code> </code><code>testdb.students</code>
<code> </code><code>shard key: { </code><code>"age"</code> <code>: 1 }</code>
<code> </code><code>chunks:</code>
<code> </code><code>shard0000 1</code>
<code> </code><code>{ </code><code>"age"</code> <code>: { </code><code>"$minKey"</code> <code>: 1 } } -->> { </code><code>"age"</code> <code>: { </code><code>"$maxKey"</code> <code>: 1 } }</code>
<code>on : shard0000 Timestamp(1, 0)</code>
分片功能已开启,接下来我们手动创建数据来验证是否会分片
<code>mongos> use testdb</code>
<code>mongos> </code><code>for</code> <code>(i=1;i<=100000;i++) db.students.insert({name:</code><code>"student"</code><code>+i,age:(i%120),address:</code><code>"China"</code><code>})</code>
<code>mongos> db.students.</code><code>find</code><code>().count()</code>
<code>100000</code>
<code> </code><code>shard0001 1</code>
<code> </code><code>shard0000 2</code>
<code> </code><code>{ </code><code>"age"</code> <code>: { </code><code>"$minKey"</code> <code>: 1 } } -->> { </code><code>"age"</code> <code>: 1 } on : shard0001 Timestamp(2, 0) </code>
<code> </code><code>{ </code><code>"age"</code> <code>: 1 } -->> { </code><code>"age"</code> <code>: 119 } on : shard0000 Timestamp(2, 2) </code>
<code> </code><code>{ </code><code>"age"</code> <code>: 119 } -->> { </code><code>"age"</code> <code>: { </code><code>"$maxKey"</code> <code>: 1 } } on : shard0000 Timestamp(2, 3)</code>
查看数据状态会发现数据已被分到不同shard上,至此,数据分片成功实现
The end
MongoDB复制集及数据分片就先说到这里了,通过以上简单应用可以看出,MongoDB在大数据处理方面比关系型数据库略胜一筹,但由于MongoDB目前还处在发展阶段,在实际生产环境中还有许多问题有待解决,不过相信在未来MongoDB会更加出色。以上仅为个人学习整理,如有错漏,大神勿喷~~~
本文转自 北城书生 51CTO博客,原文链接:http://blog.51cto.com/scholar/1673939