Mongoshake 是阿里云自研的开源工具,实现Mongodb 数据库之间的数据同步,数据灾备,数据多活,分库分表,版本升级无间断。
其中需要注意的一些事项
1 Mongoshake 尽量使用较高的版本,低版本的产品有一些Bug
2 Mongoshake 在使用中需要源库的用户权限为readAnyDatabase 同时Mongoshake 会开始自动在原实例中自建自己的数据库,来进行数据同步的事宜,目的库需要有 readWriteAnyDatabases 的权限
3 开源版本仅仅支持单向同步,并且在同步的全量过程中,严谨对数据库的进行任何DDL的操作,包含索引,加表,删除表,改名等操作。
下面把一些与mongoshake有关的参数进行一个学习和整理
这里有一个注意事项,就是mongo 的账号密码,不能带有 @符号,否则应用程序无法处理,密码中也不能带有 ,: / ?等符号
1 master_quorum 一般情况下这个参数为false 打开此参数的主要原因是,当你有两个mongoshake 都在抽取一个源端的情况下,就需要打开这个参数 master_quorum = true
2 log.level = info 这个部分默认可以不动,如果熟悉mongoshake 可以将这个问题之调整为 error log.level = error
3 log.dir = /mongoshake/log/ 存放mongoshake工作中日志的位置
4 log.file = collector.log 存放mongoshake的日志的名字
5 log.flush = false 这里建议一开始使用时设置为true ,在mongoshake 工作的情况下,可以看到最新的日志,当然这样操作可能有一些性能的影响。
6 sync_mode = all 这里在mongoshake 进行数据同步的情况下,一般情况下都是设置为all ,all 为全量 增量的模式,如果仅仅是全量同步可以设置为 full ,但如果仅仅是增量则设置为 incr
7 源端的读取地址,在配置这个部分的时候,需要注意
mongo_urls = 这里如果是复制集的形式,则需要注意你链接的地址是pimary 的还是集群的,是否带有 readPreference=secondary,这里链接的格式mongoshell 的链接格式是一致的逗号分割同一个副本集内的节点,分号分割分片的实例,
代码语言:javascript复制mongo_urls = mongodb://username:password@10.1.1.1:1001/admin?readPreference=secondary
8 Mongo_cs_url =
9 Mongo_s_url =
这两个部分是针对mongosharding 的部分,如果是mongo sharding 迁移,则需要填写这两个部分。一个是mongo sharding 的config server ,一个是mongos 的地址。
10 mongo_connect_mode 这个部分和上面的链接部分是兼容的,这里secondaryPreferred 是默认的一个设置,如果这里拉取的是分片集合,则这里建议为primary 避免孤儿文档。
11 tunnel = direct direct 标识目的端对接的mongodb是 rpc,file, kafka 的情况
其他与tunnel 部分有关的都与本次的迁移无关与kafka 有关,所以这里就不进行记录了
12 filter.namespace 这里需要注意的是,过滤是包含黑名单和白名单的,黑白名单中的指定的部分,不能有冲突,在白名单和黑名单都进行指定。
filter.namespace.black =
filter.namespace.white =
filter.pass.special.db = admin 这里主要指定的是一些特殊情况下,针对 admin system.views , mongoshake config 等数据库在默认不迁移的情况下,因为某些问题,需要进行数据迁移的情况
filter.ddl_enable = false 这个选项是在复制中不对DDL的操作进行复制,所以数据迁移中为避免一些问题,可以使用false 而数据同步的情况就需要考虑打开这个设置。
13 关于并发
full_sync.reader.collection_parallel =6 这个参数默认为 6 为一次最大并发拉取的表的数目,拉取一次过多,会导致IOPS 升高影响业务。
full_sync.reader.write_document_parallel = 8 ,对于一个表进行拉取的过程中,产生多少线程,来同时进行数据的拉取,这里注意要有主键。
full_sync.reader.document_batch_size 一次将写入多少文件进行聚合 128 表示一次写入 128个文档进行聚合 默认值为128
14 这两个参数是 2.6.4引入的参数,分别对于单 collation 拉取是并发的拉取数进行设置,这里默认为 1 对于分片的部分,目前不建议设置此参数,默认为1
full_sync.reader.parallel_thread
full_sync.reader.parallel_index
15 结构化参数
full_sync.executor.insert_on_dup_update = false
在目的库存在同样的数据库和collation 是否要进行insert to update的转换
full_sync.executor.filter.orphan_document = false
源端是shareding 的情况,是否要考虑过滤孤儿的文档,默认是不考虑。
这里是关于此次mongoshake与参数有关的部分。
关于在数据迁移中的孤儿文档的问题,孤儿文档的产生来自于mongoshake 全量的阶段而读取数据的方式是secondaryPreferred 的方式, 导致读取恶劣secondary节点的数据,而secondary 节点的数据和local 的方式不同,导致读取的数据产生问题的过程。这里为了防止问题,在读取的时候,如果MONGODB版本在3.6以下则读取的readPreferred 改为primary 对于 3.6 readconcern 改为 local,或者采用full_sync.executor.insert_on_dup_update = true 来进行过滤,不过此访问不建议。
另外还应该针对mongodb均衡器balancer 在对于分片到复制集的情况下,将其关闭,在MongoDB 5.0 之前的版本,当shard节点上的chunk 数量达到迁移阀值,banlancer对shared 节点上的chunk 进行迁移,会尽量保证shard节点的数量在各个节点是相同的。
在迁移前还要对mongodb的分片集合,做关闭balancer 的操作,通过mongos 进入到数据库中.
代码语言:javascript复制use config
while( sh.isBalancerRunning() ) {
print("waiting...");
sleep(1000);
}
sh.stopBalancer()
当然在使用完毕后可以在尽快的 打开balancer 功能
代码语言:javascript复制sh.setBalancerState(true)
启动mongoshake 有两种方法
1 通过start.sh 来启动mongoshake, 如sh start.sh collector.conf
2 通过 ./collector -conf=collector.conf -verbose
下面就将整体的工作情况进行一个流程化的记录
1 登录到分片的mongodb 中,确认当前的工作状态
这里可以看到,balancer 是开启的状态,当前并未运行
代码语言:javascript复制[mongos] admin> sh.status()
shardingVersion
{ _id: 1, clusterId: ObjectId('5e1bdb2dbd7ea0ac43') }
---
shards
[
{
_id: 'd-2ze2fde064',
host: 'mgset-21218075/7:3044',
state: 1
},
{
_id: 'd-2ze77e7e4e38ad64',
host: 'mgset-21218077/',
state: 1
}
]
---
active mongoses
[ { '4.2.1': 1 }, { '4.2.10': 1 } ]
---
autosplit
{ 'Currently enabled': 'yes' }
---
balancer
{
'Currently enabled': 'yes',
'Currently running': 'no',
'Failed balancer rounds in last 5 attempts': 0,
'Migration Results for the last 24 hours': { '11': 'Success' }
}
---
databases
[
{
database: { _id: 'config', primary: 'config', partitioned: true },
collections: {
'config.system.sessions': {
shardKey: { _id: 1 },
unique: false,
balancing: true,
chunkMetadata: [
{ shard: 'd-2ze2fde06cf92d74', nChunks: 512 },
{ shard: 'd-2ze77e7e4e38ad64', nChunks: 512 }
],
chunks: [
'too many chunks to print, use verbose if you want to force print'
],
tags: []
}
}
},
{
database: {
_id: 'report',
primary: 'd-2ze77e7e4e38ad64',
partitioned: true,
version: {
uuid: UUID('a3efc46e-b05f-4557-af1b-ddfeb212dd5e'),
lastMod: 1
}
},
collections: {
'report.request_record_new': {
shardKey: { requestId: 'hashed' },
unique: false,
balancing: true,
chunkMetadata: [
{ shard: 'd-2ze2fde06cf92d74', nChunks: 42853 },
{ shard: 'd-2ze77e7e4e38ad64', nChunks: 42852 }
],
chunks: [
'too many chunks to print, use verbose if you want to force print'
],
tags: []
}
}
}
]
代码语言:javascript复制[mongos] config> sh.stopBalancer()
{
ok: 1,
operationTime: Timestamp({ t: 1701137103, i: 45 }),
'$clusterTime': {
clusterTime: Timestamp({ t: 1701137103, i: 45 }),
signature: {
hash: Binary.createFromBase64('9RIHAXxDGxhwvcAVNQw5N0/3w9g=', 0),
keyId: Long('7253280536863440980')
这里我们关闭mongo sharding 的balancer ,限制当前balancer已经关闭。
代码语言:javascript复制active mongoses
[ { '4.2.1': 1 }, { '4.2.10': 1 } ]
---
autosplit
{ 'Currently enabled': 'no' }
---
balancer
{
'Currently running': 'no',
'Currently enabled': 'no',
然后我们配置mongoshake 的配置文件,这里需要注意,由于是分片,则需要配置三个地址,config ,mongos ,shardings
代码语言:javascript复制vi collector.conf
conf.version = 10
id = mongoshake
master_quorum = false
full_sync.http_port = 9190
incr_sync.http_port = 9190
system_profile_port = 820
log.level = info
log.dir = /mongoshake
log.file = collector.log
log.flush = false
sync_mode = all
mongo_urls = mongodb://mongosd:g_cs_1127@d-2183.mongodb.com:3797;mongodb://mongos:fe1127@d64952.mongodb.com:3797
mongo_cs_url = mongodb://mongocs:feg_cs_1127@ddee63.mongodb.rdscom:3719/admin
mongo_s_url = mongodb://ot9:01#18a4.mong.com:3719/admin
tunnel = direct
tunnel.address = mongodb://otg:B3X@dds-2ze220fb41051a641.moncom:377,1a642.mongodbs.com:377/admin?replicaSet=mset-73
tunnel.message = raw
tunnel.kafka.partition_number = 1
mongo_connect_mode = primary
这里有几个地方需要注意 1 在进行collectiion.conf 文件的编写中,分片作为源的情况下,需要注意需要填写三个地址 1.1 普通连接分片的地址 1.2 config server 的primary的地址 1.3 mongos 的地址,并且注意urls 中的地址的写法必须是每个sharding的primary的主地址,并且以 ;分号进行分割,有几个sharding 就的写几个,差一个,就会丢数据。 剩下的就是启动mongoshake 来同步数据,同步的速度比较快,尤其在开启多线程的基础上,默认是 8个,如果你的系统强悍,完全可以到16 24 只要IOPS 和CPU 可以支持的情况下。 2 在执行目录执行 collector.linux -conf=collector.conf 在进行工作中遇到的一些问题,与相关的解答,当遇到oplog 无法进行初始化的错误时,同时你又是分片的情况下,请参看下面的回答。
Q: How to solve the "Oplog Tailer initialize failed: no oplog ns in mongo" error?
A: This is usually a problem with insufficient account permissions, so, please check your permission of oplog table. If the source is sharding, the account should be added into each shard because there is no local
database in mongos
. When source is sharding, the mongo_urls
should be the shards address split by semicolon(;) like: mongo_urls: mongodb://user1:passwd1@10.1.1.1:20011,10.1.1.2:20112;mongodb://user2:passwd2@10.1.2.1:20011,10.1.2.2:20112
. Since v2.0.6, MongoShake doesn't throw this error when sync mode is full sync(sync_mode = document
).
在实际的工作中还有其他的问题,有时间可以继续说,整体传输的速度比较快,基本在较低的配置下 4C 8G ,每秒的传输效率在 0.1G左右。