5、MongoDB分片集群
分片是一种在多台机器间分配数据的方法。MongoDB使用分片来支持具有非常大的数据集和吞吐操作的部署
具有大数据集或高吞吐量应用程序的数据库系统可能会挑战单个服务器的容量。例如查询率可能会耗尽服务器CPU容量。大于系统RAM的工作集大小将强调磁盘驱动器的I / O容量。
解决系统增长有两种方法:垂直和水平缩放
垂直缩放涉及增加单个服务器的容量,例如强大的CPU,增加更多RAM或增加存储空间。可用技术的局限性可能会限制单个机器对于给定的工作负载而言足够强大。
水平缩放涉及将系统数据集和多个服务器的负载分开,增加额外的服务器以根据需要增加容量。虽然单个机器的总体速度或容量可能不高,但每台机器处理整个工作负载的一部分,可能提供比单个高速大容量服务器更好的效率。扩展部署容量只需要更具需要增加额外的服务器,对于单台机器来说,这可能比高端硬件的总体成本更低。需要权衡的是基础设施和部署维护的复杂性
MongoDB支持通过分片进行水平缩放
MongoDB分片集群由一下组件组成
分片:每个分片包含数据的一部分。每个分片可以部署为副本集
mongos:mongos作为查询路由,提供客户端应用程序和分片集群之间的接口
配置服务器:配置服务器存储集群的元数据和配置设置。从MongoDB3.4开始,配置服务器必须部署为副本集
分片键
为了将文档分发到一个集合中, MongoDB使用分片键对集合进行分区。分片键由目标集合中的每个文档的不可变字段组成
在分割集合时,我们可以选择分片键。分片后的分片键的选择不能改变。分片集合只能有一个分片键
要分片非空集合,集合必须具有分片键开头的索引。对于非空集合,如果集合尚未具有指定分片键的适当索引,则MongoDB将创建索引
分片键的选择会影响分片集群的性能,效率和可扩展性。具有最佳硬件和基础架构的集群可能会被选择分片密钥所瓶颈。分片键及其后备索引的选择也会影响集群,可以使用分片策略
块
MongoDB将分片的数据分割成块。每个块基于分片键都有一个包含更低和独占的上限范围
MongoDB使用分片集群平衡器在分片集群中迁移数据块。平衡器试图在集群中的所有分片之间实现均匀的块平衡
分片的优点
读/写
MongoDB将读写工作负载分布在片分区中的分片上,从而允许每个分片处理集群操作的一个子集。通过添加更多的分片,可以跨集群水平地缩放读取和写入工作负载
对于包含分片键或复合分片键的前缀查询,mongos可以在特定的分片或一组分片上进行查询。这些目标操作通常比向集群中的每个碎片广播更有效
存储容量
分片集群分配整个数据集,允许每个分片包含总簇数据的子集。随着数据集的增长,附加的分片增加了集群的存储容量
高可用性
分片集群可以继续执行部分读/写操作,即使一个或多个分片不可用。虽然在停机期间无法访问不可用的分片上的数据子集,但是可用分片上读/写仍然可以成功
从MongoDB3.2开始,我们可以将配置夫妻部署为副本集。只要大多数副本集可用,具有配置服务器副本集的分片集群可以继续处理读取和写入。3.4版本开始配置服务器必须部署为副本集
在生产环境中,应将各个分片部署为副本集,从而提供更多的冗余性和可用性
拆分之前的注意事项
为了确保集群的性能和效率,必须仔细考虑选择分片键。分片后不能更改分片键,也不能清理分片集
如果查询不包含分片键或复合分片键的前缀,则mongos执行广播操作,查询分片中的所有分片。这些分散/收集查询可以是长时间的操作
分片和非分片集
数据库可以混合使用分片和未完成的集合。分片集合被分区并分布在集群中的分片上。未分类的集合存储在主分片上、每个数据库独有自己的主分片
连接到分片集
我们必须连接到mongos路由器上才能与分片集群中的任何集群进行交互。客户应永远不会连接到一个分片,以便进行读写操作
分片策略
MongoDB支持两种分片策略,用于在分片集群中分配数据
散列分片
散列分片涉及计算机分片键字段值的哈希值,然后基于散列的分片键 值为每个组分配一个范围
注意:MongoDB在使用散列索引解析查询时自动计算哈希值。应用程序不需要计算哈希值
它们的散列值不太可能在同一个块中。基于散列值的数据分布有助于更均匀的数据分布,特别是在分片键单调变化的数据集中
然而,散列分布意味着在分片键上的基于范围的查询不太可能针对单个分片,导致更多集群广播操作
远程分片
远程分片根据分片键值将数据划分为范围,每个组块然后被分配基于所述键片的值的范围内
值为"close"的分片键的范围更可能驻留在同一个块上,这允许目标操作,因为mongos可以将操作路由到仅包含所需数据的分片上
远程分片的效率取决于所选择的分片键。数据分布不佳可能导致数据分布不均匀,这可能会抵消分片的一些好处,或者可能导致性能瓶颈
分片群中的区域
在分片集群中,您可以基于切分键创建共享数据区域。您可以将每个区域与集群中的一个或多个碎片关联起来。碎片可以与任意数量的非冲突区域相关联。在一个平衡的集群中,MongoDB将一个区域所覆盖的块迁移到与该区域相关联的那些碎片上
每个区域覆盖一个或多个分片键值范围。区域覆盖的每个范围总是包括其下边界,而不是其上限
当定义要覆盖的区域的新范围时,必须使用分片键中包含的字段,如果使用复合分片键,则范围必须包含分片键的前缀
当选择分片键,请仔细考虑以后使用区域分片的可能性,因为在分割收集后无法更改分片键
最常见的是,区域可以改善跨域多个数据中心的分片集群的数据位置
分片中的排序
使用shardCollection
带有该选项的命令来分割具有默认排序规则的集合 。成功的分片要求:collation : { locale : "simple" }
- 该集合必须具有前缀是分片键的索引
- 索引必须具有排序规则
{ locale: "simple" }
部署没有访问控制的分片集群
下面我对这张图解释一下:
人脸: 代表客户端,客户端肯定说,你数据库分片不分片跟我没关系,我叫你干啥就干啥,没什么好商量的。
mongos: 首先我们要了解”片键“的概念,也就是说拆分集合的依据是什么?按照什么键值进行拆分集合....
好了,mongos就是一个路由服务器,它会根据管理员设置的“片键”将数据分摊到自己管理的mongod集群,数据
和片的对应关系以及相应的配置信息保存在"config服务器"上。
mongod: 一个普通的数据库实例,如果不分片的话,我们会直接连上mongod。
一、创建config服务器副本集
对于生产部署,部署具有至少三个成员的配置服务器副本集,出于测试目的,我的环境如下
ip | hostname | 端口 | 集群名字 |
192.168.88.1 | c1.heboan.com |
27017 27018 27019 |
bagtree |
mongoDB安装脚本如下
#!/bin/bash #创建mongo用户 groupadd mongo useradd -g mongo mongo mkdir /application cd /data/tools tar zxf mongodb-linux-x86_64-rhel70-3.4.5.tgz -C /application/ cd /application ln -s mongodb-linux-x86_64-rhel70-3.4.5 mongodb #创建实例目录 mkdir -p {27017/{conf,log,pid},27018/{conf,log,pid},27019/{conf,log,pid}} #创建实例数据目录 mkdir /data/{db_27017,db_27018,db_27019} #27017配置文件 cat >/application/27017/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/27017/log/mongod.log storage: dbPath: /data/db_27017 journal: enabled: true processManagement: fork: true pidFilePath: /application/27017/pid/mongod.pid sharding: clusterRole: configsvr <------------------角色 replication: replSetName: "bagtree" <------------------集群名字 net: port: 27017 bindIp: 127.0.0.1,192.168.88.1 EOF #27018配置文件 cat >/application/27018/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/27018/log/mongod.log storage: dbPath: /data/db_27018 journal: enabled: true processManagement: fork: true pidFilePath: /application/27018/pid/mongod.pid sharding: clusterRole: configsvr replication: replSetName: "bagtree" net: port: 27018 bindIp: 127.0.0.1,192.168.88.1 EOF #27019配置文件 cat >/application/27019/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/27019/log/mongod.log storage: dbPath: /data/db_27019 journal: enabled: true processManagement: fork: true pidFilePath: /application/27019/pid/mongod.pid sharding: clusterRole: configsvr replication: replSetName: "bagtree" net: port: 27019 bindIp: 127.0.0.1,192.168.88.1 EOF #环境变量 cat >/etc/profile.d/mongodb.sh<<EOF export PATH=/application/mongodb/bin:$PATH EOF source /etc/profile.d/mongodb.sh #目录权限配置 chown -R mongo.mongo /data/{db_27017,db_27018,db_27019} chown -R mongo.mongo /application/mongo* chown -R mongo.mongo /application/2701*
启动三个实例
su - mongo mongod -f /application/27017/conf/mongod.conf mongod -f /application/27018/conf/mongod.conf mongod -f /application/27019/conf/mongod.conf
将mongo shell连接到其中一个配置服务器成员
mongo --port 27017
使用rs.initiate()方法启动副本集
> rs.initiate( ... { ... _id: "bagtree", ... configsvr: true, ... members: [ ... { _id: 0, host: "c1.heboan.com:27017"}, ... { _id: 1, host: "c1.heboan.com:27018"}, ... { _id: 2, host: "c1.heboan.com:27019"} ... ] ... } ... )
二、创建分片副本集
对于生产部署,请使用至少上成员的副本集,出于测试目的,我的的环境如下:
创建两个分片,每个分片配置上成员的副本集
ip | hostname | 分片1(集群名bg_one) | 分片2(集群名bg_two) |
192.168.88.2 | c2.heboan.com |
27017 27018 27019 |
37017 37018 37019 |
mongoDB安装脚本如下
#!/bin/bash groupadd mongo useradd -g mongo mongo mkdir /application cd /data/tools tar zxf mongodb-linux-x86_64-rhel70-3.4.5.tgz -C /application/ cd /application ln -s mongodb-linux-x86_64-rhel70-3.4.5 mongodb mkdir -p {27017/{conf,log,pid},27018/{conf,log,pid},27019/{conf,log,pid}} mkdir -p {37017/{conf,log,pid},37018/{conf,log,pid},37019/{conf,log,pid}} mkdir /data/{db_27017,db_27018,db_27019} mkdir /data/{db_37017,db_37018,db_37019} #27017 cat >/application/27017/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/27017/log/mongod.log storage: dbPath: /data/db_27017 journal: enabled: true processManagement: fork: true pidFilePath: /application/27017/pid/mongod.pid sharding: clusterRole: shardsvr replication: replSetName: "bg_one" net: port: 27017 bindIp: 127.0.0.1,192.168.88.2 EOF #27018 cat >/application/27018/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/27018/log/mongod.log storage: dbPath: /data/db_27018 journal: enabled: true processManagement: fork: true pidFilePath: /application/27018/pid/mongod.pid sharding: clusterRole: shardsvr replication: replSetName: "bg_one" net: port: 27018 bindIp: 127.0.0.1,192.168.88.2 EOF #27019 cat >/application/27019/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/27019/log/mongod.log storage: dbPath: /data/db_27019 journal: enabled: true processManagement: fork: true pidFilePath: /application/27019/pid/mongod.pid sharding: clusterRole: shardsvr replication: replSetName: "bg_one" net: port: 27019 bindIp: 127.0.0.1,192.168.88.2 EOF #37017 cat >/application/37017/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/37017/log/mongod.log storage: dbPath: /data/db_37017 journal: enabled: true processManagement: fork: true pidFilePath: /application/37017/pid/mongod.pid sharding: clusterRole: shardsvr replication: replSetName: "bg_two" net: port: 37017 bindIp: 127.0.0.1,192.168.88.2 EOF #37018 cat >/application/37018/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/37018/log/mongod.log storage: dbPath: /data/db_37018 journal: enabled: true processManagement: fork: true pidFilePath: /application/37018/pid/mongod.pid sharding: clusterRole: shardsvr replication: replSetName: "bg_two" net: port: 37018 bindIp: 127.0.0.1,192.168.88.2 EOF #37019 cat >/application/37019/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/37019/log/mongod.log storage: dbPath: /data/db_37019 journal: enabled: true processManagement: fork: true pidFilePath: /application/37019/pid/mongod.pid sharding: clusterRole: shardsvr replication: replSetName: "bg_two" net: port: 37019 bindIp: 127.0.0.1,192.168.88.2 EOF #env cat >/etc/profile.d/mongodb.sh<<EOF export PATH=/application/mongodb/bin:$PATH EOF source /etc/profile.d/mongodb.sh # chown -R mongo.mongo /data/{db_27017,db_27018,db_27019} chown -R mongo.mongo /data/{db_37017,db_37018,db_37019} chown -R mongo.mongo /application/mongo* chown -R mongo.mongo /application/2701* chown -R mongo.mongo /application/3701*
配置分片1
启动分片1的三个成员 su - mongo mongod -f /application/27017/conf/mongod.conf mongod -f /application/27018/conf/mongod.conf mongod -f /application/27019/conf/mongod.conf 将mongo shell连接到其中一个副本集成员 mongo --port 27017 使用rs.initiate()方法启动副本集 > rs.initiate( ... { ... _id: "bg_one", ... members: [ ... { _id: 0, host: "c2.heboan.com:27017" }, ... { _id: 1, host: "c2.heboan.com:27018" }, ... { _id: 2, host: "c2.heboan.com:27019" } ... ] ... } ... )
配置分片2
启动分片2的三个成员 mongod -f /application/37017/conf/mongod.conf mongod -f /application/37018/conf/mongod.conf mongod -f /application/37019/conf/mongod.conf 将mongo shell连接到其中一个副本集成员 mongo --port 37017 使用rs.initiate()方法启动副本集 > rs.initiate( ... { ... _id: "bg_two", ... members: [ ... { _id: 0, host: "c2.heboan.com:37017" }, ... { _id: 1, host: "c2.heboan.com:37018" }, ... { _id: 2, host: "c2.heboan.com:37019" } ... ] ... } ... )
三、连接mongos到分片集群
环境如下:
ip | hostname | 端口 |
192.168.88.3 | c3.heboan.com | 27017 |
安装脚本如下:
#!/bin/bash groupadd mongo useradd -g mongo mongo mkdir /application cd /data/tools tar zxf mongodb-linux-x86_64-rhel70-3.4.5.tgz -C /application/ cd /application ln -s mongodb-linux-x86_64-rhel70-3.4.5 mongodb mkdir -p mongodb/{conf,log,pid} mkdir /data/mongodb_data #27017 cat >/application/mongodb/conf/mongod.conf<<EOF systemLog: destination: file logAppend: true path: /application/mongodb/log/mongod.log processManagement: fork: true pidFilePath: /application/mongodb/pid/mongod.pid sharding: configDB: bagtree/c1.heboan.com:27017,c1.heboan.com:27018,c1.heboan.com:27019 net: port: 27017 bindIp: 127.0.0.1,192.168.88.3 EOF # cat >/etc/profile.d/mongodb.sh<<EOF export PATH=/application/mongodb/bin:$PATH EOF source /etc/profile.d/mongodb.sh # chown -R mongo.mongo /data/mongodb_data chown -R mongo.mongo /application/mongo*
启动mongos
su - mongo mongos -f /application/mongodb/conf/mongod.conf #注意,这里是mongos来启动,不是mongod
连接到mongos shell
mongo
将分片添加到集群
使用sh.addShard()方法将每个分片添加到集群,如果分片是副本集,请指定副本集的名称并指定集合的成员 mongos> sh.addShard("bg_one/c2.heboan.com:27017,c2.heboan.com:27018,c2.heboan.com:27019") mongos> sh.addShard("bg_two/c2.heboan.com:37017,c2.heboan.com:37018,c2.heboan.com:37019")
查看分片配置
mongos> use admin switched to db admin mongos> db.runCommand({listshards:1}) { "shards" : [ { "_id" : "bg_one", "host" : "bg_one/c2.heboan.com:27017,c2.heboan.com:27018,c2.heboan.com:27019", "state" : 1 }, { "_id" : "bg_two", "host" : "bg_two/c2.heboan.com:37017,c2.heboan.com:37018,c2.heboan.com:37019", "state" : 1 } ], "ok" : 1 }
四、集群已经做好了,但是mongos不知道如何切分数据,也就是分片键,在mongos中设置分片键要做两步
1、开启数据库分片功能(我这里开启数据库test)
mongos> sh.enableSharding("test")
2、指定集合中分片的分片键,这里我就指定_id字段
mongos> sh.shardCollection("test.boan", {_id:'hashed'})
查看效果
好了,至此我们的分片操作全部结束,接下来我们通过mongos向mongodb插入10w记录, mongos> use test mongos> for(var i=0;i<100000;i++){db.boandb.insert({"name":"heboan"+i, "age":i});} 然后通过printShardingStatus命令,查看mongodb的数据分片情况 mongos> db.printShardingStatus() 我们也可以在分片1的主节点查看数据的条数 bg_one:PRIMARY> db.boan.count() 49703 在分片2的主节点查看数据的条数 bg_two:PRIMARY> db.boan.count() 50297
使用密钥文件访问控制部署分片集群
一般来说,要为分片集群用户创建用户,是连接到mongos并分配分片的集群用户
但是,一些维护操作需要直接连接到分片集群的特定分片。要执行这些操作,我们必须连接到分片,并以分片本地管理用户身份进行身份验证
分片本地用户只存在于特定的分片中,并且只能用于特定分片的维护和配置
本教程需要创建分片的集群用户,也包括添加分片本地用户(可选)
以下过程涉及创建由一个mingos,config服务器和两个分片组成的分片集群
1、创建密钥文件
openssl rand -base64 756 > <path-to-keyfile> chmod 400 <path-to-keyfile>
2、分发密钥文件
将密钥文件复制到分片集群成员的每个服务器。为每个服务器使用一致的位置
确保运行mongod或mongos实例的用户可以访问密钥文件
####创建config服务器副本集
对于生产部署,部署具有至少上成员的配置服务器副本集
步骤1
在配置服务器副本集中启动每个mongod。使用keyFile设置强制执行内部身份验证和基于角色的访问控制
security: keyFile: <path-to-keyfile> sharding: clusterRole: configsvr replication: replSetName: <setname>
启动mongod
步骤2
通过localhost接口将mongo shell连接到其中一个config服务器实例
(localhost接口,因为没有用户创建才可用,本接口创建第一个用户后关闭)
步骤3
使用rs.initiate()方法启动副本集
rs.initiate( { _id: "<replSetName>", configsvr: true, members: [ { _id:0, host: "cfg1.example.net:27017"}, { _id:1, host: "cfg2.example.net:27017"}, { _id:2, host: "cfg3.example.net:27017"} ] } )
####创建分片副本集
对于生产部署,部署具有至少三个成员的分片服务器副本集
下面步骤包括分片添加本地用户的可选步骤,确保每个分片都有可用的用户执行分片级维护
步骤1
对副本集的每个成员执行访问控制
security: keyFile: <path-to-keyfile> sharding: clusterRole: shardsvr replication: replSetName: <replSetName> storage: dbPath: <path>
启动mongod
步骤2
通过localhost接口连接到其中一个副本集成员实例
步骤3
使用rs.initiate()方法启动副本集
rs.initiate( { _id: <replSetName>, members: [ { _id:0, host: "s1-mongo1.example.net:27017"}, { _id:0, host: "s1-mongo2.example.net:27017"}, { _id:0, host: "s1-mongo3.example.net:27017"} ] } )
rs.initiate()引发选举,并选举其中一名成员为主节点
连接到主节点之前,使用rs.status()定位主节点
步骤4
创建本地用户管理
以下示例创建heboan具有数据库admin的角色userAdminAnyDatabase
admin = db.getSiblingDB("admin") admin.createUser( { user: "heboan", pwd: "123.com", roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] } )
步骤5
认证为本地用户管理员
在mongo shell中,db.auth()用来认证
db.getSiblingDB("admin").auth("heboan", "123.com")
步骤6
创建分片本地集群管理员
db.getSiblingDB("admin").createUser( { "user": "ravi", "pwd": "456.com", "roles": [ { "role": "clusterAdmin", "db": "admin" } ] } )
####连接到mongos到分片集群
步骤1
mongos使用配置文件制定密钥文件
security: keyFile: <path-to-keyfile> sharding: configDB: <configReplSetName>/cfg1.example.net:27017,cfg2.example.net:27017...
启动mongos
mongos -f <path-to-config>
步骤2
通过localhost shell连接到其中一个mongos实例
步骤3
使用db.createUser()方法添加用户
admin = db.getSiblingDB("admin") admin.createUser( { user: "heboan, pwd: "123.com", roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] } )
步骤4
用db.auth()作为用户管理员进行身份验证以创建其他用户
步骤5:
创建用户进行集群管理
db.getSiblingDB("admin").createUser( { "user": "ravi", "pwd": "456.com", roles: [ { "role": "clusterAdmin", "db": "admin" } ] } )
步骤6
创建其他用户
####将分片添加到集群
我们连接到mongos必须使用集群管理员用户进行身份验证
要将每个分片添加到集群,请使用sh.addShard()方法。如果分片是副本集,请指定副本集名称并指定集合的成员
以下操作将单个分片副本集添加到集群
sh.addShard("<replSetName>/s1-mongo1.example.net:27017")
重复这些步骤,知道集群包含所有分片
####为数据库开启分片
我们连接到mongos必须使用集群管理员用户进行身份验证
sh.enableSharding("<database>")
####设置集合的分片键
我们连接到mongos必须使用集群管理员用户进行身份验证
sh.shardCollection("<database>.<collection>", {<key>: <direction>})