深入学习MongoDB分片(Sharding)集群技术

Posted 老男孩Linux

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入学习MongoDB分片(Sharding)集群技术相关的知识,希望对你有一定的参考价值。

分片(sharding)是MongoDB用来将大型集合分割到不同服务器(或者说一个集群)上所采用的方法。尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事。

mysql分区方案相比,MongoDB的最大区别在于它几乎能自动完成所有事情,只要告诉MongoDB要分配数据,它就能自动维护数据在不同服务器之间的均衡。

一、MongoDB分片介绍

1.1 分片的目的

高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。

为了解决这些问题,有两个基本的方法:垂直扩展和水平扩展。

垂直扩展:增加更多的CPU和存储资源来扩展容量。

水平扩展:将数据集分布在多个服务器上。水平扩展即分片。

1.2 分片设计思想

分片为应对高吞吐量与大数据量提供了方法。使用分片减少了每个分片需要处理的请求数,因此,通过水平扩展,集群可以提高自己的存储容量和吞吐量。举例来说,当插入一条数据时,应用只需要访问存储这条数据的分片.

使用分片减少了每个分片存储的数据。

例如,如果数据库1tb的数据集,并有4个分片,然后每个分片可能仅持有256GB的数据。如果有40个分片,那么每个切分可能只有25GB的数据。

深入学习MongoDB分片(Sharding)集群技术

1.3 分片机制提供了如下三种优势

1)对集群进行抽象,让集群“不可见”

MongoDB自带了一个叫做mongos的专有路由进程。mongos就是掌握统一路口的路由器,其会将客户端发来的请求准确无误的路由到集群中的一个或者一组服务器上,同时会把接收到的响应拼装起来发回到客户端。

2)保证集群总是可读写

MongoDB通过多种途径来确保集群的可用性和可靠性。将MongoDB的分片和复制功能结合使用,在确保数据分片到多台服务器的同时,也确保了每分数据都有相应的备份,这样就可以确保有服务器换掉时,其他的从库可以立即接替坏掉的部分继续工作。

3)使集群易于扩展

当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量。

1.4 分片集群架构

组件 说明
Config Server 存储集群所有节点、分片数据路由信息。默认需要配置3个Config Server节点。
Mongos 提供对外应用访问,所有操作均通过mongos执行。一般有多个mongos节点。数据迁移和数据自动平衡。
Mongod 存储应用数据记录。一般有多个Mongod节点,达到数据分片目的。

深入学习MongoDB分片(Sharding)集群技术

分片集群的构造

(1)mongos:数据路由,和客户端打交道的模块。mongos本身没有任何数据,他也不知道该怎么处理这数据,去找configserver

(2)configserver:所有存、取数据的方式,所有shard节点的信息,分片功能的一些配置信息。可以理解为真实数据的元数据。

(3)shard:真正的数据存储位置,以chunk为单位存数据。

Mongos本身并不持久化数据,Shardedcluster所有的元数据都会存储到ConfigServer,而用户的数据会议分散存储到各个shardMongos启动后,会从配置服务器加载元数据,开始提供服务,将用户的请求正确路由到对应的碎片。

Mongos的路由功能:

当数据写入时,MongoDBCluster根据分片键设计写入数据。

当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据。

二、集群中数据分布

2.1 Chunk是什么

在一个shardserver内部,MongoDB还是会把数据分为chunks,每个chunk代表这个shardserver内部一部分数据。chunk的产生,会有以下两个用途:

Splitting:当一个chunk的大小超过配置中的chunksize时,MongoDB的后台进程会把这个chunk切分成更小的chunk,从而避免chunk过大的情况

Balancing:在MongoDB中,balancer是一个后台进程,负责chunk的迁移,从而均衡各个shardserver的负载,系统初始1个chunk,chunksize默认值64M,生产库上选择适合业务的chunksize是最好的。ongoDB会自动拆分和迁移chunks。

分片集群的数据分布(shard节点)

