MongoDB中使用分片集群结构
1、mongos:
数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多mongos作为请求的入口,防止其中一个挂掉所有的mongodb请求都没有办法操作。
2、config server:
顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样 mongos 就能继续准确路由。在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货, mongodb集群就不会挂掉。
3、shard:
这就是传说中的分片了。上面提到一个机器就算能力再大也有天花板,就像军队打仗一样,一个人再厉害喝血瓶也拼不过对方的一个师。俗话说三个臭皮匠顶个诸葛亮,这个时候团队的力量就凸显出来了。在互联网也是这样,一台普通的机器做不了的多台机器来做
首先确定各个组件的数量,mongos 3个, config server 3个,数据分3片 shard server 3个,每个shard 有一个副本一个仲裁也就是 3 * 2 = 6 个,总共需要部署15个实例。这些实例可以部署在独立机器也可以部署在一台机器,我们这里测试资源有限,只准备了 3台机器,在同一台机器只要端口不同就可以
解压压缩包
tar zxf mongodb-linux-x86_64-rhel62-3.0.6.tgz
mv mongodb-linux-x86_64-rhel62-3.0.6 mongodb
分别在每台机器建立mongos 、config 、 shard1 、shard2、shard3 五个目录。
mkdir -p /usr/nacp/mongodb/mongos/log
mkdir -p /usr/nacp/mongodb/config/data
mkdir -p /usr/nacp/mongodb/config/log
mkdir -p /usr/nacp/mongodb/shard1/data
mkdir -p /usr/nacp/mongodb/shard1/log
mkdir -p /usr/nacp/mongodb/shard2/data
mkdir -p /usr/nacp/mongodb/shard2/log
mkdir -p /usr/nacp/mongodb/shard3/data
mkdir -p /usr/nacp/mongodb/shard3/log
规划5个组件对应的端口号,由于一个机器需要同时部署 mongos、config server 、shard1、shard2、shard3,所以需要用端口进行区分。这个端口可以自由定义,在本文 mongos为 20000, config server 为 21000, shard1为 22001 , shard2为22002, shard3为22003。
在每一台服务器分别启动配置服务器。
/usr/nacp/mongodb/bin/mongod --configsvr --dbpath /usr/nacp/mongodb/config/data --port 21000 --logpath /usr/nacp/mongodb/config/log/config.log --fork
在每一台服务器分别启动mongos服务器。
/usr/nacp/mongodb/bin/mongos --configdb 192.168.2.233:21000,192.168.2.235:21000,192.168.2.236:21000 --port 20000 --logpath /usr/nacp/mongodb/mongos/log/mongos.log --fork
在每一台配置各个分片的副本集。nojournal 是为了关闭日志信息。 oplogsize是为了降低 local 文件的大小
#在每个机器里分别设置分片1服务器及副本集shard1
/usr/nacp/mongodb/bin/mongod --shardsvr --replSet shard1 --port 22001 --dbpath /usr/nacp/mongodb/shard1/data --logpath /usr/nacp/mongodb/shard1/log/shard1.log --fork --nojournal --oplogSize 10
#在每个机器里分别设置分片2服务器及副本集shard2
/usr/nacp/mongodb/bin/mongod --shardsvr --replSet shard2 --port 22002 --dbpath /usr/nacp/mongodb/shard2/data --logpath /usr/nacp/mongodb/shard2/log/shard2.log --fork --nojournal --oplogSize 10
#在每个机器里分别设置分片3服务器及副本集shard3
/usr/nacp/mongodb/bin/mongod --shardsvr --replSet shard3 --port 22003 --dbpath /usr/nacp/mongodb/shard3/data --logpath /usr/nacp/mongodb/shard3/log/shard3.log --fork --nojournal --oplogSize 10
分别对每个分片配置副本集
#设置第一个分片副本集
/usr/nacp/mongodb/bin/mongo 127.0.0.1:22001
#使用admin数据库
use admin
#定义副本集配置
config = { _id:"shard1", members:[
{_id:0,host:"192.168.2.233:22001",arbiterOnly:true},
{_id:1,host:"192.168.2.235:22001"},
{_id:2,host:"192.168.2.236:22001"}
]
}
#初始化副本集配置
rs.initiate(config);
#设置第二个分片副本集
/usr/nacp/mongodb/bin/mongo 127.0.0.1:22002
#使用admin数据库
use admin
#定义副本集配置
config = { _id:"shard2", members:[
{_id:0,host:"192.168.2.233:22002"},
{_id:1,host:"192.168.2.235:22002",arbiterOnly:true},
{_id:2,host:"192.168.2.236:22002"}
]
}
#初始化副本集配置
rs.initiate(config);
#设置第三个分片副本集
/usr/nacp/mongodb/bin/mongo 127.0.0.1:22003
#使用admin数据库
use admin
#定义副本集配置
config = { _id:"shard3", members:[
{_id:0,host:"192.168.2.233:22003"},
{_id:1,host:"192.168.2.235:22003"},
{_id:2,host:"192.168.2.236:22003",arbiterOnly:true}
]
}
#初始化副本集配置
rs.initiate(config);
#mongodb默认是从主节点读写数据的,副本节点上不允许读,需要设置副本节点可以读。
repset:SECONDARY> db.getMongo().setSlaveOk();
设置分片配置,让分片生效。
如里shard是单台服务器,用 db.runCommand( { addshard : “[: ]” } )这样的命令加入,如果shard是副本集,用db.runCommand( { addshard : “replicaSetName/[:port][,serverhostname2[:port],…]” });这样的格式表示
#连接到mongos
/usr/nacp/mongodb/bin/mongo 127.0.0.1:20000
#使用admin数据库
user admin。
#串联路由服务器与分配副本集1
db.runCommand( { addshard : "shard1/192.168.2.233:22001,192.168.2.235:22001,192.168.2.236:22001"});
#串联路由服务器与分配副本集2
db.runCommand( { addshard : "shard2/192.168.2.233:22002,192.168.2.235:22002,192.168.2.236:22002"});
#串联路由服务器与分配副本集3
db.runCommand( { addshard : "shard3/192.168.2.233:22003,192.168.2.235:22003,192.168.2.236:22003"});
查看分片服务器的配置
db.runCommand( { listshards : 1 } );
#内容输出
{
"shards" : [
{
"_id" : "shard1",
"host" : "shard1/192.168.0.136:22001,192.168.0.137:22001"
},
{
"_id" : "shard2",
"host" : "shard2/192.168.0.136:22002,192.168.0.137:22002"
},
{
"_id" : "shard3",
"host" : "shard3/192.168.0.136:22003,192.168.0.137:22003"
}
],
"ok" : 1
}
分片副本集的仲裁节点,所以在上面结果没有列出来。
连接在mongos上,准备让指定的数据库、指定的集合分片生效。
#指定testdb分片生效
db.runCommand( { enablesharding :"testdb"});
#指定数据库里需要分片的集合和片键
db.runCommand( { shardcollection : "testdb.table1",key : {id: 1} } )
我们设置testdb的 table1 表需要分片,根据 id 自动分片到 shard1 ,shard2,shard3 上面去。要这样设置是因为不是所有mongodb 的数据库和表 都需要分片!
测试分片配置结果。
#连接mongos服务器
/usr/nacp/mongodb/bin/mongo 127.0.0.1:20000
#使用testdb
use testdb;
#插入测试数据
for (var i = 1; i <= 100000; i++) db.table1.save({id:i,"test1":"testval1"});
#查看分片情况如下,部分无关信息省掉了
db.table1.stats();
{
"sharded" : true,
"ns" : "testdb.table1",
"count" : 100000,
"numExtents" : 13,
"size" : 5600000,
"storageSize" : 22372352,
"totalIndexSize" : 6213760,
"indexSizes" : {
"_id_" : 3335808,
"id_1" : 2877952
},
"avgObjSize" : 56,
"nindexes" : 2,
"nchunks" : 3,
"shards" : {
"shard1" : {
"ns" : "testdb.table1",
"count" : 42183,
"size" : 0,
...
"ok" : 1
},
"shard2" : {
"ns" : "testdb.table1",
"count" : 38937,
"size" : 2180472,
...
"ok" : 1
},
"shard3" : {
"ns" : "testdb.table1",
"count" :18880,
"size" : 3419528,
...
"ok" : 1
}
},
"ok" : 1
}
java程序调用分片集群,因为我们配置了三个mongos作为入口,就算其中哪个入口挂掉了都没关系,使用集群客户端程序如下:
public class TestMongoDBShards {
public static void main(String[] args) {
try {
List addresses = new ArrayList();
ServerAddress address1 = new ServerAddress("192.168.2.233" , 20000);
ServerAddress address2 = new ServerAddress("192.168.2.235" , 20000);
ServerAddress address3 = new ServerAddress("192.168.2.236" , 20000);
addresses.add(address1);
addresses.add(address2);
addresses.add(address3);
MongoClient client = new MongoClient(addresses);
DB db = client.getDB( "testdb" );
DBCollection coll = db.getCollection( "table1" );
BasicDBObject object = new BasicDBObject();
object.append( "id" , 1);
DBObject dbObject = coll.findOne(object);
System. out .println(dbObject);
//读操作从副本节点读取
ReadPreference preference = ReadPreference. secondary();
DBObject dbObject = coll.findOne(object, null , preference);
System. out .println(dbObject);
} catch (Exception e) {
e.printStackTrace();
}
}
}
primary:默认参数,只从主节点上进行读取操作;
primaryPreferred:大部分从主节点上读取数据,只有主节点不可用时从secondary节点读取数据。
secondary:只从secondary节点上进行读取操作,存在的问题是secondary节点的数据会比primary节点数据“旧”。
secondaryPreferred:优先从secondary节点进行读取操作,secondary节点不可用时从主节点读取数据;
nearest:不管是主节点、secondary节点,从网络延迟最低的节点上读取数据。
//连接到MongoDB服务 如果是远程连接可以替换“localhost”为服务器所在IP地址
//ServerAddress()两个参数分别为 服务器地址 和 端口
ServerAddress serverAddress = new ServerAddress("localhost",27017);
List addrs = new ArrayList();
addrs.add(serverAddress);
//MongoCredential.createScramSha1Credential()三个参数分别为 用户名 数据库名称 密码
MongoCredential credential = MongoCredential.createScramSha1Credential("username", "databaseName", "password".toCharArray());
List credentials = new ArrayList();
credentials.add(credential);
//通过连接认证获取MongoDB连接
MongoClient mongoClient = new MongoClient(addrs,credentials);
MongoClientOptions.Builder build = new MongoClientOptions.Builder();
//与数据最大连接数50
build.connectionsPerHost(50);
//如果当前所有的connection都在使用中,则每个connection上可以有50个线程排队等待
build.threadsAllowedToBlockForConnectionMultiplier(50);
build.connectTimeout(1*60*1000);
build.maxWaitTime(2*60*1000);
MongoClientOptions options = build.build();
MongoClient client = new MongoClient("127.0.0.1", options);