MongoDB分片配置

MongoDB分片配置涉及mongos路由进程、config server路由表服务和数据存储分片。选择高cardinality的shard key以分散数据,避免查询瓶颈。考虑使用hash key增加随机性,但可能影响query isolation。配置过程包括关闭config server、备份数据、重启config server等步骤。
简单注解:
  1. mongos 路由进程, 应用程序接入mongos再查询到具体分片,监听端口默认27017
  2. config server 路由表服务, 每一台都具有全部chunk的路由信息 
  3. shard为数据存储分片, 每一片都可以是复制集(replica set)


部署分片集群

#配置mongoDB shareding时,使用hosts域名代替IP地址好处非常多,可以在很多迁移的地方,直接更换迁移服务器,IP地址变了也没关系
#例如在迁移config服务器时,只需要在mongos服务器里面把配置的config服务器的hosts绑定修改一下即可

step 1 启动config server
/usr/bin/mongod --configsvr --dbpath /data/mongodb/config/  --logpath/data/mongodb/config/log/configdb.log --port 20000
#正式生产环境一般启动3个config server,启动3个是为了做热备,三个config server中有一个失败则三个config server集群变成只读群集

step 2 启动mongos
/usr/bin/mongos --configdb 192.168.10.1:20000,192.168.10.2:20000,192.168.10.3:20000 --logpath/data/mongodb/mongos/mongos.log
#把所有要加入到分片的mongo配置文件都加入进来
step3 启动分片mongod
/usr/bin/mongod --shardsvr --replSet rs1 --dbpath /data/mongodb/data/ --logpath /tmp/sharda.log  --port 10000
/usr/bin/mongod --shardsvr --replSet rs2 --dbpath /data/mongodb/data/ --logpath /tmp/sharda.log  --port 10000
/usr/bin/mongod --shardsvr --replSet rs3 --dbpath /data/mongodb/data/ --logpath /tmp/sharda.log  --port 10000  –directoryperdb
#分片是一个mongo副本集,--replSet rs1是副本集的名称,生产环境即使分片是单台服务器,也建议这么设置,方便以后扩展
#上面是在3台不同的服务器上启动的三个副本集,名称分别上rs1,rs2,rs3
#可以启用这个命令, –directoryperdb

/usr/bin/mongod --shardsvr --dbpath /data/mongodb/data/ --logpath /tmp/sharda.log  --port 10000
#分片是一个普通的mongo服务器
 

step4 在mongos添加分片
mongo 127.0.0.1/admin
#用mongo 连接上mongos,切换到admin库
sh.addShard("rs1/192.168.10.1:20000") 
sh.addShard("rs2/192.168.10.2:20000") 
sh.addShard("rs3/192.168.10.3:20000") 
sh.addShard("rs3/192.168.10.3:20000","allowLocal :1") 
sh.addShard("shared4/10.26.79.89:27017,10.26.165.157:27017,10.26.165.112:27017") 
db.runCommand( { addshard : "127.0.0.1:27020", allowLocal :1} )
#添加一个 replica set副本集作为一个分片
#上面是添加了三个副本集,每个副本集目前只有一个成员,做了三个分片
#实际上在分片情况下在一个副本集中可以只有一个或者两个成员,不一定至少三个
#添加本地的分片时可能需要参数“allowLocal :1”
sh.addShard("192.168.10.1:20000")
#添加非replica set作为分片: 


step5 对某个数据库启用分片
sh.enableSharding("test")
#这里只是标识这个test数据库可以启用分片,但实际上还并没有进行分片。
 