(1)使用chunk来存储数据

(2)进群搭建完成之后,默认开启一个chunk,大小是64M,

(3)存储需求超过64M,chunk会进行分裂,如果单位时间存储需求很大,设置更大的chunk

(4)chunk会被自动均衡迁移。

2.2 chunksize的选择

适合业务的chunksize是最好的。

chunk的分裂和迁移非常消耗IO资源;chunk分裂的时机:在插入和更新,读数据不会分裂。

chunksize的选择:

小的chunksize:数据均衡是迁移速度快,数据分布更均匀。数据分裂频繁,路由节点消耗更多资源。

大的chunksize:数据分裂少。数据块移动集中消耗IO资源。通常100-200M

2.3 chunk分裂及迁移

随着数据的增长,其中的数据大小超过了配置的chunksize,默认是64M,则这个chunk就会分裂成两个。数据的增长会让chunk分裂得越来越多。

深入学习MongoDB分片(Sharding)集群技术

这时候,各个shard上的chunk数量就会不平衡。这时候,mongos中的一个组件balancer 就会执行自动平衡。把chunkchunk数量最多的shard节点挪动到数量最少的节点。

深入学习MongoDB分片(Sharding)集群技术

chunkSize 对分裂及迁移的影响:

MongoDB默认的chunkSize为64MB,如无特殊需求,建议保持默认值;chunkSize会直接影响到chunk分裂、迁移的行为。

chunkSize越小,chunk分裂及迁移越多,数据分布越均衡;反之,chunkSize越大,chunk分裂及迁移会更少,但可能导致数据分布不均。

chunkSize太小,容易出现jumbochunk(即shardKey的某个取值出现频率很高,这些文档只能放到一个chunk里,无法再分裂)而无法迁移;chunkSize越大,则可能出现chunk内文档数太多(chunk内文档数不能超过250000)而无法迁移。

chunk自动分裂只会在数据写入时触发,所以如果将chunkSize改小,系统需要一定的时间来将chunk分裂到指定的大小。

chunk只会分裂,不会合并,所以即使将chunkSize改大,现有的chunk数量不会减少,但chunk大小会随着写入不断增长,直到达到目标大小。

三、数据区分

3.1 分片键shardkey

MongoDB中数据的分片是、以集合为基本单位的,集合中的数据通过片键(Shardkey)被分成多部分。其实片键就是在集合中选一个键,用该键的值作为数据拆分的依据。

所以一个好的片键对分片至关重要。片键必须是一个索引,通过sh.shardCollection加会自动创建索引(前提是此集合不存在的情况下)。一个自增的片键对写入和数据均匀分布就不是很好,因为自增的片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。

随机片键对数据的均匀分布效果很好。注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos会对结果进行归并排序。

对集合进行分片时,你需要选择一个片键,片键是每条记录都必须包含的,且建立了索引的单个字段或复合字段,MongoDB按照片键将数据划分到不同的数据块中,并将数据块均衡地分布到所有分片中。

为了按照片键划分数据块,MongoDB使用基于范围的分片方式或者基于哈希的分片方式。

注意:

分片键是不可变。

分片键必须有索引。

分片键大小限制512bytes。

分片键用于路由查询。

MongoDB不接受已进行collection级分片的collection上插入无分片

键的文档(也不支持空值插入)

3.2 以范围为基础的分片ShardedCluster

ShardedCluster支持将单个集合的数据分散存储在多shard上,用户可以指定根据集合内文档的某个字段即shardkey来进行范围分片(rangesharding)。

深入学习MongoDB分片(Sharding)集群技术

对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。

假设有一个数字的片键想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点。MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块,每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中,拥有”相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中。

3.3 基于哈希的分片

分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。 

深入学习MongoDB分片(Sharding)集群技术

对于基于哈希的分片,MongoDB计算一个字段的哈希值,并用这个哈希值来创建数据块。在使用基于哈希分片的系统中,拥有”相近”片键的文档很可能不会存储在同一个数据块中,因此数据的分离性更好一些。

Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档。

