本文已参与 新人创作礼 活动,一起开启掘金创作之路
mongodb的主从很早接触过,也比较简单,monggo分布式集群一直以来想自己实际操作一遍,但是因为各种原因耽误了,今天刚好有空,终于可以亲自搭建一下了。很多事情都是这样,只有自己实践过才会印象深刻,下面分享下整个搭建过程。
mongodb的集群节点数量
官方推荐MongoDB副本集的成员数量最好为奇数,且选举要求参与的节点数量必须大于成员数的一半。假设MongoDB集群有3个节点,那么只要有2个节点活着就可以选举;如果有5个,那么活3个节点就可以选举;如果有7个节点,那么活4个就可以选举.....
MongoDB集群最多允许12个副本集节点,其中最多7个节点参与选举。这是为了减少心跳请求的网络流量和选举花费的时间,心跳每2秒发送一次。
mongodb集群架构及核心概念
集群中的四种角色:
mongos:数据库集群请求的入口,所有的请求都通过mongos进行协调,不需要在应用程序添加一个路由选择器,mongos自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多mongos作为请求的入口,防止其中一个挂掉所有的mongodb请求都没有办法操作。
config server:顾名思义为配置服务器,存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样 mongos 就能继续准确路由。在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,这个可不能丢失!就算挂掉其中一台,只要还有存货,mongodb集群就不会挂掉。
shard:这就是传说中的分片了。一台普通的机器做不了的多台机器来做,如下图:
一台机器的一个数据表 Collection1 存储了 1T 数据,压力太大了!再分给4个机器后,每个机器都是256G,则分摊了集中在一台机器的压力。在mongodb集群只要设置好了分片规则,通过mongos操作数据库就能自动把对应的数据操作请求转发到对应的分片机器上。
replica set(副本集) : 其实上图4个分片如果没有 replica set 是个不完整架构,假设其中的一个分片挂掉那四分之一的数据就丢失了,所以在高可用性的分片架构还需要对于每一个分片构建 replica set 副本集保证分片的可靠性。生产环境通常是 2个副本 + 1个仲裁。
读写方式
1.1 部署
1.1.1 部署方式
因为机器数量有限,所以本次在一台VM上模拟12个节点(3个mongos,2个数据分片,1个config分片),每个分片都是一个PSS(Primary-Secondary-Secondary)模式的数据副本集,Config副本集采用PSS(Primary-Secondary-Secondary)模式。
内部鉴权:
节点间鉴权采用keyfile方式实现鉴权,mongos与分片之间、副本集节点之间共享同一套keyfile文件。
账户设置:
管理员账户:admin/yozo@2019,具有集群及所有库的管理权限
应用账号:appuser/AppUser@01,具有appdb的owner权限
1.1.2 准备工作
1、下载安装包
下载地址:www.mongodb.com/download-ce…
使用版本是4.2.0:
# wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.0.tgz
2、解压
# tar -zxvf mongodb-linux-x86_64-rhel70-4.2.0.tgz
3、创建运行目录并将将数据复制到相关目录
# mkdir -p /data/mongodb-cluster
# cp -r /bin !$
4、创建配置文件
# mkdir /data/mongodb-cluster/conf
# vim mongo_node.conf
storage:
engine: wiredTiger
directoryPerDB: true
journal:
enabled: true
systemLog:
destination: file
logAppend: true
operationProfiling:
slowOpThresholdMs: 10000
replication:
oplogSizeMB: 10240
processManagement:
fork: true
net:
http:
enabled: false
security:
authorization: "enabled"
# vim mongos.conf
systemLog:
destination: file
logAppend: true
processManagement:
fork: true
net:
http:
enabled: false
5、创建keyfile文件(采用随机算法生成,用作节点内部通讯的密钥文件)
# mkdir /data/mongodb-cluster/keyfile
# openssl rand -base64 756 > mongo.key
# chmod 400 mongo.key
6、创建节点目录
# WORK_DIR=/data/mongodb-cluster
# mkdir -p $WORK_DIR/nodes/config/n1/data
# mkdir -p $WORK_DIR/nodes/config/n2/data
# mkdir -p $WORK_DIR/nodes/config/n3/data
# mkdir -p $WORK_DIR/nodes/shard1/n1/data
# mkdir -p $WORK_DIR/nodes/shard1/n2/data
# mkdir -p $WORK_DIR/nodes/shard1/n3/data
# mkdir -p $WORK_DIR/nodes/shard2/n1/data
# mkdir -p $WORK_DIR/nodes/shard2/n2/data
# mkdir -p $WORK_DIR/nodes/shard2/n3/data
# mkdir -p $WORK_DIR/nodes/mongos/n1
# mkdir -p $WORK_DIR/nodes/mongos/n2
# mkdir -p $WORK_DIR/nodes/mongos/n3
整个目录结构如下:
1.1.3 创建Config副本集
启动3个Config,下面是启动脚本:
#!/bin/bash
WORK_DIR=/data/mongodb-cluster
KEYFILE=$WORK_DIR/keyfile/mongo.key
CONFFILE=$WORK_DIR/conf/mongo_node.conf
MONGOD=$WORK_DIR/bin/mongod
$MONGOD --port 26001 --configsvr --replSet configReplSet --keyFile $KEYFILE --dbpath $WORK_DIR/nodes/config/n1/data --pidfilepath $WORK_DIR/nodes/config/n1/db.pid --logpath $WORK_DIR/nodes/config/n1/db.log --config $CONFFILE
$MONGOD --port 26002 --configsvr --replSet configReplSet --keyFile $KEYFILE --dbpath $WORK_DIR/nodes/config/n2/data --pidfilepath $WORK_DIR/nodes/config/n2/db.pid --logpath $WORK_DIR/nodes/config/n2/db.log --config $CONFFILE
$MONGOD --port 26003 --configsvr --replSet configReplSet --keyFile $KEYFILE --dbpath $WORK_DIR/nodes/config/n3/data --pidfilepath $WORK_DIR/nodes/config/n3/db.pid --logpath $WORK_DIR/nodes/config/n3/db.log --config $CONFFILE
启动的时候报错了:
查看官网文档才发现从3.2版本以后已移除:
然后从mongo_node.conf配置文件中删掉http配置项:
重新创建,成功:
连接其中一个Config进程,执行副本集初始化:
# /data/mongodb-cluster/bin/mongo --port 26001 --host 127.0.0.1
> cfg={
_id:"configReplSet",
configsvr: true,
members:[
{_id:0, host:'127.0.0.1:26001'},
{_id:1, host:'127.0.0.1:26002'},
{_id:2, host:'127.0.0.1:26003'}
]};
> rs.initiate(cfg);
1.1.4 创建分片
创建分片脚本:
#!/bin/bash
WORK_DIR=/data/mongodb-cluster
KEYFILE=$WORK_DIR/keyfile/mongo.key
CONFFILE=$WORK_DIR/conf/mongo_node.conf
MONGOD=$WORK_DIR/bin/mongod
echo "start shard1 replicaset"
$MONGOD --port 27001 --shardsvr --replSet shard1 --keyFile $KEYFILE --dbpath $WORK_DIR/nodes/shard1/n1/data --pidfilepath $WORK_DIR/nodes/shard1/n1/db.pid --logpath $WORK_DIR/nodes/shard1/n1/db.log --config $CONFFILE
$MONGOD --port 27002 --shardsvr --replSet shard1 --keyFile $KEYFILE --dbpath $WORK_DIR/nodes/shard1/n2/data --pidfilepath $WORK_DIR/nodes/shard1/n2/db.pid --logpath $WORK_DIR/nodes/shard1/n2/db.log --config $CONFFILE
$MONGOD --port 27003 --shardsvr --replSet shard1 --keyFile $KEYFILE --dbpath $WORK_DIR/nodes/shard1/n3/data --pidfilepath $WORK_DIR/nodes/shard1/n3/db.pid --logpath $WORK_DIR/nodes/shard1/n3/db.log --config $CONFFILE
连接其中一个Shard进程,执行副本集初始化:
# /data/mongodb-cluster/bin/mongo --port 27001 --host 127.0.0.1
> cfg={
_id:"shard1",
members:[
{_id:0, host:'127.0.0.1:27001'},
{_id:1, host:'127.0.0.1:27002'},
{_id:2, host:'127.0.0.1:27003'}
]};
> rs.initiate(cfg);
参考以上步骤,启动Shard2的3个实例进程,并初始化副本集。
1.1.5 启动mongos路由
启动路由脚本:
#!/bin/bash
WORK_DIR=/data/mongodb-cluster
KEYFILE=$WORK_DIR/keyfile/mongo.key
CONFFILE=$WORK_DIR/conf/mongos.conf
MONGOS=$WORK_DIR/bin/mongos
echo "start mongos instances"
$MONGOS --port 25001 --configdb configReplSet/127.0.0.1:26001,127.0.0.1:26002,127.0.0.1:26003 --keyFile $KEYFILE --pidfilepath $WORK_DIR/nodes/mongos/n1/db.pid --logpath $WORK_DIR/nodes/mongos/n1/db.log --config $CONFFILE
$MONGOS --port 25002 --configdb configReplSet/127.0.0.1:26001,127.0.0.1:26002,127.0.0.1:26003 --keyFile $KEYFILE --pidfilepath $WORK_DIR/nodes/mongos/n2/db.pid --logpath $WORK_DIR/nodes/mongos/n2/db.log --config $CONFFILE
$MONGOS --port 25003 --configdb configReplSet/127.0.0.1:26001,127.0.0.1:26002,127.0.0.1:26003 --keyFile $KEYFILE --pidfilepath $WORK_DIR/nodes/mongos/n3/db.pid --logpath $WORK_DIR/nodes/mongos/n3/db.log --config $CONFFILE
这里遇到了同样的问题:
把mongos.conf配置文件中的http配置去掉就好:
接入其中一个mongos实例,执行添加分片操作:
# /data/mongodb-cluster/bin/mongo --port 25001 --host 127.0.0.1
> sh.addShard("shard1/127.0.0.1:27001")
> sh.addShard("shard2/127.0.0.1:27004")
1.1.6 初始化用户
接入其中一个mongos实例,添加管理员用户:
# /data/mongodb-cluster/bin/mongo --port 25001 --host 127.0.0.1
> use admin;
> db.createUser({
user:'admin',pwd:'yozo@2019',
roles:[
{role:'clusterAdmin',db:'admin'},
{role:'userAdminAnyDatabase',db:'admin'},
{role:'dbAdminAnyDatabase',db:'admin'},
{role:'readWriteAnyDatabase',db:'admin'}
]})
当前admin用户具有集群管理权限、所有数据库的操作权限。接下来的所有操作要求先
过鉴权。分片集群中的访问都会通过mongos入口,而鉴权数据是存储在config副本集中的,即config实例中system.users数据库存储了集群用户及角色权限配置。mongos与shard实例则通过内部鉴权(keyfile机制)完成,因此shard实例上可以通过添加本地用户以方便操作管理。在一个副本集上,只需要在Primary节点上添加用户及权限,相关数据会自动同步到Secondary节点。
1.1.7 查看集群状态
mongos> use admin
mongos> db.auth('admin','yozo@2019')
mongos> sh.status()
1.1.8 数据操作
在案例中,创建appuser用户、为数据库实例appdb启动分片。
>use appdb
>db.createUser({user:'appuser',pwd:'AppUser@01',roles:[{role:'dbOwner',db:'appdb'}]})
>sh.enableSharding("appdb")
创建集合book,为其执行分片初始化:
>use appdb
>db.createCollection("book")
>db.device.ensureIndex({createTime:1})
>sh.shardCollection("appdb.book", {bookId:"hashed"}, false, { numInitialChunks: 4} )
继续往device集合写入1000W条记录,观察chunks的分布情况
> use appdb
> var cnt = 0;
> for(var i=0; i<1000; i++){
var dl = [];
for(var j=0; j<100; j++){
dl.push({
"bookId" : "BBK-" + i + "-" + j,
"type" : "Revision",
"version" : "IricSoneVB0001",
"title" : "Jackson's Life",
"subCount" : 10,
"location" : "China CN Shenzhen Futian District",
"author" : {
"name" : 50,
"email" : "RichardFoo@yahoo.com",
"gender" : "female"
},
"createTime" : new Date()
});
}
cnt += dl.length;
db.book.insertMany(dl);
print("insert ", cnt);
}
查看分片状态:
db.book.getShardDistribution()
总结:
- Mongodb集群架构由Mongos、Config副本集和多个分片组成;
- 部署过程中先初始化Config副本集、分片副本集,最后通过Mongos添加分片
- Config副本集存储了集群访问的用户及角色权限,为了方便管理,可以给分片副本集添加本地用户
- Mongodb提供了LocalException机制,首次安装数据库时可以在本机直接添加用户 参考文档:
好了,今天的分享就到这里了,希望对大家有所帮助,我们下期再见~~