Mongodb快速搭建副本集群#
https://www.mongodb.com/docs/manual/replication/
多看文档。基本概念不再一一叙述。
启动副本集#
三个节点
创建目录。docker-compose起服务
services:
# mongo_2和mongo_3类似。放在别的目录
mongo_1:
hostname: mongo_1
image: mongo:latest
deploy:
resources:
limits:
memory: 1G
networks:
- my_app
ports:
- "27017:27017" # mongo_1
#- "27018:27017" mongo_2
#- "27019:27017" mongo_3
volumes:
- "/xxx/xxx/test/mongodb/:/xxx/xxx/test/mongodb/"
- "/xxx/xxx/test/mongodb/mongodb_1/data:/data/db/"
environment: # 环境变量
TZ: Asia/Shanghai
MONGO_INITDB_ROOT_USERNAME: toor
MONGO_INITDB_ROOT_PASSWORD: 123456
# 需要生成一个key_file。随便填一些数据。
# 需要chown 999:999和chmod 600
# replSet指定要起副本集
# 副本集的名字my_repl_set
command: mongod --bind_ip_all --replSet my_repl_set --keyFile /xxx/xxx/test/mongodb/key_file.txt
基本测试#
# 进入容器
sudo docker-compose exec mongo_1 bash
# 用mongosh或者gui工具连上mongodb
# 官方的Compass挺好的
# 常用命令
use admin
db.auth('toor', '123456')
db.hostInfo()
# 集群状态
rs.status()
# 初始化副本集
rs.initiate( {
_id : "my_repl_set",
members: [
{ _id: 0, host: "x.x.x.x:27017" },
{ _id: 1, host: "x.x.x.x:27018" },
{ _id: 2, host: "x.x.x.x:27019" }
]
})
# 此时查rs.status()可以看到一堆信息,包含每个节点的状态等。
# 只有主可写。写入后从节点立即可见。
# 默认可能也只有主能写。有各种设置。
# 比如db.getMongo().setReadPref('secondaryPreferred')可让直连到从节点的client可读。Compass的连接有选项设定。
简单大剂量测试#
用pymongo/motor操作数据库。
MONGODB_AUTH = 'mongodb://toor:123456@x.x.x.x:27017,x.x.x.x:27018,x.x.x.x:27019/?replicaSet=my_repl_set'
mongodb_client = motor.motor_asyncio.AsyncIOMotorClient(MONGODB_AUTH, readPreference = 'secondaryPreferred')
# 不设置secondaryPreferred的话默认只能在主上读
写程序大量并发读/写/插入数据。每秒上千的读写。
然后对三个节点进行各种折腾,看应用端状态。
用Compass看每个节点的实时请求数。
集群默认的Write Concern(w)参数是majority。即写操作会达成大多数一致性。
Read Concern和Write Concern需要特别关注,概念和Mysql Group Replication
大同小异。进行一致性的设置。
关掉一个节点。过会再加入。
可看到这个节点追数据非常快。
最后看count。
查数据数量一定用countDocuments。用count可能不准,非常容易坑人,会以为数据不对。
db.getCollection("xxx").countDocuments()
关掉主时发生重新选主。
期间可出现各种相关错误,属于正常情况。
pymongo.errors.WriteConcernError
pymongo.errors.ServerSelectionTimeoutError: No primary available for writes
pymongo.errors.NotPrimaryError: Not primary so we cannot begin or continue a transaction
重新选出主后自动恢复。选主速度较快。
只剩一个节点时。无法写。可读。
加入一个节点后可自动恢复。
3节点运行过程中,可用rs.add(“xxx.xxx.xxx.xxx:port”)新加一个节点。
只能在可写节点操作。
如果新节点之前有数据,会完全删掉,完全同步成群组的数据。
mongodb同步数据非常快,应该是得益于nosql的数据结构。
测试程序读写期间,对数据库进行各种折腾,不再详写。
最后达到几百万条数据,所有节点数据量仍然一致。
期间Compass的实时统计曲线也符合实际操作,很满意。
pymongo相当于自带一个简单中间件,可实现failover自动切换入口。
不用像mysql那样,一般需要另起一个独立的proxysql之类。
非常好,可满足一般需要。
但是读的一致性还有很多相关问题。
https://pymongo.readthedocs.io/en/stable/examples/high_availability.html
https://www.mongodb.com/docs/manual/tutorial/force-member-to-be-primary/
https://www.mongodb.com/docs/manual/tutorial/configure-secondary-only-replica-set-member/
可设置优先级,让某节点优先成为主节点,让某节点禁止成为主节点。
只能在可写节点操作。
cfg = rs.conf()
cfg.members[0].priority = 10
cfg.members[1].priority = 0 # 设置为0禁止成为主节点
rs.reconfig(cfg)
把当前某个从节点提高优先级,稍后可看到它变成了主节点。
Replication Lag and Flow Control
db.adminCommand({ “getDefaultRWConcern”: 1 })
主上的写操作默认需要达成大多数一致。
Troubleshoot Replica Sets#
查看副本集的lag#
rs.printSecondaryReplicationInfo()
Write Concern相关#
https://www.mongodb.com/docs/manual/reference/write-concern/
Write Concern
决定了写操作的表现。
mysql group replication
也有相似的概念。
在集群环境中很重要。
如果允许在从节点上读,有可能读到老数据,有可能读到之后被rollback的数据。
# 查看默认设置
db.adminCommand({
"getDefaultRWConcern": 1
})
defaultReadConcern: { level: 'local' },
defaultWriteConcern: { w: 'majority', wtimeout: 0 },
defaultWriteConcernSource: 'implicit',
defaultReadConcernSource: 'implicit',
w
参数可以是数字x,规定写操作必须等待在x个节点上生效后才返回。
w
可以是默认值”majority”。规定写操作必须等待在多数
节点上生效后才返回。
多数节点数到底是多少。见rs.status()
返回的writeMajorityCount
。默认会根据节点数改变。
j
参数。决定如果日志打开,w
为”majority”时,是否要等写操作再多数节点上写入log再返回。
rs.conf()
的writeConcernMajorityJournalDefault
默认为true。
默认要等。
一致性问题#
默认的majority是保证写到大多数,因为是大多数
,所以是有可能写返回之后在一个少数
节点读到老数据的。
那么如果要确保读不到老数据,要把此次操作的w参数设置为总节点数,确保写入所有节点才返回。
但有个问题是此时如果有节点挂了,这个数量就对不上了。可以把重要的读强制放到主上来解决?
或者用下面的linearizable读,本质就是读主节点,一定不会读到老数据。
相比mgr稍有不同。mgr如果设置了after,保证所有
节点都commit。无论如何不会读到老数据。
Read Preference#
https://www.mongodb.com/docs/manual/core/read-preference/
副本集中的读操作,如果不是强制读主节点,一定会有几率读到老数据。
Read Concern相关#
https://www.mongodb.com/docs/manual/reference/read-concern/
Read Concern
控制一致性和隔离属性,决定读操作的表现。
结合Write Concern
,可以在各种场景按需组合。
上面的getDefaultRWConcern
命令可以看到defaultReadConcern
的level默认值是local。
local
返回的数据不保证会在集群中达成一致(可能rollback)。
见local文档
观察基本流程,主直接下发写操作,从1收到后立刻能读到这个数据。
此时还没走完一致性流程,如果此时两个从突然挂了。那么这次写操作就挂了。但从1却读出了挂的数据。available
如果没有用sharding。和local完全一样。
暂不考虑sharding。majority
如果不是multi-document
事务,保证读到的数据已经走完一致性流程,不可能rollback。
见majority文档
从节点要在t5收到主的一致性ack后才能读到新写的值。
如果是multi-document
事务,只保证write concern设置为majority的事务。linearizable
一定会读到majority写成功的数据。也就是一定会读到新数据。snapshot