3.4 分片键选择建议

1、递增的shardingkey

数据文件挪动小。(优势)

因为数据文件递增,所以会把insert的写IO永久放在最后一片上,造成最后一片的写热点。同时,随着最后一片的数据量增大,将不断的发生迁移至之前的片上。

2、随机的shardingkey

数据分布均匀,insert的写IO均匀分布在多个片上。(优势)

大量的随机IO,磁盘不堪重荷。

3、混合型key

大方向随机递增,小范围随机分布。

为了防止出现大量的chunk均衡迁移,可能造成的IO压力。我们需要设置合理分片使用策略(片键的选择、分片算法(range、hash))

分片注意:

分片键是不可变、分片键必须有索引、分片键大小限制512bytes、分片键用于路由查询。

MongoDB不接受已进行collection级分片的collection上插入无分片键的文档(也不支持空值插入)

四、部署分片集群

4.1 环境准备

创建程序所需的目录

for  i in 17 18 19 20 21 22 23 24 25 26 
  do 
  mkdir -p /mongodb/280$i/conf  
  mkdir -p /mongodb/280$i/data  
  mkdir -p /mongodb/280$i/log
done

4.2 shard集群配置

编辑shard集群配置文件

cat > /mongodb/28021/conf/mongod.conf <<'EOF'
systemLog:
  destination: file
  path: /mongodb/28021/log/mongodb.log   
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28021/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 10.0.0.152
  port: 28021
replication:
  oplogSizeMB: 2048
  replSetName: sh1
sharding:
  clusterRole: shardsvr
processManagement: 
  fork: true
EOF

复制shard集群配置文件

for  i in  22 23 24 25 26  
  do  
   \cp  /mongodb/28021/conf/mongod.conf  /mongodb/280$i/conf/
done

修改配置文件端口

for  i in   22 23 24 25 26  
  do 
    sed  -i  "s#28021#280$i#g" /mongodb/280$i/conf/mongod.conf
done