step6 对collection进行分片
sh.shardCollection("records.people", { "zipcode": 1, "name": 1 } )
sh.shardCollection("people.addresses", { "state": 1, "_id": 1 } )
sh.shardCollection("assets.chairs", { "type": 1, "_id": 1 } ) 
#对某个库的某个表进行分片,分片的key可以使用单个字段,也可以使用多个字段
#这里分片用的key,就自动变成该表的索引字段
#至于具体哪一行分到哪一片了,只有config配置文件知道
#不像atlas可以明确的知道那行在哪个表里面
#对一个已经有数据的表进行分片时需要先建立索引,再把索引字段当作分片key来进行分片
db.alerts.ensureIndex( { _id : "hashed"} )
sh.shardCollection("events.alerts", { "_id": "hashed"} )
db.t3.ensureIndex({age:1} , {backgroud:true})
#对alerts表进行建立索引操作,增加一个_id索引字段,并对该字段生成随机hash值,这样就可以根据这个hash sharded key进行分片了
#hash sharded key是为了解决某些情况下sharded key的 write scaling的问题。
#_id字段是mongodb默认每行都有的

分片的正确姿势
sh.enableSharding("mexueGrowth")
#先对数据库启用分片
db.growth_user_record.ensureIndex({ "userId" : 1, "recordId" : 1 }, { name : "_idx_userId_recordId" }, { "background" : true })
#对需要分片的空的collection建立分片索引
sh.shardCollection("mexueGrowth.growth_user_record", { "userId" : 1, "recordId" : 1 } )
#在空的collection上启用分片
#最后将数据导入
#这样就可以保证分片一定成功,不论数据量有多大

如何选择shard key

  • shard key需要有高的cardinality,也就是shard key需要拥有很多不同的值, 便于数据的切分和迁移。
  • 尽量与应用程序融合,让mongos面对查询时可以直接定位到某个shard
  • 具有随机性,这是为了不会让某段时间内的insert请求全部集中到某个单独的分片上,造成单片的写速度成为整个集群的瓶颈
  • 用objectId作为shard key时会发生随机性差情况,ObjectId实际上由进程ID+TIMESTAMP + 其他因素组成, 所以一段时间内的timestamp会相对集中
  • 不过随机性高会有一个副作用,就是query isolation性比较差
  • 可用hash key增加随机性


如何查看shard信息
登上mongos
sh.status()
sh.status({verbose:true})
#需要看的详细一点
Sharding Status ---
sharding version: { "_id": 1, "version": 3 }
shards:
{  "_id": "shard0000",  "host": " m0.example.net:30001"}
{  "_id": "shard0001",  "host": " m3.example2.net:50000"}
databases:
{  "_id": "admin",  "partitioned": false,  "primary": "config"}
{  "_id": "contacts",  "partitioned": true,  "primary": "shard0000"}
foo.contacts
shard key: { "zip": 1 }
chunks:
shard0001    2
shard0002    3
shard0000    2
{ "zip": { "$minKey": 1 } } -->> { "zip": 56000 } on : shard0001 { "t": 2, "i": 0 }
{ "zip": 56000 } -->> { "zip": 56800 } on : shard0002 { "t": 3, "i": 4 }
{ "zip": 56800 } -->> { "zip": 57088 } on : shard0002 { "t": 4, "i": 2 }
{ "zip": 57088 } -->> { "zip": 57500 } on : shard0002 { "t": 4, "i": 3 }
{ "zip": 57500 } -->> { "zip": 58140 } on : shard0001 { "t": 4, "i": 0 }
{ "zip": 58140 } -->> { "zip": 59000 } on : shard0000 { "t": 4, "i": 1 }
{ "zip": 59000 } -->> { "zip": { "$maxKey": 1 } } on : shard0000 { "t": 3, "i": 3 }
{  "_id": "test",  "partitioned": false,  "primary": "shard0000"}

 
 
 

备份cluster meta information
#mongoDB的Balance是指分片数据时存储在config数据库里的数据同步
#就是每个config server之间的数据同步
Step1 disable balance process. 
    sh.getBalancerState()
    #查看分片的Balancer复制状态
    sh.setBalancerState(false)
    #停止分片的Balancer复制

Step2 关闭config server 

Step3 备份数据文件夹 

Step4 重启config server 

