一,概述
mongodb是一个典型的文档型nosql数据库,下面说一下Nosql包括的几大类型数据库:
NoSQL的数据存储模型
键值模型:
数据模型:key-value存储
优点:查找速度快
缺点:数据无结构,通常只被当作字符串或二进制数据
应用场景:内容缓存
实例:Redis, Dynamo
列式模型:
数据模型:数据按列存储,将同一列数据存在一起;
优点:查找迅速、可扩展性强、易于实现分布式;
缺点:功能相对SQL很有限;
应用场景:分布式文件系统或分布式存储
实例:Bigtable, Cassandra, HBase, Hypertable
文档模型:
数据模型:与键值模型类似,value指向结构化数据;
优点:数据格式要求不严格,无需事先定义结构
缺点:查询性能不高,缺乏统一查询语法
应用场景:web应用;
实例:MongoDB, CouchDB
图式模型:
数据模型:图结构模型
优点:利用图结构相关算法提高性能,并足特殊场景应用需求
缺点:难以实现分布式,功能有定向性;
应用场景:社交网络、推荐系统、关系图谱
实例:Neo4J
本文介绍的是mongodb的复制集以及sharding,下面来说一下mongodb为什么要做sharding
越来越大的数据集及不断提升吞吐量的应用程序对单台mongodb服务器来讲是一个挑战————大量的查询很快即能耗尽CPU的计算能力,而较大的数据集存储需求也有可能很快超出单节点的存储能力。最终,工作集的大多超出了系统的RAM并给I/O带去巨大压力。数据库管理系统界解决此类问题通常有两类方案:向上扩展和水平扩展。
sharding即是水平扩展的一种解决方案。它通过将数据集分布于多个也称作分片(shard)的节点上来降低单节点的访问压力。每个分片都是一个独立的数据库,所有的分片组合起来构成一个逻辑上的完整意义的数据库。因此,分片机制降低了每个分片的数据操作量及需要存储的数据量。
要构建一个 MongoDB Sharding Cluster,需要三种角色:
Shard Server:用于存储实际的数据块,也就是元数据,实际生产环境中一个shard server角色可由几台机器组个一个relica set承担,防止主机单点故障
Config Server:存储了整个 Cluster Metadata,其中包括 chunk 信息。
Route Server:前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。
下面是mongodb sharding的工作原理图:
二,实验环境
3台主机的环境:
192.168.30.116 OS:Centos 6.4 x86_64
192.168.30.117 OS:Centos 6.4 x86_64
192.168.30.119 OS:Centos 6.4 x86_64
下面是主机角色:
下面开始安装mongodb,这里使用通用的二进制包安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
[root@node1 ~] # tar zxvf mongodb-linux-x86_64-2.0.4.tgz -C /usr/local/
[root@node1 ~] # mv /usr/local/mongodb-linux-x86_64-2.0.4 /usr/local/mongodb
[root@node1 ~] # groupadd -r mongod
[root@node1 ~] # useradd -M -r -g mongod -d /mongodb/data -s /bin/false -c mongod mongod
[root@node1 ~] # mkdir /mongodb/data/shard11 -p
[root@node1 ~] # mkdir /mongodb/data/shard21 -p
[root@node1 ~] # mkdir /mongodb/data/config -p
[root@node1 ~] # mkdir -p /var/log/mongo
[root@node1 ~] # chown -R mongod.mongod /mongodb/data/ /var/log/mongo/
[root@node2 ~] # tar zxvf mongodb-linux-x86_64-2.0.4.tgz -C /usr/local/
[root@node2 ~] # mv /usr/local/mongodb-linux-x86_64-2.0.4 /usr/local/mongodb
[root@node2 ~] # groupadd -r mongod
[root@node2 ~] # useradd -M -r -g mongod -d /mongodb/data -s /bin/false -c mongod mongod
[root@node2 ~] # mkdir /mongodb/data/shard12 -p
[root@node2 ~] # mkdir /mongodb/data/shard22 -p
[root@node2 ~] # mkdir /mongodb/data/config -p
[root@node2 ~] # mkdir -p /var/log/mongo
[root@node2 ~] # chown -R mongod.mongod /mongodb/data/ /var/log/mongo/
[root@node3 ~] # tar zxvf mongodb-linux-x86_64-2.0.4.tgz -C /usr/local/
[root@node3 ~] # mv /usr/local/mongodb-linux-x86_64-2.0.4 /usr/local/mongodb
[root@node3 ~] # groupadd -r mongod
[root@node3 ~] # useradd -M -r -g mongod -d /mongodb/data -s /bin/false -c mongod mongod
[root@node3 ~] # mkdir /mongodb/data/shard13 -p
[root@node3 ~] # mkdir /mongodb/data/shard23 -p
[root@node3 ~] # mkdir /mongodb/data/config -p
[root@node3 ~] # mkdir -p /var/log/mongo
[root@node3 ~] # chown -R mongod.mongod /mongodb/data/ /var/log/mongo/
|
配置mongod
mongod在启动时通过命令行选项或配置文件(如/etc/mongod.conf)接口读取其配置属性。这两种配置接口能够提供相同的功能,因此,管理员可以根据偏好进行选择。如果要通过配置文件读取配置选项,则可以在启动mongod时使用--config或-f选项来指定配置文件的位置。
mongod的基本配置参数
配置mongod的基本工作属性可通过一些常用参数进行。
fork={true|false}: 是否以daemon方式启动mongod,为true表示其启动后其自动转入后台工作;
bind_ip=IP:指定mongod监听的IP地址;
port=PORT:指定mongod监听的端口,默认为27017;
quiet={true|false}:是否工作于静默模式,以最小化的记录日志信息;正常条件下,应该设置为true,仅在调试时才将其设置为false;
dbpath=/PATH/TO/SOMEWHERE:指定mongod存储数据的位置,通常为/data/mongodb、/var/lib/mongodb或/srv/mongodb等;
logpath=/PATH/TO/SOMEFILE:日志文件路径,例如/var/log/mongodb/mongod.log;如果没有指定文件路径,日志信息则会发往标准输出;
logappend={true|false}:设定mongod在启动时是否不覆盖日志文件中的原有日志信息,true表示以追加方式记录日志,即不覆盖;
journal={true|false}:是否启动日志功能;日志功能是保证工作于单实例模式的mongod写入数据持久性的惟一途径;
安全相关的配置参数
数据库服务器的安全机制可有效防范非授权访问。
bind_ip=IP: 指定mongod监听的IP地址;生产环境中,通常需要将其指定为一个可接受外部客户端请求的IP地址,但应该仅开放需要接受客户端请求的IP地址;如果需要指定多个地址,彼此间使用逗号分隔即可;
nounixsocket={true|false}:是否禁用mongodb的Unix套接字功能,默认为启用;主要用于本地通信;
auth={true|false}:是否启用认证功能;如果要启用,远程客户端需要被授权方能访问mongodb服务器;
配置replica sets
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
[root@node1 ~] # mv /etc/mongod.conf /etc/mongod.conf.bak
[root@node1 ~] # vi /etc/mongod.conf
shardsvr = true
replSet = shard1 port = 27017 dbpath= /mongodb/data/shard11
oplogSize = 100 logpath= /var/log/mongo/shard11 .log
logappend = true
fork = true
[root@node2 ~] # mv /etc/mongod.conf /etc/mongod.conf.bak
[root@node2 ~] # vi /etc/mongod.conf
shardsvr = true
replSet = shard1 port = 27017 dbpath= /mongodb/data/shard12
oplogSize = 100 logpath= /var/log/mongo/shard12 .log
logappend = true
fork = true
[root@node3 ~] # mv /etc/mongod.conf /etc/mongod.conf.bak
[root@node3 ~] # vi /etc/mongod.conf
shardsvr = true
replSet = shard1 port = 27017 dbpath= /mongodb/data/shard13
oplogSize = 100 logpath= /var/log/mongo/shard13 .log
logappend = true
fork = true
|
启动replica sets
1
2
3
4
|
[root@node1 ~] # /usr/local/mongodb/bin/mongod -f /etc/mongod.conf
[root@node1 ~] # netstat -anptl | grep mong
tcp 0 0 0.0.0.0:28017 0.0.0.0:* LISTEN 5888 /mongod
tcp 0 0 0.0.0.0:27017 0.0.0.0:* |
同样启动node2,node3的shard1
配置shard2用到的replica sets:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
[root@node1 ~] # vi /etc/mongod2.conf
shardsvr = true
replSet = shard2 port = 27018 dbpath= /mongodb/data/shard21
oplogSize = 100 logpath= /var/log/mongo/shard21 .log
logappend = true
fork = true
[root@node2 ~] # vi /etc/mongod2.conf
shardsvr = true
replSet = shard2 port = 27018 dbpath= /mongodb/data/shard22
oplogSize = 100 logpath= /var/log/mongo/shard22 .log
logappend = true
fork = true
[root@node3 ~] # vi /etc/mongod.conf
shardsvr = true
replSet = shard2 port = 27018 dbpath= /mongodb/data/shard23
oplogSize = 100 logpath= /var/log/mongo/shard23 .log
logappend = true
fork = true
|
分别启动node1,node2,node3的shard2
1
2
3
|
[root@node1 ~] # /usr/local/mongodb/bin/mongod -f /etc/mongod2.conf
[root@node2 ~] # /usr/local/mongodb/bin/mongod -f /etc/mongod2.conf
[root@node3 ~] # /usr/local/mongodb/bin/mongod -f /etc/mongod2.conf
|
登录其中一个shard1的mongod,初始化复制集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[root@node1 ~] # mongo 192.168.30.116:27017
> config = {_id: 'shard1' ,members: [
{_id: 0, host: '192.168.30.116:27017' },
{_id: 1, host: '192.168.30.117:27017' },
{_id: 2, host: '192.168.30.119:27017' }]
} > rs.initiate(config); [root@node1 ~] # mongo 192.168.30.116:27018
> config = {_id: 'shard2' ,members: [
{_id: 0, host: '192.168.30.116:27018' },
{_id: 1, host: '192.168.30.117:27018' },
{_id: 2, host: '192.168.30.119:27018' }]
} > rs.initiate(config); |
到此就配置好了二个replica sets,也就是准备好了二个shards
配置三台config server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
[root@node1 ~] # vi /etc/config.conf
configsvr = true
port = 20000 dbpath= /mongodb/data/config
logpath= /var/log/mongo/config .log
logappend = true
fork = true
[root@node2 ~] # vi /etc/config.conf
configsvr = true
port = 20000 dbpath= /mongodb/data/config
logpath= /var/log/mongo/config .log
logappend = true
fork = true
[root@node3 ~] # vi /etc/config.conf
configsvr = true
port = 20000 dbpath= /mongodb/data/config
logpath= /var/log/mongo/config .log
logappend = true
fork = true
[root@node1 ~] # /usr/local/mongodb/bin/mongod -f /etc/config.conf
[root@node2 ~] # /usr/local/mongodb/bin/mongod -f /etc/config.conf
[root@node3 ~] # /usr/local/mongodb/bin/mongod -f /etc/config.conf
|
配置启动mongs
1
2
3
|
[root@node1 ~] # /usr/local/mongodb/bin/mongos --configdb 192.168.30.116:20000,192.168.30.117:20000,192.168.30.119:20000 --port 30000 --chunkSize 5 --logpath /mongodb/data/mongos.log --logappend --fork
[root@node2 ~] # /usr/local/mongodb/bin/mongos --configdb 192.168.30.116:20000,192.168.30.117:20000,192.168.30.119:20000 --port 30000 --chunkSize 5 --logpath /mongodb/data/mongos.log --logappend --fork
[root@node3 ~] # /usr/local/mongodb/bin/mongos --configdb 192.168.30.116:20000,192.168.30.117:20000,192.168.30.119:20000 --port 30000 --chunkSize 5 --logpath /mongodb/data/mongos.log --logappend --fork
|
查看是mongod,config,mongos是否启动
1
2
3
4
5
6
7
8
9
|
[root@node1 ~] # ss -antpl | grep mong
LISTEN 0 128 192.168.30.116:30000 *:* users :(( "mongos" ,3627,14))
LISTEN 0 128 192.168.30.116:28017 *:* users :(( "mongod" ,22036,6))
LISTEN 0 128 192.168.30.116:28018 *:* users :(( "mongod" ,3287,8))
LISTEN 0 128 192.168.30.116:31000 *:* users :(( "mongos" ,3627,13))
LISTEN 0 128 192.168.30.116:20000 *:* users :(( "mongod" ,3487,6))
LISTEN 0 128 192.168.30.116:21000 *:* users :(( "mongod" ,3487,7))
LISTEN 0 128 192.168.30.116:27017 *:* users :(( "mongod" ,22036,7))
LISTEN 0 128 192.168.30.116:27018 *:* users :(( "mongod" ,3287,7))
|
都已启动成功
连接到其中一个mongos进程,并切换到admin数据库做以下配置:
1
2
3
4
5
|
[root@node1 ~] # /usr/local/mongodb/bin/mongo 192.168.30.116:30000/admin
MongoDB shell version: 2.0.4 connecting to: 192.168.30.116:30000 /admin
>db.runCommand( { addshard: "shard1/192.168.30.116:27017,192.168.30.117:27017,192.168.30.119:27017″,name:" s1",maxsize:20480});
>db.runCommand( { addshard: "shard2/192.168.30.116:27018,192.168.30.117:27018,192.168.30.119:27018″,name:" s2",maxsize:20480});
|
查看加入的2个shard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
mongos> db.runCommand( { listshards : 1 } ); { "shards" : [
{
"_id" : "s1" ,
"host" : "shard1/192.168.30.116:27017,192.168.30.117:27017,192.168.30.119:27017"
},
{
"_id" : "s2" ,
"host" : "shard2/192.168.30.116:27018,192.168.30.117:27018,192.168.30.119:27018"
}
],
"ok" : 1
} |
启动用数据库分片
1
2
|
mongos> db.runCommand({enablesharding : "testdb" });
{ "ok" : 1 }
|
指定分片的key
1
2
|
mongos> db.runCommand({shardcollection : "testdb.user" ,key :{_id:1}});
{ "collectionsharded" : "test.user" , "ok" : 1 }
|
插入20W行数据测试是否分片
1
2
3
4
5
6
7
8
9
10
11
12
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
44
45
46
47
48
49
50
51
52
53
54
55
56
|
mongos> use testdb switched to db testdb mongos> for (var i=1;i<=200000;i++) db.user.insert({age:i,name: "ljl" ,addr: "beijing" ,country: "china" })
mongos> db.user.stats() { "sharded" : true ,
"flags" : 1,
"ns" : "testdb.user" ,
"count" : 70001,
"numExtents" : 7,
"size" : 6160096,
"storageSize" : 11190272,
"totalIndexSize" : 2289280,
"indexSizes" : {
"_id_" : 2289280
},
"avgObjSize" : 88.00011428408166,
"nindexes" : 1,
"nchunks" : 10,
"shards" : {
"s1" : {
"ns" : "testdb.user" ,
"count" : 0,
"size" : 0,
"storageSize" : 8192,
"numExtents" : 1,
"nindexes" : 1,
"lastExtentSize" : 8192,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 8176,
"indexSizes" : {
"_id_" : 8176
},
"ok" : 1
},
"s2" : {
"ns" : "testdb.user" ,
"count" : 70001,
"size" : 6160096,
"avgObjSize" : 88.00011428408166,
"storageSize" : 11182080,
"numExtents" : 6,
"nindexes" : 1,
"lastExtentSize" : 8388608,
"paddingFactor" : 1,
"flags" : 1,
"totalIndexSize" : 2281104,
"indexSizes" : {
"_id_" : 2281104
},
"ok" : 1
}
},
"ok" : 1
} |
以上信息发现数据已经分布在不同分片上
下面模拟自动故障转移功能,在node1关闭shard1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
[root@node1 ~] # /usr/local/mongodb/bin/mongod --shutdown --shardsvr --replSet shard1 --port 27017 --dbpath /mongodb/data/shard11
killing process with pid: 22036 [root@node1 ~] # /usr/local/mongodb/bin/mongo 192.168.30.116:30000/testdb
MongoDB shell version: 2.0.4 connecting to: 192.168.30.116:30000 /testdb
mongos> db.user. find ()
{ "_id" : ObjectId( "5338092034594e87a8cb007b" ), "age" : 1, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb007c" ), "age" : 2, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb007d" ), "age" : 3, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb007e" ), "age" : 4, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb007f" ), "age" : 5, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0080" ), "age" : 6, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0081" ), "age" : 7, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0082" ), "age" : 8, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0083" ), "age" : 9, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0084" ), "age" : 10, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0085" ), "age" : 11, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0086" ), "age" : 12, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0087" ), "age" : 13, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0088" ), "age" : 14, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb0089" ), "age" : 15, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb008a" ), "age" : 16, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb008b" ), "age" : 17, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb008c" ), "age" : 18, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb008d" ), "age" : 19, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
{ "_id" : ObjectId( "5338092034594e87a8cb008e" ), "age" : 20, "name" : "ljl" , "addr" : "beijing" , "country" : "china" }
has more
mongos> |
以上信息发现数据一切正常
分片注意的几个事项:
选择sharding key的标准:
应该在哪儿存储数据?
应该从哪儿得到希望的数据?
基本法则:
sharding key应该是主键;
sharding key应该能尽量保证避免跨分片查询。