MongoDB Sharding Cluster

Posted

tags:

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

MongoDB Sharding Cluster

第1章 分片技术简述:

shardingMongoDB用来将大型集合分割高不同服务器上所采用的方法,尽管分片起源于关系型数据库分区,MongoDB分片完全又是另一回事

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

1.1 分片的目的:

?  垂直扩展:增加更多的cpu和存储资源来扩展容量,也属于硬件扩展

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

1.2 分片设计思想:

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

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

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

技术分享图片

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

1.      对集群进行抽象,让集群不可见

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

2.      保证集群总是可读写

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

3.      使集群易于扩展,当系统需要更多的空间和资源的时候,MongoDB使我们可以按需方便的扩充系统容量

1.2 MongoDB sharding架构:

分片集群架构:

组件

说明

Config Server

存储集群所有节点、分片数据路由信息。默认需要配置3Config Server节点。

Mongos

提供对外应用访问,所有操作均通过mongos执行。一般有多个mongos节点。数据迁移和数据自动平衡。

Mongod

存储应用数据记录。一般有多个Mongod节点,达到数据分片目的。

技术分享图片

1.1.1 分片集群的构造:

1.      mongos : 数据路由,和客户端打交道的模块,mongos自身没有任何数据,也不知道该如何处理数据,这些都需要config-server来告诉他

2.      config-server : 所有存,取数据的方式,所有shard节点的信息,分片功能的一些配置信息,可以理解为真实数据的元数据

3.      shard : 真正的数据存储位置,chunk为单位存储数据,超过设定的默认大小就会继续向下分裂成多个chunk

mongos本身不持久化数据,sharded cluster所有的元数据都会存储到config-server,而用户的数据会分散存储到各个shard分片中,mongos启动后,会从配置服务器加载元数据,开始提供服务,将用户的请求正确路由到对应的碎片

1.1.2 mongos路由功能:

1.      当数据写入时,MongoDB Cluster根据分片键设计写入数据

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

1.2 集群中数据的分布:

1.2.1 Chunk是什么?

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

?  Splitting :

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

?  Balancing :

MongoDB,balance是一个后台进程,负责chunk的迁移,从而均衡各个shard-server负载,系统初识1chunk,chunk size默认值是64M,生产库上选择适合业务的chunk size是最好的,MongoDB会自动拆分和迁移chunks

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

1.      使用chunk来存储数据

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

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

4.      chunk会自动均衡迁移

1.3 chunk size的选择

适合业务的chunk size是最好的

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

1.3.1 chunk size的选择:

?  小的chunk size : 数据均衡时迁移快,数据分布更加均匀,数据分裂频繁,路由节点消耗更多资源

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

1.3.2 chunk分裂和迁移:

随着数据的增长,其中的数据大小超过了配合的chunk size,默认值时64M,则这个chunk分裂成两个,数据的增长会让chunk分裂的越来越多

技术分享图片

?  这时候,各个shard上的chunk数量会不平衡

mongos中的一个组件balancer就会执行自动平衡,chunkchunk数量最多的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 大小会随着写入不断增长,直到达到目标大小。

数据区分:

1.1 2.3.1 分片键shard key

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

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

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

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

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

注意:

分片键是不可变。

分片键必须有索引。

分片键大小限制512bytes

分片键用于路由查询。

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

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

1.1.1 以范围为基础的分片Sharded Cluster

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

技术分享图片

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

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

1.1.1 基于哈希的分片

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

技术分享图片

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

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

1.1.1 分片键选择建议

?  1、递增的sharding key

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

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

?  2、随机的sharding key

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

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

?  3、混合型key

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

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

1.2 分片注意:

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

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

第2章 部署分片集群:

2.1 规划:

?  mongos:  

       port 28017

    dir:/mongodb/28017/conf  /mongodb/28017/log

?  confdb:

  port28018,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

2.2 环境准备:

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

2.3 shard集群配置:

?  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

2.3.1 启动shard集群:

for  i in  {21..26}

  do 

    mongod -f /mongodb/280$i/conf/mongod.conf

done

2.3.2 配置复制集sh1:

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)

2.3.3 配置复制集sh2:

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)

2.4 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

2.4.1 启动config server集群:

for  i in  {18..20}

  do 

    mongod -f /mongodb/280$i/conf/mongod.conf

done

2.4.2 配置config server复制集:

?  配置复制集信息:

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)

2.5 配置mongos节点:

?  配置文件:

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:

第3章 数据库分片配置:

3.1 主键索引:

?  激活数据库分片功能 : 语法:( { enablesharding : "数据库名称" } )

db.runCommand( { enablesharding : "test" } )

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

db.vast.ensureIndex( { id: 1 } )

use admin

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

3.1.1 集合分片验证:

for(i=0;i<2000;i++){ db.vast1.insert({"id":i,"name":"clsn","age":70,"date":new Date()}); }

db.vast.stats()

3.2 创建hash索引:

切记,管理其他库,首先要切换到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 })

3.2.1 验证结果:

?  mongo --port 28021

sh1:PRIMARY> db.vast.find().count()

479

?  mongo --port 28024

sh2:PRIMARY> db.vast.find().count()

521

第4章 管理操作:

判断是否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

}

4.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 }

4.2 查看分片的片键

db.collections.find()

4.3 查看分片的详细信息:

mongos> db.printShardingStatus()

--- Sharding Status ---

sh.status()

4.4 删除分片节点:

sh.getBalancerState()

mongos> db.runCommand( { removeShard: "shard2" } )

第5章 balance操作:

5.1 查看balance

?  查看Mongo集群是否开启了balance:

mongos> sh.getBalancerState()

true

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

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

连接mongo集群的路由节点

mongos> sh.isBalancerRunning()

false

5.2 设置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 }

)

5)删除balance 窗口

use config

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

5.2.1 关闭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);

}

5.3 关于集合的balance

?  关闭某个集合的balance

sh.disableBalancing("students.grades")

?  打开某个集合的balance

sh.enableBalancing("students.grades")

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

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

5.4 数据库性能问题

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 Sharding Cluster的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB Sharding Cluster

Mongodb中Sharding集群

MongoDB Sharding Cluster分片集群

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

Mongodb的Replica Sets + Sharding Architect

MongoDB sharding cluster Step by Step