Step5 enable balance process.
    sh.setBalancerState(true)
    #启动分片的Balancer复制


查看balance 状态

可以通过下面的命令来查看当前的balance进程状态。先连接到任意一台mongos
use config
db.locks.find( { _id : "balancer" } ).pretty()
{   "_id" : "balancer",
"process" : " mongos0.example.net:1292810611:1804289383",
  "state" : 2,
     "ts" : ObjectId("4d0f872630c42d1978be8a2e"),
   "when" : "Mon Dec 20 2010 11:41:10 GMT-0500 (EST)",
    "who" : " mongos0.example.net:1292810611:1804289383:Balancer:846930886",
    "why" : "doing balance round" }
 
state=2 表示正在进行balance, 在2.0版本之前这个值是1
配置balance时间窗口

可以通过balance时间窗口指定在一天之内的某段时间之内可以进行balance, 其他时间不得进行balance。
先连接到任意一台mongos
use config
db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "23:00", stop : "6:00" } } }, true )
这个设置让只有从23:00到6:00之间可以进行balance

也可以取消时间窗口设置:
use config
db.settings.update({ _id : "balancer" }, { $unset : { activeWindow : true } })

修改chunk size

这是一个全局的参数。 默认是64MB。
小的chunk会让不同的shard数据量更均衡。 但会导致更多的Migration。
大的chunk会减少migration。不同的shard数据量不均衡。
这样修改chunk size。先连接上任意mongos
db.settings.save( { _id:"chunksize", value: "256" } )
单位是MB

何时会自动balance

每个mongos进程都可能发动balance。
一次只会有一个balance跑。 这是因为需要竞争这个锁:
db.locks.find( { _id : "balancer" } )
balance一次只会迁移一个chunk。
 

设置分片上最大的存储容量

有两种方式,第一种在添加分片时候用maxSize参数指定:
db.runCommand( { addshard : " example.net:34008", maxSize : 125 } )

第二种方式可以在运行中修改设定:
use config
db.shards.update( { _id : "shard0000" }, { $set : { maxSize : 250 } } )

删除分片

连接上任意一台mongos
STEP1 确认balancer已经打开

STEP2 运行命令:
db.runCommand( { removeShard: "mongodb0" } )
#mongodb0是需要删除的分片的名字。这时balancer进程会开始把要删除掉的分片上的数据往别的分片上迁移。
 
STEP3 查看是否删除完
还是运行上面那条removeShard命令
db.runCommand( { removeShard: "mongodb0" } ) 

如果还未删除完数据则返回:
{ msg: "draining ongoing" , state: "ongoing" , remaining: { chunks: NumberLong(42), dbs : NumberLong(1) }, ok: 1 }

STEP4 删除unsharded data
有一些分片上保存上一些unsharded data, 需要迁移到其他分片上:
 
可以用sh.status()查看分片上是否有unsharded data。
如果有则显示:
{ "_id" : "products", "partitioned" : true, "primary" : "mongodb0" }

用下面的命令迁移:
db.runCommand( { movePrimary: "products", to: "mongodb1" })
只有全部迁移完上面的命令才会返回:
{ "primary" : "mongodb1", "ok" : 1 }
#实际上上面命令是重新设置某张表的初始primary

STEP5 最后运行命令
db.runCommand( { removeShard: "mongodb0" } )

##在迁移完一个分片后,在mongs里面查看movePrimary的show collections/show tables时会报错,此时需要重新启动一下mongos服务器



删除在sharding中删除复制集replica set的成员

假设sharding的分片是复制集,需要删除某个复制集的某个成员。
只要在复制集的设置中删除该成员即可,不需要在mongos中删除。mongos会自动同步这个配置。
例如 sharding cluster中有这个分片:
 
{  "_id" : "rs3",  "host" : "rs3/192.168.1.5:30003,192.168.1.6:30003" }
 