修改配置文件复制集名称(replSetName

for  i in    24 25 26  
  do 
    sed  -i  "s#sh1#sh2#g" /mongodb/280$i/conf/mongod.conf
done

启动shard集群

for  i in  21 22 23 24 25 26
  do  
    mongod -f /mongodb/280$i/conf/mongod.conf 
done

配置复制集1

mongo --host 10.0.0.152 --port 28021  admin

#配置复制集

config = {_id: 'sh1', members: [
                          {_id: 0, host: '10.0.0.152:28021'},
                          {_id: 1, host: '10.0.0.152:28022'},
                          {_id: 2, host: '10.0.0.152:28023',"arbiterOnly":true}]
           }  
 # 初始化配置
rs.initiate(config)  

配置复制集2

mongo --host 10.0.0.152 --port 28024  admin

#配置复制集

config = {_id: 'sh2', members: [
                          {_id: 0, host: '10.0.0.152:28024'},
                          {_id: 1, host: '10.0.0.152:28025'},
                          {_id: 2, host: '10.0.0.152:28026',"arbiterOnly":true}]
           }
# 初始化配置
rs.initiate(config)

4.3 config集群配置

创建主节点配置文件

cat > /mongodb/28018/conf/mongod.conf <<'EOF'
systemLog:
  destination: file
  path: /mongodb/28018/log/mongodb.conf
  logAppend: true
storage:
  journal:
    enabled: true
  dbPath: /mongodb/28018/data
  directoryPerDB: true
  #engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
    collectionConfig:
      blockCompressor: zlib
    indexConfig:
      prefixCompression: true
net:
  bindIp: 10.0.0.152
  port: 28018
replication:
  oplogSizeMB: 2048
  replSetName: configReplSet
sharding:
  clusterRole: configsvr
processManagement: 
  fork: true
EOF

将配置文件分发到从节点

for  i in 19 20 
  do  
   \cp  /mongodb/28018/conf/mongod.conf  /mongodb/280$i/conf/
done

修改配置文件端口信息

for  i in 19 20  
  do 
    sed  -i  "s#28018#280$i#g" /mongodb/280$i/conf/mongod.conf
done

启动configserver集群

for  i in 19 20  
  do 
    sed  -i  "s#28018#280$i#g" /mongodb/280$i/conf/mongod.conf
done

配置configserver复制集

mongo --host 10.0.0.152 --port 28018  admin

#配置复制集信息

config = {_id: 'configReplSet', members: [
                          {_id: 0, host: '10.0.0.152:28018'},
                          {_id: 1, host: '10.0.0.152:28019'},
                          {_id: 2, host: '10.0.0.152:28020'}]
           }
# 初始化配置
rs.initiate(config)   

注:configserver 使用复制集不用有arbiter节点。3.4版本以后config必须为复制集

4.4 mongos节点配置

修改配置文件

cat > /mongodb/28017/conf/mongos.conf <<'EOF'
systemLog:
  destination: file
  path: /mongodb/28017/log/mongos.log
  logAppend: true
net:
  bindIp: 10.0.0.152
  port: 28017
sharding:
  configDB: configReplSet/10.0.0.152:28108,10.0.0.152:28019,10.0.0.152:28020
processManagement: 
  fork: true
EOF 

启动mongos

mongos -f /mongodb/28017/conf/mongos.conf 

登陆到mongos

mongo 10.0.0.152:28017/admin

添加分片节点

db.runCommand( { addshard "sh1/10.0.0.152:28021,10.0.0.152:28022,10.0.0.152:28023",name:"shard1"} )
db.runCommand( { addshard "sh2/10.0.0.152:28024,10.0.0.152:28025,10.0.0.152:28026",name:"shard2"} )

列出分片

mongos> db.runCommand( { listshards : 1 } )
{
    "shards" : [
        {
            "_id" : "shard2",
            "host" : "sh2/10.0.0.152:28024,10.0.0.152:28025"
        },
        {
            "_id" : "shard1",
            "host" : "sh1/10.0.0.152:28021,10.0.0.152:28022"
        }
    ],
    "ok" : 1
}

整体状态查看

mongossh.status();

至此MongoDB的分片集群就搭建完成。

4.5 数据库分片配置

激活数据库分片功能

语法:( { enablesharding "数据库名称" } )

mongosdb.runCommand( { enablesharding "test" } )

指定分片建对集合分片,范围片键--创建索引

mongosuse test 
mongosdb.vast.ensureIndex( { id1 } )
mongosuse admin
mongosdb.runCommand( { shardcollection "test.vast",key : {id: 1} } )

集合分片验证

mongos> use test
mongos> for(i=0;i<20000;i++){ db.vast1.insert({"id":i,"name":"clsn","age":70,"date":new Date()}); }
mongos> db.vast.stats()

插入数据的条数尽量大些,能够看出更好的效果。

五、分片集群的操作

5.1 不同分片键的配置

范围片键

admin> sh.shardCollection("数据库名称.集合名称",key : {分片键: 1}  )

admin> db.runCommand( { shardcollection : "数据库名称.集合名称",key : {分片键: 1} } )

eg

admin > sh.shardCollection("test.vast",key : {id1}  )

admindb.runCommand( { shardcollection "test.vast",key : {id: 1} } )

哈希片键

admin > sh.shardCollection( "数据库名.集合名", { 片键: "hashed" } )

创建哈希索引

admindb.vast.ensureIndex( { a"hashed" } )
admin > sh.shardCollection( "test.vast", { a"hashed" } )

5.2 分片集群的操作

判断是否Shard集群

admindb.runCommand({ isdbgrid 1})

列出所有分片信息

admindb.runCommand({ listshards 1})

列出开启分片的数据库

admin> use config
config> db.databases.find( { "partitioned"true } )
config> db.databases.find() //列出所有数据库分片情况

查看分片的片键

config> db.collections.find()
{
    "_id" : "test.vast",
    "lastmodEpoch" : ObjectId("58a599f19c898bbfb818b63c"),
    "lastmod" : ISODate("1970-02-19T17:02:47.296Z"),
    "dropped" : false,
    "key" : {
        "id" : 1
    },
    "unique" : false
}

查看分片的详细信息

admindb.printShardingStatus()

adminsh.status()

删除分片节点

sh.getBalancerState()
mongosdb.runCommand( { removeShard"shard2" } )


六、balance操作

查看mongo集群是否开启了balance状态

mongos> sh.getBalancerState()
true

当然你也可以通过在路由节点mongos上执行sh.status()查看balance状态。

如果balance开启,查看是否正在有数据的迁移

连接mongo集群的路由节点

mongos> sh.isBalancerRunning()
false

6.1 设置balance窗口

(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 }
)

