目录
MongoDB学习(六):集群之复制集 提到,复制集(副本集)在数据量过大,以至于无法全部装载到内存中时,分布式架构也很难提升系统性能。MongoDB的方案是分片。
1 概念
所谓分片,就是将大数据集分割为较小的数据集的过程。之前使用MongoDB的过程中,无论是单节点 or 复制集,每个服务器都保存了全量数据。这就带来两个问题:首先,随着数据增长,单台服务器的处理能力逐渐到达极限(内存无法装下所有数据);其次,由于数据过大,新节点进行初始化的全量同步时,很可能出现卡死的问题(I/O导致CPU不足)。
分片集群的相关组件如下:
- 分片(Shards):每个分片存储了完整数据的一部分,实际是一个单节点服务器or复制集。只有路由和管理员可以直接访问分片。
- 路由(Routers):存储了集群元数据,用来查找操作的数据在哪个分片上。
- 配置服务器(Config Servers):持久化集群元数据,例如每个分片存储了哪部分数据。
另一个问题是,MongoDB分片的粒度。在MongoDB中,有数据库、集合、文档等结构,实际上,在集合和文档之间,还存在一中名为“块”的概念,指的是根据一个或多个字段(称为分片键)的值聚集的一系列文档。分片是在数据库和块这两级上进行的。
实际上,数据库级的分片是手动的,即我们必须保证每个分片上的每个数据库所包含的数据都大致均衡。如果有某个数据库存放了过多数据,就需要对其进行分库。
在块上的分片则是自动进行的,根据指定的分片键的值,将整个值域分割为多个区间,每个区间映射到一个分片上。例如一共分两个片,分片键的值以A~Z开头,那么可能A~E分到分片1,F~J分到分片2……
2 建立分片集
建立分片集的步骤如下,这里以两个分片为例:
1)启动mongod、mongos进程
首先,需要为每个分片创建复制集,步骤参见MongoDB学习(六):集群之复制集
以分片A为例:
root@Ubuntu:~# mkdir -p ~/rs/a-1
root@Ubuntu:~# mkdir -p ~/rs/a-2
root@Ubuntu:~# mkdir -p ~/rs/a-3
root@Ubuntu:~# mongod --sharedsvr --replSet shard-a --dbpath ~/rs/a-1 --port 30000 --logpath ~/rs/a-1.log --fork
root@Ubuntu:~# mongod --sharedsvr --replSet shard-a --dbpath ~/rs/a-2 --port 30001 --logpath ~/rs/a-1.log --fork
root@Ubuntu:~# mongod --sharedsvr --replSet shard-a --dbpath ~/rs/a-3 --port 30002 --logpath ~/rs/a-1.log --fork
然后连接端口为30000的服务器,使用rs.initiate()进行复制集初始化,添加30001服务器为从节点,30002服务器为仲裁节点。
分片B如法创建。然后创建配置服务器:
root@Ubuntu:~# mkdir -p ~/rs/config-1
root@Ubuntu:~# mkdir -p ~/rs/config-2
root@Ubuntu:~# mkdir -p ~/rs/config-3
root@Ubuntu:~# mongod --configsvr --dbpath ~/rs/config-1 --port 30200 --logpath ~/rs/config-1.log --fork --nojournal
root@Ubuntu:~# mongod --configsvr --dbpath ~/rs/config-1 --port 30201 --logpath ~/rs/config-2.log --fork --nojournal
root@Ubuntu:~# mongod --configsvr --dbpath ~/rs/config-1 --port 30202 --logpath ~/rs/config-3.log --fork --nojournal
注意配置服务器不要带--replSet选项。
然后启动mongos:
root@Ubuntu:~# mongos --configdb localhost:30200,localhost:30201,localhots:30202 --logpath ~/mongos.log --fork --port 30300
然后就可以连接mongos服务器进行配置了。
2)配置集群
可以使用sh.help()查询可用的命令。
主要用到的方法是sh.addShard():
> sh.addShard("shard-a/localhost:30000,localhost:30001")
30002是仲裁节点,不存储数据,所以不用添加。shard-b添加方式类似。
然后就可以使用db.getSiblingDB("config").shards.find()查询当前所有的分片。
以下方法可以查看块的数量:
> use config
> db.chunks.count()
假如传入{"shard":"shard-a"}作为参数,则是查询分片A上的块数。
块信息实际存放在chunks集合中,可以使用find()方法查看其信息,如分片键范围等。
3)分配集合
假设要在test数据库上启用分片,则:
> sh.enableSharding("test")
可以使用db.getSiblingDB("config").databases.find()查询配置结果。
现在可以对集合进行分片了,假设集合名为user,以username和_id作为分片键:
> sh.shardCollection("test.user",{username:1,_id:1})
可以使用db.getSiblingDB("config").collections.find()查询配置结果。
假如要删除一个分片,可以使用runCommand函数,以下命令删除了分片A:
> use admin
> db.runCommand({removeshard:"shard-a/localhost:30000,localhost:30001"})
块分片不是固定的,可能会出现块转移的情况(原来的块膨胀,导致发生了块分裂,分裂后的块就需要进行迁移),可以用以下命令查询:
> use config
> db.changelog.find({what:"moveChunk.commit"})
可以使用sh.splitAt("test.user",{username:"somename",_id:ObjectId("someid")})在指定点切分块
可以使用sh.moveChunk("test.user",{username:"somename"},"shardB")将某个块转移到分片B
3 客户端连接
假如mongos启动在27017端口上,可以直接MongoClients.create():
MongoClient mongoClient = MongoClients.create();
否则传入url即可,字符串 或 ConnectionString均可:
MongoClient mongoClient = MongoClients.create("mongodb://localhost:30300");
也可以以ClusterSettings+MongoClientSettings的形式传入url:
ClusterSettings clusterSettings = ClusterSettings.builder()
.hosts(asList(new ServerAddress("localhost:30300")))
.build();
MongoClientSettings settings = MongoClientSettings.builder()
.clusterSettings(clusterSettings)
.build();
MongoClient mongoClient = MongoClients.create(settings);
如果有多个mongos实例,只要用逗号隔开每个url即可:
MongoClient mongoClient = MongoClients.create("mongodb://host1:27017,host2:27017");
ClusterSettings+MongoClientSettings的形式更简单,因为hosts方法里传入的是个List,只要往List中多放几个元素就行。
剩下的操作和单节点没什么区别了。