需要删除192.168.1.6:30003这个成员。
只需要:
step 1: 在192.168.1.6:30003上运行db.shutdownServer()关闭mongod
step 2:在rs3的primary的成员192.168.1.5:30003上执行
rs.remove("192.168.1.6:30003")

常见问题:

新添加的分片始终不进行数据同步的问题
 
1 如果sharding cluster中新添加的分片始终不进行数据migration, 并出现类似日志:
 
migrate commit waiting for 2 slaves for
 
则需要重启该分片的mongod进程。
 
特别需要注意的是,如果某mongod进程是一个replica set的primary, 并且该replica set上只有一个mongod, 那么不能用db.shutdownServer()的方法关闭。 会报下面的错误:
 
no secondary is within 10 seconds of the primary,
 
需要用下面的命令关闭:
 
db.adminCommand({shutdown : 1, force : true})


一个新的分片始终不进行更新的问题
 
日志里出现这样的错误:
 
secondaryThrottle on, but doc insert timed out after 60 seconds, continuing
 
通过1 将所有分片的secondary和arbitary删除掉,2 重启同步的分片解决。
 

mongod的日志出现

moveChunk cannot start migration with zero version
 
解决方法,在mongos上运行
mongos> use admin
switched to db admin
mongos> db.runCommand("flushRouterConfig");
{ "flushed" : true, "ok" : 1 }

PS:额外补充

MongoDB持续灌入大数据遇到的一些问题
首先这类NoSQL数据库的设计宗旨基本是“读多写少”。
而我们的需求是:有一大批数据(5亿条记录,每条100B左右,按此计算,一亿行数据大概10G空间,5亿条记录有50G,有命令查看平均一条数据大小的,这里说的不准)。需要灌入MongoDB。
考虑到数据规模,事先已经做好了Sharding,10台机器。
1、并发12进程,持续灌入数据。
一开始速度很快,基本10W/s,到了1亿6千万左右,开始变成龟速。经过仔细检查,mongos中报大量如下错误:
Wed Jun  6 16:56:41 [conn33] warning: splitChunk failed - cmd: { splitChunk: "trec.doc", keyPattern: { id: 1.0 }, min: { id: MinKey }, max: { id: "00029610933" }, from: "172.22.0.16:27018", splitKeys: [ { id: "00000000000" } ], shardId: "trec.doc-id_MinKey", configdb: "172.22.0.11:27019" } result: { who: { _id: "trec.doc", process: "node5:27018:1338970017:616299555", state: 2, ts: ObjectId('4fcf1a425ac42b3930c16116'), when: new Date(1338972738056), who: "node5:27018:1338970017:616299555:conn14:1486636348", why: "migrate-{ id: "01032438609" }" }, errmsg: "the collection's metadata lock is taken", ok: 0.0 }
其实很很容易理解,灌入速度太暴力,导致很多后台进程如分片、迁移Shard的任务一直拿不到锁。
 
最后导致Shard1上面有130个Chunk,其他上面就是3~5个。 ###不论是否先启用分片,插入数据是都会先插入到primary shareding中
杀掉进程后,Mongo开始逐渐均衡,但考虑到时间会很长,遂删除数据。
 
2、灌数据建索引。
 
我们对灌入的数据需要建一些索引,之前没有经验,在配置好Shard后就ensureIndex了,导致到达1亿条后速度明显变慢。
其实这种先写后读的需求,完全可以等全部插入后再建索引,防止大量的锁竞争。
 
3、关于数据分块大小
 
默认的64MB,对于千万级别是可以的。像我们这种,就要适当调大,不然反复的split会导致性能下降。如果再加上持续插入数据,很可能导致一直无法split,当分片过大后就无法再进行迁移了。
 
4、关于自动Balance及我们的策略
 
