说明
突然发现更需要的是副本集群,而不是分片式集群。
分片式集群将数据切分成了一块块,集群的每台机器上理论都不应该有完整的数据;而为了保证机器坏掉时还能够正常访问所有数据,就要做冗余:每个分片要有副本(或/和仲裁)。这样原始数据至少膨胀了3倍,而且还要协调好分片的分布(例如至少可以容忍任意两台物理机挂掉而数据不受影响)。
所以,分片集群应该面对的是海量的数据,至少10台以上的物理机部署。
目前我的业务目标是:
- 1 机器(节点)的动态迁移
- 2 并发查询(考虑大量查数的情况)
- 3 分布式计算
我觉得在10个T以内的数据都不必称为大数据,用高性能台式机集群就可以处理。Mongo会对数据进行压缩,10个T的数据存储空间估计4个T左右就够了,普通的计算机,机上pci扩展卡,单机的固态很容易上10T容量。如果数据到了PB级,那么可能就要考虑分片集群了。
内容
踩了不少坑,算是还了账(之前搭建分片的过程过于顺利了)
以下是本次的架构目标图:在局域网的三台机器上启动副本集,当我们在主节点上写入时,副本集会自动进行同步。对于用户的使用来说,不必去管当前的主节点是谁,直接去读写,这时候我要观察一下读的速度是否有提升(是否会balance)。
1 背景
Mongo的集群结构有主从、副本和分片三种形式。由于副本和主从非常相似,但是更健壮,我好像有看到文章说以后就没有主从了,使用副本。
上面已经提过,分片集群目前并不适合我用,而且构建起来更为麻烦一些。所以我打算先专心搞好副本集。
关于副本集群的介绍
好文章还是比较有帮助的:这篇不错关于Mongo副本集的介绍
里面和我相关的一些要点记录一下:
- 1 MongoDB 的副本集是自带故障转移功能的主从复制
- 2 一个副本集最多可以有50 个成员,但只有7 个投票成员。如果副本集已经有 7 个投票成员,则额外的成员必须是非投票成员
- 3 Oplog的配置至少1个G(WiredTiger Storage Engine)
- 4 参与选举的节点数量必须大于副本集总节点数量的一半,如果已经小于一半了所有节点保持只读状态(所以主节点+副本+仲裁必须为奇数)
副本成员的类型(从参考文章中截过来的),这个参考信息还是很有用的。例如:
- 1 Secondary是最常用的副本
- 2 Arbiter来确保可以选举
- 3 Priority0 来确保不担任主节点
- 4 Vote0 看起来像旁观者
- 5 Hidden平时不会启用
- 6 Delayed 用来做时间镜像恢复
2 搭建
- 1 程序/配置文件保存在opt目录下
- 2 数据文件夹保存在data目录下
2.1 配置文件
本次实验集群的搭建我省去了用户的授权认证、集群成员的秘钥这些设置。
主要的配置文件如下(放在opt下面):
mongod.conf
storage:
dbPath: /home/mongod/db #分片数据库路径
journal:
enabled: true
directoryPerDB: true
# engine:
# mmapv1:
# wiredTiger:
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /home/mongod/log/mongod.log # 副本日志
# network interfaces
net:
bindIpAll: true
# how the process runs
processManagement:
fork: false
# security:
# keyFile: /etc/mongo.key
# authorization: enabled
# set auth?
# setParameter:
# enableLocalhostAuthBypass: false
#operationProfiling:
replication:
oplogSizeMB: 500
replSetName: myrs #副本集名称
2.2 数据文件
切到data路径,执行这个脚本快速创建文件夹并设置权限
create_shard_folder.sh
脚本内容
#!bin/bash
mkdir -p $1
mkdir $1/db
mkdir $1/log
touch $1/log/mongod.log
chmod -R 777 $1
执行(假设要创建名为replica的文件夹)
proc_path=/opt/aprojects/Mongo_Replica_24831/
data_path=/data/aprojects/Mongo_Replica_24831/
sh ${proc_path}/create_shard_folder.sh ${data_path}/replica
2.3 分别启动mongo
在m1和m7上分别启动(我使用 -it 启动,方便看他们打印出来的信息)
proc_path=/opt/aprojects/Mongo_Replica_24831/
data_path=/data/aprojects/Mongo_Replica_24831/
image_name="Mongo镜像"
port=25001
docker run -it \
--name='mongo_rs_data' \
--rm \
-v ${data_path}/replica:/home/mongod \
-v ${proc_path}replica/mongod.conf:/etc/mongod.conf \
-p ${port}:27017 \
${image_name} \
-f /etc/mongod.conf
2.4 设置集群
然后这里踩了一个坑,坑了我一天。
首先切进任意一个副本的mongo shell
docker exec -it mongo_rs_data mongo
> rs.status()
{
"ok" : 0,
"errmsg" : "no replset config has been received",
"code" : 94,
"codeName" : "NotYetInitialized"
}
此时是全新的数据库,所以不会有任何信息,然后进行初始化。这里踩的坑就是:一开始我认为可以使用add方式直接开始添加新的集群节点,但结果是我发现节点之间开始通信,但是新的节点始终处于START
状态,不会进入正常状态(例如 Secondary)。
节点的流转状态如下:
- STARTUP :刚加入到复制集中,配置还未加载
- STARTUP2 :配置已加载完,初始化状态
- RECOVERING :正在恢复,不适用读
- ARBITER: 仲裁者
- DOWN :节点不可到达
- UNKNOWN :未获取其他节点状态而不知是什么状态,一般发生在只有两个成员的架构,脑裂
- REMOVED :移除复制集
- ROLLBACK :数据回滚,在回滚结束时,转移到 RECOVERING 或 SECONDARY 状态
- FATAL :出错。查看日志 grep “replSet FATAL” 找出错原因,重新做同步
- PRIMARY :主节点
- SECONDARY :备份节点
此时在新的节点上查看,会和之前刚进入primary的mongo shell一样,告诉你没有配置文件。然后,…, 我翻了很多很多文章也没搞明白为什么。
我的猜想是:主节点初始化的时候需要一个配置文件,我使用add语句认为mongo会自动创建一个默认的配置文件(用于分发),但实际上没有。而使用下面的语句,则会产生这样的配置文件。(并且后续再单个add节点的时候也是没问题的)。
也怪我自己没有详细了解mongo的使用吧,但我确实没那么多时间。
rs.initiate(
{
_id : "myrs",
members: [
{ _id : 0, host : "192.168.0.187:25001",priority:5 },
{ _id : 1, host : "192.168.0.4:25001",priority:3 },
]
})
一开始节点可能会认为自己是Secondary,但是不一会就会变成Primary
myrs:SECONDARY> rs.status()
...
"members" : [
{
"_id" : 0,
"name" : "192.168.0.187:25001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
...
然后我插入数据
myrs:PRIMARY> db.test.insert( { item : "card", qty : 15 })
WriteResult({ "nInserted" : 1 })
myrs:PRIMARY> show dbs;
admin 0.000GB
config 0.000GB
local 0.000GB
test 0.000GB
再加入一个节点
rs.add('192.168.0.4:25002')
从节点很快就转变状态了(无语),然后也能看到集群信息
myrs:SECONDARY> rs.status()
"members" : [
{
"_id" : 0,
"name" : "192.168.0.187:25001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
...
"_id" : 1,
"name" : "192.168.0.4:25001",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 248,
使用命令将节点转为可读状态
myrs:SECONDARY> rs.secondaryOk()
myrs:SECONDARY> show dbs;
admin 0.000GB
config 0.000GB
local 0.000GB
test 0.000GB
myrs:SECONDARY> use test;
switched to db test
myrs:SECONDARY> show collections;
test
myrs:SECONDARY> db.test.find({})
{ "_id" : ObjectId("62c64602fa53ce157f16019a"), "item" : "card", "qty" : 15 }
然后在主节点继续加了一条数据,从节点可以读到
myrs:SECONDARY> db.test.find({})
{ "_id" : ObjectId("62c64602fa53ce157f16019a"), "item" : "card", "qty" : 15 }
{ "_id" : ObjectId("62c64671fa53ce157f16019b"), "item" : "card", "qty" : 16 }
后来又用pymongo往里写了两条
myrs:SECONDARY> db.test.find({})
{ "_id" : ObjectId("62c64602fa53ce157f16019a"), "item" : "card", "qty" : 15 }
{ "_id" : ObjectId("62c64671fa53ce157f16019b"), "item" : "card", "qty" : 16 }
{ "_id" : ObjectId("62c67e1d0b1aab0341399456"), "yoyo" : "lets go" }
{ "_id" : ObjectId("62c67e1d0b1aab0341399457"), "aa" : 123 }
后来我又加入了一个节点,并且断开,查询集群信息会显示该节点不可达。
3 python操作
一开始我是这么写的,这两条数据已经在上面看到了
from pymongo import MongoClient
c = MongoClient('mongodb://192.168.0.187:25001,192.168.0.4:25001')
c['test']['test'].insert_many([{'yoyo':'lets go'}, {'aa':123}])
后来我看了看,大概这么写会更好些
client = pymongo.MongoClient(['192.168.0.187:25001,192.168.0.4:25001'],replicaset='myrs')
这个client和单机的没啥差别(事实上也是访问一个库),只不过在发生故障时会自动切换。有很多信息可以看的:
client.is_primary
True
client.primary
('192.168.0.187', 25001)
client.secondaries
{('192.168.0.4', 25001)}
client.arbiters
set()
4 Next
4.1 MongoAgent
- 1 连接。因为MongoAgent的连接和之前不一样,所以要修改连接接口(add_a_connection)
- 2 用户信息认证。目前是无用户密码的,之后加上(这样和单机就保持一致了)。
- 3 集群信息的展示。
4.2 集群配置
这次不得不把线程管理设成了false,
processManagement:
fork: false
不然会报这样的错,需要再研究一下
about to fork child process, waiting until server is ready for connections.
forked process: 23
child process started successfully, parent exiting
可以给集群再加上通信秘钥,毕竟将来会有公网可达的副本。
# security:
# keyFile: /etc/mongo.key
# authorization: enabled