标签:MongoDB
MongoDB Sharding Clustersharding是MongoDB用来将大型集合分割高不同服务器上所采用的方法,尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事
和mysql分区方案相比,MongoDB的最大区别在于它几乎能自动完成所有事情,只要告诉MongoDB要分配数据,它就能自动维护数据在不同的服务器之间的均衡
? 垂直扩展:增加更多的cpu和存储资源来扩展容量,也属于硬件扩展
? 水平扩展:将数据集分布在多个服务器上,水平扩展即分片
分片为应对高吞吐量与大数据量提供了方法。使用分片减少了每个分片需要处理的请求数,因此,通过水平扩展,集群可以提高自己的存储容量和吞吐量。举例来说,当插入一条数据时,应用只需要访问存储这条数据的分片.
使用分片减少了每个分片存储的数据。
例如,如果数据库1tb的数据集,并有4个分片,然后每个分片可能仅持有256 GB的数据。如果有40个分片,那么每个切分可能只有25GB的数据。
1. 对集群进行抽象,让集群不可见
a) MongoDB自带了一个叫做mongos的专有路由进程,mongos就是掌握统一路口的路由器,它会将客户端发来的请求准确无误的路由到集群中的一个或者一组服务器上,同时会把接受到的响应拼装起来发挥到客户端
2. 保证集群总是可读写
a) MongoDB通过多种途径来确保集群的可用性和可靠性,将MongoDB的分片和复制功能结合使用,在确保数据分片到多台服务器的同时也确保了每分钟数据都有响应的备份,这样就可以确保有服务器换掉时,其他的从库可以立即替换宕机的节点继续工作
3. 使集群易于扩展,当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量
分片集群架构:
组件 | 说明 |
Config Server | 存储集群所有节点、分片数据路由信息。默认需要配置3个Config Server节点。 |
Mongos | 提供对外应用访问,所有操作均通过mongos执行。一般有多个mongos节点。数据迁移和数据自动平衡。 |
Mongod | 存储应用数据记录。一般有多个Mongod节点,达到数据分片目的。 |
1. mongos : 数据路由,和客户端打交道的模块,mongos自身没有任何数据,也不知道该如何处理数据,这些都需要config-server来告诉他
2. config-server : 所有存,取数据的方式,所有shard节点的信息,分片功能的一些配置信息,可以理解为真实数据的元数据
3. shard : 真正的数据存储位置,以chunk为单位存储数据,超过设定的默认大小就会继续向下分裂成多个chunk
mongos本身不持久化数据,sharded cluster所有的元数据都会存储到config-server,而用户的数据会分散存储到各个shard分片中,mongos启动后,会从配置服务器加载元数据,开始提供服务,将用户的请求正确路由到对应的碎片
1. 当数据写入时,MongoDB Cluster根据分片键设计写入数据
2. 当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据
在一个shard-server内部,MongoDB还是会把数据分为chunks,每个chunk代表这个shard-server内部一分部数据,chunk的产生,会有以下两个用途:
? Splitting :
当一个chunk的大小超过配置中的chunk size时,MongoDB的后台进程会把这个chunk切分成更小的chunk,从而避免chunk过大的情况
? Balancing :
当MongoDB中,balance是一个后台进程,负责chunk的迁移,从而均衡各个shard-server负载,系统初识1个chunk,chunk size默认值是64M,生产库上选择适合业务的chunk size是最好的,MongoDB会自动拆分和迁移chunks
1. 使用chunk来存储数据
2. 进程搭建完成之后,默认开启一个chunk,大小是64M
3. 存储需求超过64M,chunk会进行分列,如果单位时间存储需求很大,设置更大的chunk
4. chunk会自动均衡迁移
适合业务的chunk size是最好的
chunk的分裂和迁移非常消耗IO资源,chunk分裂的时机:在插入和更新,读数据不会分裂
? 小的chunk size : 数据均衡时迁移快,数据分布更加均匀,数据分裂频繁,路由节点消耗更多资源
? 大的chunk size : 数据分裂少,数据块移动集中消耗IO资源,通常100-200M
随着数据的增长,其中的数据大小超过了配合的chunk size,默认值时64M,则这个chunk分裂成两个,数据的增长会让chunk分裂的越来越多
? 这时候,各个shard上的chunk数量会不平衡
mongos中的一个组件balancer就会执行自动平衡,把chunk从chunk数量最多的shard节点挪动到数量最少的节点
? chunkSize 对分裂及迁移的影响
MongoDB 默认的 chunkSize 为64MB,如无特殊需求,建议保持默认值;chunkSize 会直接影响到 chunk 分裂、迁移的行为。
chunkSize 越小,chunk 分裂及迁移越多,数据分布越均衡;反之,chunkSize 越大,chunk 分裂及迁移会更少,但可能导致数据分布不均。
chunkSize 太小,容易出现 jumbo chunk(即shardKey 的某个取值出现频率很高,这些文档只能放到一个 chunk 里,无法再分裂)而无法迁移;chunkSize 越大,则可能出现 chunk 内文档数太多(chunk 内文档数不能超过 250000 )而无法迁移。
chunk 自动分裂只会在数据写入时触发,所以如果将 chunkSize 改小,系统需要一定的时间来将 chunk 分裂到指定的大小。
chunk 只会分裂,不会合并,所以即使将 chunkSize 改大,现有的 chunk 数量不会减少,但 chunk 大小会随着写入不断增长,直到达到目标大小。
数据区分:
MongoDB中数据的分片是、以集合为基本单位的,集合中的数据通过片键(Shard key)被分成多部分。其实片键就是在集合中选一个键,用该键的值作为数据拆分的依据。
所以一个好的片键对分片至关重要。片键必须是一个索引,通过sh.shardCollection加会自动创建索引(前提是此集合不存在的情况下)。一个自增的片键对写入和数据均匀分布就不是很好,因为自增的片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。
随机片键对数据的均匀分布效果很好。注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos会对结果进行归并排序。
对集合进行分片时,你需要选择一个片键,片键是每条记录都必须包含的,且建立了索引的单个字段或复合字段,MongoDB按照片键将数据划分到不同的数据块中,并将数据块均衡地分布到所有分片中。
为了按照片键划分数据块,MongoDB使用基于范围的分片方式或者 基于哈希的分片方式。
注意:
分片键是不可变。
分片键必须有索引。
分片键大小限制512bytes。
分片键用于路由查询。
MongoDB不接受已进行collection级分片的collection上插入无分片
键的文档(也不支持空值插入)
Sharded Cluster支持将单个集合的数据分散存储在多shard上,用户可以指定根据集合内文档的某个字段即shard key来进行范围分片(range sharding)。
对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。
假设有一个数字的片键:想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点。MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块,每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中,拥有”相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中。
分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。
对于基于哈希的分片,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有”相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。
Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档。
? 1、递增的sharding key
数据文件挪动小。(优势)
因为数据文件递增,所以会把insert的写IO永久放在最后一片上,造成最后一片的写热点。同时,随着最后一片的数据量增大,将不断的发生迁移至之前的片上。
? 2、随机的sharding key
数据分布均匀,insert的写IO均匀分布在多个片上。(优势)
大量的随机IO,磁盘不堪重荷。
? 3、混合型key
大方向随机递增,小范围随机分布。
为了防止出现大量的chunk均衡迁移,可能造成的IO压力。我们需要设置合理分片使用策略(片键的选择、分片算法(range、hash))
分片键是不可变、分片键必须有索引、分片键大小限制512bytes、分片键用于路由查询。
MongoDB不接受已进行collection级分片的collection上插入无分片键的文档(也不支持空值插入)
? mongos:
port 28017
dir:/mongodb/28017/conf /mongodb/28017/log
? confdb:
port:28018,28019,28020
replSetName: configReplSet
clusterRole: configsvr
dir:
/mongodb/28018/conf /mongodb/28018/log /mongod/28018/data
/mongodb/28019/conf /mongodb/28019/log /mongod/28019/data
/mongodb/28020/conf /mongodb/28020/log /mongod/28020/data
? shard1:
port:29021,29022,29023
replSetName: sh1
clusterRole: shardsvr
dir:
/mongodb/28021/conf /mongodb/28021/log /mongod/28021/data
/mongodb/28022/conf /mongodb/28022/log /mongod/28022/data
/mongodb/28023/conf /mongodb/28023/log /mongod/28023/data
? shard2:
port:28024,28025,28026
replSetName: sh2
clusterRole: shardsvr
dir:
/mongodb/28024/conf /mongodb/28024/log /mongod/28024/data
/mongodb/28025/conf /mongodb/28025/log /mongod/28025/data
/mongodb/28026/conf /mongodb/28026/log /mongod/28026/data
for i in {17..26}
do
mkdir -p /mongodb/280$i/conf
mkdir -p /mongodb/280$i/data
mkdir -p /mongodb/280$i/log
done
? sh1复制集配置文件:
for i in {21..23} ; do
cat > /mongodb/280$i/conf/mongod.conf <<'EOF'
systemLog:
destination: file
path: /mongodb/280$i/log/mongodb.log
logAppend: true
storage:
journal:
enabled: true
dbPath: /mongodb/280$i/data
directoryPerDB: true
#engine: wiredTiger
wiredTiger:
engineConfig:
cacheSizeGB: 1
directoryForIndexes: true
collectionConfig:
blockCompressor: zlib
indexConfig:
prefixCompression: true
net:
bindIp: 10.0.0.18
port: 280$i
replication:
oplogSizeMB: 2048
replSetName: sh1
sharding:
clusterRole: shardsvr
processManagement:
fork: true
EOF
done
? sh2复制集配置文件:
for i in {24..26} ; do
cat > /mongodb/280$i/conf/mongod.conf <<'EOF'
systemLog:
destination: file
path: /mongodb/280$i/log/mongodb.log
logAppend: true
storage:
journal:
enabled: true
dbPath: /mongodb/280$i/data
directoryPerDB: true
#engine: wiredTiger
wiredTiger:
engineConfig:
cacheSizeGB: 1
directoryForIndexes: true
collectionConfig:
blockCompressor: zlib
indexConfig:
prefixCompression: true
net:
bindIp: 10.0.0.18
port: 280$i
replication:
oplogSizeMB: 2048
replSetName: sh2
sharding:
clusterRole: shardsvr
processManagement:
fork: true
EOF
done
for i in {21..26}
do
mongod -f /mongodb/280$i/conf/mongod.conf
done
config = {_id: 'sh1', members: [
{_id: 0, host: '10.0.0.18:28021'},
{_id: 1, host: '10.0.0.18:28022'},
{_id: 2, host: '10.0.0.18:28023',"arbiterOnly":true}]
}
> rs.initiate(config)
config = {_id: 'sh2', members: [
{_id: 0, host: '10.0.0.18:28024'},
{_id: 1, host: '10.0.0.18:28025'},
{_id: 2, host: '10.0.0.18:28026',"arbiterOnly":true}]
}
> rs.initiate(config)
注:config server可以是一个节点,官方建议复制集,因为一旦发生故障,mongos将无法工作,congfig-server中不可以有arbiter
? 配置文件:
for i in {18..20}
do
cat >/mongodb/280$i/conf/mongod.conf<<EOF
systemLog:
destination: file
path: /mongodb/280$i/log/mongodb.log
logAppend: true
storage:
journal:
enabled: true
dbPath: /mongodb/280$i/data
directoryPerDB: true
wiredTiger:
engineConfig:
cacheSizeGB: 1
directoryForIndexes: true
collectionConfig:
blockCompressor: zlib
indexConfig:
prefixCompression: true
processManagement:
fork: true
net:
port: 280$i
replication:
oplogSizeMB: 2048
replSetName: configReplSet
sharding:
clusterRole: configsvr
EOF
done
for i in {18..20}
do
mongod -f /mongodb/280$i/conf/mongod.conf
done
? 配置复制集信息:
config = {_id: 'configReplSet', members: [
{_id: 0, host: '10.0.0.18:28018'},
{_id: 1, host: '10.0.0.18:28019'},
{_id: 2, host: '10.0.0.18:28020'}]
}
> rs.initiate(config)
? 配置文件:
systemLog:
destination: file
path: /mongodb/28017/log/mongos.log
logAppend: true
net:
port: 28017
sharding:
configDB: configReplSet/10.0.0.18:28018,10.0.0.18:28019,10.0.0.18:28020
processManagement:
fork: true
? 启动mongos节点
mongos -f /mongodb/28017/conf/mongod.conf
? 登录mongos:
mongo --port 28017 admin
? 添加分片节点:
db.runCommand( { addshard : "sh1/10.0.0.18:28021,10.0.0.18:28022,10.0.0.18:28023",name:"shard1"} )
db.runCommand( { addshard : "sh2/10.0.0.18:28024,10.0.0.18:28025,10.0.0.18:28026",name:"shard2"} )
? 列出分片信息:
db.runCommand( { listshards : 1 } )
{
"shards" : [
{
"_id" : "shard1",
"host" : "sh1/10.0.0.18:28021,10.0.0.18:28022"
},
{
"_id" : "shard2",
"host" : "sh2/10.0.0.18:28024,10.0.0.18:28025"
}
],
"ok" : 1
}
? 查看集体信息:
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("5ad76a5f23d528a32c92db04")
}
shards:
{ "_id" : "shard1", "host" : "sh1/10.0.0.18:28021,10.0.0.18:28022" }
{ "_id" : "shard2", "host" : "sh2/10.0.0.18:28024,10.0.0.18:28025" }
active mongoses:
"3.2.8" : 1
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 0
Migration Results for the last 24 hours:
No recent migrations
databases:
? 激活数据库分片功能 : 语法:( { enablesharding : "数据库名称" } )
db.runCommand( { enablesharding : "test" } )
? 指定分片建对集合分片,范围键---创建索引
db.vast.ensureIndex( { id: 1 } )
use admin
db.runCommand( { shardcollection : "test.vast",key : {id: 1} } )
for(i=0;i<2000;i++){ db.vast1.insert({"id":i,"name":"clsn","age":70,"date":new Date()}); }
db.vast.stats()
切记,管理其他库,首先要切换到admin管理库下进行操作
mongos> use admin
switched to db admin
mongos> db.runCommand( { enablesharding : "oldboy" } )
{ "ok" : 1 }
mongos> use oldboy
switched to db oldboy
mongos> db.vast.ensureIndex( { id: "hashed" } )
{
"raw" : {
"sh2/10.0.0.18:28024,10.0.0.18:28025" : {
"createdCollectionAutomatically" : true,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(1524105418, 2),
"electionId" : ObjectId("7fffffff0000000000000003")
}
}
},
"ok" : 1
}
mongos> use admin
switched to db admin
mongos> sh.shardCollection( "oldboy.vast", { id: "hashed" } )
{ "collectionsharded" : "oldboy.vast", "ok" : 1 }
mongos> use oldboy
switched to db oldboy
mongos> for(i=0;i<1000;i++){ db.vast.insert({"id":i,"name":"shenzheng","age":70,"date":new Date()}); }
WriteResult({ "nInserted" : 1 })
? mongo --port 28021
sh1:PRIMARY> db.vast.find().count()
479
? mongo --port 28024
sh2:PRIMARY> db.vast.find().count()
521
判断是否shard集群:
db.runCommand({ isdbgrid : 1})
列出所有分片信息
mongos> db.runCommand({ listshards : 1})
{
"shards" : [
{
"_id" : "shard1",
"host" : "sh1/10.0.0.18:28021,10.0.0.18:28022"
},
{
"_id" : "shard2",
"host" : "sh2/10.0.0.18:28024,10.0.0.18:28025"
}
],
"ok" : 1
}
mongos> use config
switched to db config
mongos> show dbs
config 0.001GB
oldboy 0.000GB
test 0.000GB
mongos> db.databases.find()
{ "_id" : "test", "primary" : "shard1", "partitioned" : true }
{ "_id" : "oldboy", "primary" : "shard2", "partitioned" : true }
db.collections.find()
mongos> db.printShardingStatus()
--- Sharding Status ---
或
sh.status()
sh.getBalancerState()
mongos> db.runCommand( { removeShard: "shard2" } )
? 查看Mongo集群是否开启了balance:
mongos> sh.getBalancerState()
true
当然也可以通过路由节点mongos执行sh.status()查看balance状态
如果balance开启,查看是否有数据正在迁移
连接mongo集群的路由节点
mongos> sh.isBalancerRunning()
false
(1)连接mongo集群的路由节点
(2)切换到配置节点
use config
(3)确定balance 开启中
sh.getBalancerState()
如果未开启,执行命令
sh.setBalancerState( true )
(4)修改balance 窗口的时间
db.settings.update(
{ _id: "balancer" },
{ $set: { activeWindow : { start : "<start-time>", stop : "<stop-time>" } } },
{ upsert: true }
)
(5)删除balance 窗口
use config
db.settings.update({ _id : "balancer" }, { $unset : { activeWindow : true } })
默认balance 的运行可以在任何时间,只迁移需要迁移的chunk,如果要关闭balance运行,停止一段时间可以用下列方法:
(1) 连接到路由mongos节点
(2) 停止balance
sh.stopBalancer()
(3) 查看balance状态
sh.getBalancerState()
(4)停止balance 后,没有迁移进程正在迁移,可以执行下列命令
use config
while( sh.isBalancerRunning() ) {
print("waiting...");
sleep(1000);
}
? 关闭某个集合的balance
sh.disableBalancing("students.grades")
? 打开某个集合的balance
sh.enableBalancing("students.grades")
? 确定某个集合的balance是开启或者关闭
db.getSiblingDB("config").collections.findOne({_id : "students.grades"}).noBalance;
mongodb在做自动分片平衡的时候,或引起数据库响应的缓慢,可以通过禁用自动平衡以及设置自动平衡进行的时间来解决这一问题。
(1)禁用分片的自动平衡
// connect to mongos
> use config
> db.settings.update( { _id: "balancer" }, { $set : { stopped: true } } , true );
(2)自定义 自动平衡进行的时间段
// connect to mongos
> use config
> db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "21:00", stop : "9:00" } } }, true )
标签:MongoDB
原文地址:http://blog.51cto.com/13520772/2110603