经过仔细阅读文档,我发现MongoDB的Balance做的非常好,是后台自动完成的。但有一个缺陷:就是前面提到的,前台数据插入优先级更高,Balance只能在空闲时候做。
因此,我把5亿条数据分为10部分,每部分插入完成后,等待Balance平衡后,再插入下一部分。一般来说平衡的过程是非常快的,等一会再插入,能保证持续不断的性能。
关于查看各个Shard上的Chunk数量:
db.printShardingStatus()
此外,还修改了Chunk Size为256MB




常用查看命令:

db.printShardingStatus()
#查看整个mongdb服务器所有数据库是否启用分片的信息
use db
db.collection.stats()
#查看某个集合的状态,包括是否启用了分片,需要先切换到该DB下面










<think>我们正在解决MongoDB分片集群中添加分片时出现的`SyntaxError:""literalnotterminatedbeforeendofscript`错误。这个错误通常是由于字符串字面量未正确终止引起的,可能是在执行`sh.addShard()`命令时,参数中的字符串引号不匹配或未正确闭合。根据引用[1]中提到的类似问题(在创建集合时因配置对象中的字符串问题导致语法错误),我们可以推测在`sh.addShard()`命令中可能存在类似问题。例如,在指定分片副本集名称和成员列表时,字符串的引号可能没有正确闭合。同时,引用[3]提到了在添加分片时遇到的另一个错误,其中涉及到了副本集名称和成员列表的配置问题,这提示我们在添加分片时需要确保副本集名称和成员列表的字符串格式正确,并且与目标副本集的配置一致。具体解决步骤:1.**检查命令语法**:确保在`sh.addShard()`命令中,所有字符串参数都使用匹配的引号(单引号或双引号),并且没有遗漏。例如:```javascript//正确的格式sh.addShard("replicaSetName/host1:port1,host2:port2,...")```注意整个字符串用双引号括起来,内部如果有嵌套引号,要确保正确转义或使用不同的引号(如内部用单引号)。2.**避免在字符串中包含特殊字符**:如果分片副本集名称或主机名中包含特殊字符(如空格、引号等),需要确保正确转义。3.**使用正确的分隔符**:分片成员列表使用逗号分隔,且整个分片字符串的格式为:`<replicaSetName>/<host1:port1>,<host2:port2>,...`4.**在MongoShell中执行命令**:确保命令是在MongoShell中执行,而不是在JavaScript文件中。如果是在JavaScript文件中,可能需要检查整个脚本的语法,包括该命令之前的代码是否有错误(比如前面的字符串未闭合)。5.**检查副本集名称和成员列表**:确认副本集名称与目标副本集配置的名称完全一致,成员列表的地址和端口正确。示例错误命令:```javascript//错误的命令:字符串内部有未转义的双引号或单引号sh.addShard("rs0/host1:27017,host2:27017")//如果副本集名称或主机名中有特殊字符,比如包含空格,则可能引起问题sh.addShard("myreplicaset/host1:27017")//错误,因为副本集名称有空格,但整个字符串没有正确处理```正确做法:对于包含空格的副本集名称,应该确保整个字符串的引号匹配,并且副本集名称中的空格不需要特殊处理(只要整个字符串用引号括起来即可):```javascriptsh.addShard("myreplicaset/host1:27017,host2:27017")//正确```但是,为了避免麻烦,通常建议副本集名称不要包含空格等特殊字符。如果已经存在,则按上述方式正确书写。6.**检查整个脚本**:如果是在一个脚本文件中执行多条命令,检查该错误命令之前的代码是否有语法错误(比如字符串未闭合),导致该命令被错误解析。7.**使用JSON格式验证**:如果命令参数比较复杂,可以先构造一个JSON对象,然后将其作为参数传递给`sh.addShard()`。注意,`sh.addShard()`的参数是一个字符串,所以不能直接传递对象。但我们可以通过字符串拼接来构造。8.**使用MongoDB的连接字符串标准格式**:另一种方式是使用连接字符串格式,确保符合URI标准。如果上述检查都通过,仍然出现问题,可以尝试将命令拆分成最简单的形式,逐步添加参数,以定位问题。举例:先尝试添加一个只有一个成员的分片:```javascriptsh.addShard("rs0/host1:27017")```如果成功,再尝试添加多个成员。如果问题仍然存在,请提供具体的命令和上下文环境,以便进一步分析。根据引用[2]中的配置问题,我们也要注意环境配置的影响,但本错误主要是语法错误,所以重点检查命令字符串。下面给出一个具体示例:错误示例:```javascriptsh.addShard("rs0/host1:27017,host2:27017,host3:27017)```这里字符串结尾缺少一个双引号,导致字符串未终止。正确示例:```javascriptsh.addShard("rs0/host1:27017,host2:27017,host3:27017")```因此,请仔细检查命令中的引号是否成对出现。如果是在一个JavaScript文件中执行,也要检查整个文件是否有语法错误,比如前面的语句缺少括号、引号等,导致后面的命令被错误解析。总结:该错误是由于字符串字面量未正确终止,通常是因为引号不匹配。请检查命令中的引号使用,确保每个打开的引号都有对应的闭合引号,并注意特殊字符的转义。</think>### MongoDB 添加分片时出现 `SyntaxError: "" literal not terminated before end of script` 错误的原因与解决方案 #### 错误原因分析 该错误通常发生在使用 `sh.addShard()` 命令时,**JSON格式字符串未正确终止**。主要原因是: 1. **引号不匹配**:命令中的字符串缺少闭合引号 2. **特殊字符未转义**:主机名/IP地址中的特殊字符(如冒号、点号)未正确处理 3. **副本集名称格式错误**:副本集名称包含非法字符或未正确引用 4. **脚本执行环境问题**:在mongo shell外执行命令时格式错误(如忘记添加引号)[^1] #### 解决方案步骤 ##### 1. 检查引号匹配性(最常见原因) ```javascript // 错误示例:缺少闭合引号 sh.addShard("rs-shard-01/172.28.22.166:37017) // 正确示例:确保引号成对出现 sh.addShard("rs-shard-01/172.28.22.166:37017") ``` ##### 2. 处理特殊字符(主机名/IP地址) ```javascript // 当主机名包含特殊字符时使用单引号包裹 sh.addShard('rs-shard-01/host-name-with-dash:37017') // 或使用双引号并转义特殊字符 sh.addShard("rs-shard-01/192\.168\.0\.1:37017") ``` ##### 3. 验证副本集名称格式 ```javascript // 副本集名称需满足: // - 仅包含字母、数字、下划线、连字符 // - 不以数字开头 // - 长度≤64字符 // 错误示例:包含空格 sh.addShard("my set/172.28.22.166:37017") // 正确示例:使用合法名称 sh.addShard("my-set-01/172.28.22.166:37017") ``` ##### 4. 在mongo shell中直接执行(推荐) ```bash mongo --host config-server:27019 > sh.addShard("rs-shard-01/172.28.22.166:37017") ``` ##### 5. 脚本执行时的正确格式 ```javascript // 在JavaScript文件中执行时 var shard = "rs-shard-01/172.28.22.166:37017"; sh.addShard(shard); ``` #### 验证成功 ```javascript // 检查分片状态 sh.status() // 预期输出包含: shards: { "_id" : "rs-shard-01", "host" : "rs-shard-01/172.28.22.166:37017", ... } ``` #### 预防措施 1. **使用连接字符串验证工具**: ```bash mongosh "mongodb://172.28.22.166:37017/?directConnection=true" ``` 2. **配置分片时遵循标准格式**: ``` <replica-set-name>/<host1>:<port>,<host2>:<port>,... ``` 3. **在配置服务器上预先验证副本集名称**: ```javascript rs.conf()._id // 返回当前副本集名称 ``` > **关键提示**:该错误与引用[3]中提到的副本集配置不兼容错误不同,后者需要确保目标节点的副本集名称与集群配置一致[^3]。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值