第1章 MongoDB分片(Sharding)技术简介
1.1 分布式系统设计理念
1.1.1 分布式存储
- 能够将大数据,按照一定的算法,均匀的分布到不同的节点上
- 能够按照原有设计的存储策略,提取数据
1.1.2 分布式的业务系统
- 除了实现分布式存储,还要考虑数据热点问题
1.2 Mongodb分片集群
- 原生态的分布式集群
- 分布式存储系统,并自带了自动分片和均衡的特性
- 将来能不能成为一个分布式的业务系统,取决于使用者
1.2.1 分片的目的
高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。为了解决这些问题,有两个基本的方法: 垂直扩展和水平扩展。
垂直扩展:增加更多的CPU和存储资源来扩展容量。
水平扩展:将数据集分布在多个服务器上。水平扩展即分片。
1.2.2 分片集群架构
1.2.2.1 Config Server
存储集群所有节点、分片数据路由信息、所有存/取数据的方式、所有shard节点的信息、分片功能的一些配置信息等,可以理解为真实数据的元数据。默认需要配置3个Config Server节点。
1.2.2.2 Mongos
Mongos是数据路由,和客户端打交道的模块,提供对外应用访问,所有操作均通过mongos执行,mongos本身没有任何数据,他也不知道该怎么处理这些数据,需要去找config server处理。一般有多个mongos节点。
1.2.2.3 Mongod(shard)
存储应用数据记录,是真正的数据存储位置,以chunk为单位村粗数据。一般有多个Mongod节点以达到数据分片的目的。
1.2.3 分片集群的数据分布(shard节点)
- 使用chunk来存储数据
- 集群搭建完成之后,默认开启一个chunk,大小是64M,
- 存储需求超过64M,chunk会进行分裂,如果单位时间存储需求很大,可以设置更大的chunk
- chunk会被自动均衡迁移
1.2.3.1 chunksize的选择的原则
- 适合业务的chunksize是最好的
- chunk的分裂和迁移:非常消耗IO资源
- chunk分裂的时机:插入和更新时,读数据不会分裂
1.2.3.2 chunksize的选择
- 小的chunksize:数据均衡是迁移速度快,数据分布更均匀。数据分裂频繁,路由节点消耗更多资源
- 大的chunksize:数据分裂少。数据块移动集中消耗IO资源
- 通常100-200M
1.3 分片策略
为了防止出现大量的chunk均衡迁移,可能造成的IO压力,我们需要设置合理分片使用策略(片键的选择、分片算法(range、hash))
1.3.1 路由和平衡
- 当数据写入时,MongoDBCluster根据分片键设计写入数据
- 当外部语句发起数据查询时,MongoDB根据数据分布自动路由至指定节点返回数据
1.3.2 平衡(数据分布策略)
Sharded Cluster支持将单个集合的数据分散存储在多shard上,用户可以指定根据集合内文档的某个字段即shard key来分布数据。
范围分片:
1.3.2.1 Mongos均衡特性
如图,第一个chunk的范围就是uid从-∞到12000范围内的数据。第二个就是12000到58000 。以此类推。对于一个刚配置为Sharding的collection ,最开始只有一个chunk,范围是从-∞到+∞。
随着数据的增长,其中的数据大小超过了配置的chunk size,默认是64M,则这个chunk就会分裂成两个。数据的增长会让chunk分裂得越来越多。这时候,各个shard 上的chunk数量就会不平衡。这时候, mongos中的一个组件balancer就会执行自动平衡。把chunk从chunk数量最多的shard节点挪动到数量最少的节点。
1.3.3 Hash分片
Hash分片与范围分片互补,能将文档随机的分散到各个chunk,充分的扩展写能力,弥补了范围分片的不足,但不能高效的服务范围查询,所有的范围查询要分发到后端所有的Shard才能找出满足条件的文档。
1.3.4 分片注意事项
- 分片键是不可变
- 分片键必须有索引
- 分片键大小限制512bytes
- 分片键用于路由查询
- MongoDB不接受已进行collection级分片的collection上插入无分片键的文档(也不支持空值插入)
1.4 分片键
1.4.1 分片键介绍
- 必须为分片collection 定义分片键
- 基于一个或多个列(类似一个索引)
- 分片键定义数据空间
- 想象key space 类似一条线上一个点数据
- 一个key range 是一条线上一段数据
1.4.2 分片键选择的建议
- 递增的sharding key
- 数据文件挪动小。(优势)
- 因为数据文件递增,所以会把insert的写IO永久放在最后一片上,造成最后一片的写热点。
- 同时,随着最后一片的数据量增大,将不断的发生迁移至之前的片上。
- 随机的sharding key
- 数据分布均匀,insert的写IO均匀分布在多个片上。(优势)
- 大量的随机IO,磁盘不堪重荷。
- 混合型key
- 大方向随机递增。
- 小范围随机分布。
第2章 搭建及管理分片集群
提示:此分片集群是在已经安装好mongodb的基础上进行配置的,详情请参考《MongoDB基础(二)—搭建》。
2.1 搭建分片集群
2.1.1 创建实例目录及授权
[root@db01 ~]# mkdir -p /usr/local/mongodb/290{17..27}/{conf,data,log} [root@db01 ~]# chown -R mongod.mongod /usr/local/mongodb/
2.1.2.1 编写配置文件
- sh1复制集:
[root@db01 ~]# vim /usr/local/mongodb/29021/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/29022/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/29023/conf/mongod.yml systemLog: destination: file path: "/usr/local/mongodb/29021/log/mongod.log" #path: "/usr/local/mongodb/29022/log/mongod.log" #path: "/usr/local/mongodb/29023/log/mongod.log" logAppend: true processManagement: fork: true storage: journal: enabled: true dbPath: "/usr/local/mongodb/29021/data" #dbPath: "/usr/local/mongodb/29022/data" #dbPath: "/usr/local/mongodb/29023/data" directoryPerDB: true wiredTiger: engineConfig: cacheSizeGB: 1 directoryForIndexes: true collectionConfig: blockCompressor: zlib indexConfig: prefixCompression: true net: bindIp: 10.0.0.41 port: 29021 #port: 29022 #port: 29023 replication: oplogSizeMB: 2048 replSetName: sh1 sharding: clusterRole: shardsvr
- sh2复制集:
[root@db01 ~]# vim /usr/local/mongodb/29024/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/29025/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/29026/conf/mongod.yml systemLog: destination: file path: "/usr/local/mongodb/29024/log/mongod.log" #path: "/usr/local/mongodb/29025/log/mongod.log" #path: "/usr/local/mongodb/29026/log/mongod.log" logAppend: true processManagement: fork: true storage: journal: enabled: true dbPath: "/usr/local/mongodb/29024/data" #dbPath: "/usr/local/mongodb/29025/data" #dbPath: "/usr/local/mongodb/29026/data" directoryPerDB: true wiredTiger: engineConfig: cacheSizeGB: 1 directoryForIndexes: true collectionConfig: blockCompressor: zlib indexConfig: prefixCompression: true net: bindIp: 10.0.0.41 port: 29024 #port: 29025 #port: 29026 replication: oplogSizeMB: 2048 replSetName: sh2 sharding: clusterRole: shardsvr
2.1.2.2 开启服务
[mongod@db01 ~]$ mongod -f /usr/local/mongodb/29021/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/29022/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/29023/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/29024/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/29025/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/29026/conf/mongod.yml
2.1.2.3 配置复制集
- 配置sh1复制集:
[mongod@db01 ~]$ mongo --host=10.0.0.41 --port=29021 config = {_id: 'sh1', members: [ {_id: 0, host: '10.0.0.41:29021'}, {_id: 1, host: '10.0.0.41:29022'}, {_id: 2, host: '10.0.0.41:29023',"arbiterOnly":true}] } rs.initiate(config)
- 配置sh2复制集:
[mongod@db01 ~]$ mongo --host=10.0.0.41 --port=29024 config = {_id: 'sh2', members: [ {_id: 0, host: '10.0.0.41:29024'}, {_id: 1, host: '10.0.0.41:29025'}, {_id: 2, host: '10.0.0.41:29026',"arbiterOnly":true}] } rs.initiate(config)
2.1.3 配置config server节点
2.1.3.1 编辑配置文件
[root@db01 ~]# vim /usr/local/mongodb/29018/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/29019/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/29020/conf/mongod.yml systemLog: destination: file path: "/usr/local/mongodb/29018/log/mongod.log" #path: "/usr/local/mongodb/29019/log/mongod.log" #path: "/usr/local/mongodb/29020/log/mongod.log" logAppend: true processManagement: fork: true storage: journal: enabled: true dbPath: "/usr/local/mongodb/29018/data" #dbPath: "/usr/local/mongodb/29019/data" #dbPath: "/usr/local/mongodb/29020/data" directoryPerDB: true wiredTiger: engineConfig: cacheSizeGB: 1 directoryForIndexes: true collectionConfig: blockCompressor: zlib indexConfig: prefixCompression: true net: bindIp: 10.0.0.41 port: 29018 #port: 29019 #port: 29020 replication: oplogSizeMB: 2048 replSetName: configReplSet sharding: clusterRole: configsvr
2.1.3.2 开启服务
[mongod@db01 ~]$ mongod -f /usr/local/mongodb/29018/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/29019/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/29020/conf/mongod.yml
2.1.3.3 配置configReplSet复制集
注意:mongodb 3.4之后,虽然要求config server为replica set,但是不支持arbiter。
[mongod@db01 ~]$ mongo --host=10.0.0.41 --port=29018 config = {_id: 'configReplSet', members: [ {_id: 0, host: '10.0.0.41:29018'}, {_id: 1, host: '10.0.0.41:29019'}, {_id: 2, host: '10.0.0.41:29020'}] } rs.initiate(config)
2.1.4 配置mongos节点
2.1.4.1 编写配置文件
[root@db01 ~]# vim /usr/local/mongodb/29017/conf/mongod.yml systemLog: destination: file path: "/usr/local/mongodb/29017/log/mongod.log" logAppend: true processManagement: fork: true net: bindIp: 10.0.0.41 port: 29017 sharding: configDB: configReplSet/10.0.0.41:29018,10.0.0.41:29019,10.0.0.41:29020
2.1.4.2 开启服务
[mongod@db01 ~]$ mongos -f /usr/local/mongodb/29017/conf/mongod.yml
2.1.5 连接到mongs并添加分片信息
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.runCommand( { addshard : "sh1/10.0.0.41:29021,10.0.0.41:29022,10.0.0.41:29023",name:"shard1"} ) { "shardAdded" : "shard1", "ok" : 1 } mongos> db.runCommand( { addshard : "sh2/10.0.0.41:29024,10.0.0.41:29025,10.0.0.41:29026",name:"shard2"} ) { "shardAdded" : "shard2", "ok" : 1 }
2.1.6 查看分片集群配置情况
2.1.6.1 列出分片
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.runCommand( { listshards : 1 } ) { "shards" : [ { "_id" : "shard1", "host" : "sh1/10.0.0.41:29021,10.0.0.41:29022" }, { "_id" : "shard2", "host" : "sh2/10.0.0.41:29024,10.0.0.41:29025" } ], "ok" : 1 }
2.1.6.2 查看整体状态
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> sh.status() --- Sharding Status --- sharding version: { "_id" : 1, "minCompatibleVersion" : 5, "currentVersion" : 6, "clusterId" : ObjectId("5a20ad3bf228506f1dc38231") } shards: { "_id" : "shard1", "host" : "sh1/10.0.0.41:29021,10.0.0.41:29022" } { "_id" : "shard2", "host" : "sh2/10.0.0.41:29024,10.0.0.41:29025" } 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:
2.2 分片集群管理操作
2.2.1 模拟业务数据
[mongod@db01 ~]$ mongo 10.0.0.41:29017/test mongos> for(i=0;i<2000000;i++){ db.vast.insert({"id":i,"name":"shenzheng","age":70,"date":new Date()}); } # 等待时间较长 WriteResult({ "nInserted" : 1 })
2.2.2 激活数据库分片功能
【语法】:
admin> ( { enablesharding : "数据库名称" } )
【操作】:
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.runCommand( { enablesharding : "test" } ) { "ok" : 1 } # 或 ( { enablesharding : "数据库名称" } )
2.2.3 创建索引
从插入的数据发现业务只有id和date列,唯一值比较多的,适合做索引以及分片键,这里选择id列做索引和分片键:
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.vast.ensureIndex( { id: 1 } ) { "raw" : { "configReplSet/10.0.0.41:29018,10.0.0.41:29019,10.0.0.41:29020" : { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 2, "note" : "all indexes already exist", "ok" : 1, "$gleStats" : { "lastOpTime" : Timestamp(1512091603, 4), "electionId" : ObjectId("7fffffff0000000000000001") } } }, "ok" : 1 } # 或 db.runCommand( { shardcollection : "test.vast",key : {id: 1} } )
2.2.4 创建分片键
【语法】:
admin> sh.shardCollection("数据库名称.集合名称",key : {分片键: 1} ) 或 admin> db.runCommand( { shardcollection : "数据库名称.集合名称",key : {分片键: 1} } )
【操作】:
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.runCommand( { shardcollection : "test.vast",key : {id: 1} } ) { "collectionsharded" : "test.vast", "ok" : 1 } # 或 sh.shardCollection("test.vast",key : {id: 1} )
2.2.5 创建哈希索引(扩展)
【语法】:
admin > sh.shardCollection( "数据库名.集合名", { 片键: "hashed" } )
【操作】:
admin> db.vast.ensureIndex( { a: "hashed" } ) admin > sh.shardCollection( "test.vast", { a: "hashed" } )
2.2.6 判断是否Shard集群
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.runCommand({ isdbgrid : 1}) { "isdbgrid" : 1, "hostname" : "db01", "ok" : 1 }
2.2.7 列出所有分片信息
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.runCommand({ listshards : 1}) { "shards" : [ { "_id" : "shard1", "host" : "sh1/10.0.0.41:29021,10.0.0.41:29022" }, { "_id" : "shard2", "host" : "sh2/10.0.0.41:29024,10.0.0.41:29025" } ], "ok" : 1 }
2.2.8 列出开启分片的数据库
[mongod@db01 ~]$ mongo 10.0.0.41:29017/config mongos> db.databases.find( { "partitioned": true } ) { "_id" : "test", "primary" : "shard2", "partitioned" : true }
2.2.9 列出所有数据库分片情况
[mongod@db01 ~]$ mongo 10.0.0.41:29017/config mongos> db.databases.find() { "_id" : "test", "primary" : "shard2", "partitioned" : true }
2.2.10 查看分片的片键
[mongod@db01 ~]$ mongo 10.0.0.41:29017/config mongos> db.collections.find().pretty() { "_id" : "test.vast", "lastmodEpoch" : ObjectId("5a20afdaf228506f1dc3827b"), "lastmod" : ISODate("1970-02-19T17:02:47.296Z"), "dropped" : false, "key" : { "id" : 1 }, "unique" : false }
2.2.11 查看分片的详细信息
mongos> db.printShardingStatus() 或 mongos> sh.status() --- Sharding Status --- sharding version: { "_id" : 1, "minCompatibleVersion" : 5, "currentVersion" : 6, "clusterId" : ObjectId("5a20ad3bf228506f1dc38231") } shards: { "_id" : "shard1", "host" : "sh1/10.0.0.41:29021,10.0.0.41:29022" } { "_id" : "shard2", "host" : "sh2/10.0.0.41:29024,10.0.0.41:29025" } 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: 7 : Success 1 : Failed with error 'aborted', from shard2 to shard1 databases: { "_id" : "test", "primary" : "shard2", "partitioned" : true } test.vast shard key: { "id" : 1 } unique: false balancing: true chunks: shard1 7 shard2 8 { "id" : { "$minKey" : 1 } } -->> { "id" : 1 } on : shard1 Timestamp(8, 1) { "id" : 1 } -->> { "id" : 13 } on : shard2 Timestamp(7, 1) { "id" : 13 } -->> { "id" : 103576 } on : shard2 Timestamp(2, 2) { "id" : 103576 } -->> { "id" : 223714 } on : shard2 Timestamp(2, 3) { "id" : 223714 } -->> { "id" : 327277 } on : shard1 Timestamp(3, 2) { "id" : 327277 } -->> { "id" : 447413 } on : shard1 Timestamp(3, 3) { "id" : 447413 } -->> { "id" : 550976 } on : shard2 Timestamp(4, 2) { "id" : 550976 } -->> { "id" : 671112 } on : shard2 Timestamp(4, 3) { "id" : 671112 } -->> { "id" : 774675 } on : shard1 Timestamp(5, 2) { "id" : 774675 } -->> { "id" : 895972 } on : shard1 Timestamp(5, 3) { "id" : 895972 } -->> { "id" : 1103098 } on : shard2 Timestamp(6, 2) { "id" : 1103098 } -->> { "id" : 1373512 } on : shard2 Timestamp(6, 3) { "id" : 1373512 } -->> { "id" : 1580638 } on : shard1 Timestamp(7, 2) { "id" : 1580638 } -->> { "id" : 1861821 } on : shard1 Timestamp(7, 3) { "id" : 1861821 } -->> { "id" : { "$maxKey" : 1 } } on : shard2 Timestamp(8, 0)
2.3 节点管理操作
2.3.1 添加节点(分片)
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.runCommand( { addshard : "sh1/10.0.0.41:29021,10.0.0.41:29022,10.0.0.41:29023",name:"shard1"} ) { "shardAdded" : "shard1", "ok" : 1 }
2.3.2 删除节点(分片)
注意:由于删除节点会产生chunk迁移,所以删除节点时间视数据量大小而定,并且删除节点的时候不要再做其他操作,防止数据库发生未知错误。
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> db.runCommand( { removeShard: "shard2" } ) { "msg" : "draining started successfully", "state" : "started", # 删除开始 "shard" : "shard2", "note" : "you need to drop or movePrimary these databases", "dbsToMove" : [ "test" ], "ok" : 1 } mongos> db.runCommand( { removeShard: "shard2" } ) { "msg" : "draining ongoing", "state" : "ongoing", # 删除进行中 "remaining" : { "chunks" : NumberLong(8), "dbs" : NumberLong(1) }, "note" : "you need to drop or movePrimary these databases", "dbsToMove" : [ "test" ], "ok" : 1 } mongos> db.runCommand( { removeShard: "shard2" } ) { "msg" : "removeshard completed successfully", "state" : "completed", # 删除完成 "shard" : "shard2", "ok" : 1 }
2.4 balance管理操作
2.4.1 查看balance状态
[mongod@db01 ~]$ mongo 10.0.0.41:29017/admin mongos> sh.getBalancerState() true
2.4.2 关闭和启动balance(不要随意操作)
mongo 10.0.0.41:29017/admin mongos> sh.stopBalancer() Waiting for active hosts... Waiting for the balancer lock... Waiting again for active hosts after balancer is off... mongos> sh.getBalancerState() false mongos> sh.startBalancer() mongos> sh.getBalancerState() true
2.4.3 定义balancer的工作窗口(很重要)
mongo 10.0.0.41:29017/admin mongos>db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "00:00", stop : "5:00" } } }, true )
提示:设置时间窗口的原则:1、避开业务繁忙期;2、避开备份时间窗口
2.4.4 关于集合的balance(很少用到)
2.4.4.1 关闭某个集合的balance
sh.disableBalancing("students.grades")
2.4.4.2 打开某个集合的balance
sh.enableBalancing("students.grades")
2.4.4.3 确定某个集合的balance是开启或者关闭
db.getSiblingDB("config").collections.findOne({_id : "students.grades"}).noBalance;