eg

db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "00:00"stop : "5:00" } } }, true )

当你设置了activeWindow,就不能用sh.startBalancer()启动balance

(5)删除balance窗口

use config
db.settings.update({ _id : "balancer" }, { $unset : { activeWindow : true } })

6.2 关闭balance

默认balance的运行可以在任何时间,只迁移需要迁移的chunk,如果要关闭balance运行,停止一段时间可以用下列方法:

1)连接到路由mongos节点

2)停止balance

sh.stopBalancer()

3)查看balance状态

sh.getBalancerState()

4)停止balance后,没有迁移进程正在迁移,可以执行下列命令

use config
while( sh.isBalancerRunning() ) {
          print("waiting...");
          sleep(1000);
}

6.3 重新打开balance

如果你关闭了balance,准备重新打开balance

1)连接到路由mongos节点

2)打开balance

sh.setBalancerState(true)

如果驱动没有命令 sh.startBalancer(),可以用下列命令

use config
db.settings.update( { _id: "balancer" }, { $set : { stopped: false } } , { upserttrue } )

6.4 关于集合的balance

关闭某个集合的balance

sh.disableBalancing("students.grades")

打开某个集合的balance

sh.enableBalancing("students.grades")

确定某个集合的balance是开启或者关闭

db.getSiblingDB("config").collections.findOne({_id "students.grades"}).noBalance;

6.5 问题解决

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 )

本文内容来自于 老男孩Linux云计算运维优秀学员课后笔记整理

老男孩教育作为率先开展Linux和Python培训的领跑者,
Linux云计算&&安全运维工程师2019重磅升级,持续领跑高端IT教育:
课程更新7大亮点:
1、增加Python自动化CMDB平台实战项目(约4-10天);
2、增加大量网络安全课程(约10-20天);
3、增加阿里云企业级云服务实战环境项目实践(约2-4天);
4、增加个人成长、职场高薪、成为管理者职场思想课(约10-20天);
5、增加Docker、K8S企业级实战实战项目(约5-10天);
6、增加ELK日志收集项目实战(约2天);
7、全程提供教学教材、习题、考试,学生人手多本;
将Linux运维工程师、网络安全工程师、云计算工程师、运维开发、高级架构师、数据库管理员全运维类技术岗位一网打尽。



老男孩IT教育【Linux云计算运维】课程,开设有全日制脱产班、周末班和网络在线班,以帮助更多有需要的盆友们提升技能,若是想要了解具体课程内容的话,那就点击本文左下方“阅读原文”来咨询吧~


看完本文有收获?那就分享给更多朋友吧~


以上是关于深入学习MongoDB分片(Sharding)集群技术的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB Sharding Cluster分片集群

搭建 MongoDB分片(sharding) / 分区 / 集群环境

搭建 MongoDB分片(sharding) / 分区 / 集群环境

Docker——基于Docker搭建MongoDB分片集群

Mongodb集群架构之分片架构

MongoDB学习笔记——分片(Sharding)