数据库系统之MongoDB Sharding

MongoDB的Sharding是解决大数据存储和高吞吐量需求的一种策略,通过分布式数据库架构将数据分散到多个服务器上。分片主要出于存储分配和负载分配的考虑,当数据库容量或处理能力不足时,可以采用Sharding。配置Sharding涉及启动配置服务器、数据分片和 mongos 进程。在实际生产环境中,Sharding需要根据网络使用、磁盘和内存资源来决策。操作步骤包括创建和初始化配置及数据分片服务器,然后启用数据库和集合的分片。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础知识

Sharding是将一个大型数据集划分为更小、更易于管理的片段的过程。

Shared nothing架构是一种分布式计算架构,其中每个计算节点不与其他节点共享数据。

在数据库系统中,它被称为sharding(shared nothing)。

在出现以下三种情况时我们要考虑使用Sharding:

  1. 大量的数据和更大的读/写吞吐量需求使得商品数据库服务器不够。

  2. 数据库服务器不能寻址足够的RAM,或者它们可能没有足够的CPU内核来有效地处理工作负载。

  3. 由于数据量大,在一个磁盘或RAID存储(廉价磁盘的冗余阵列)上存储和管理备份是不实际的。

这些问题的解决方案是将数据库和数据库处理分布到多个服务器上。

mongodb中实现这一功能的方法称为sharding。由于管理和性能开销,sharding使数据库系统变得复杂。

为什么要Sharding?

分片有两个主要原因:

  1. 存储分配

  2. 负荷分配

如果对存储容量的监视显示,在某个时刻,数据库应用程序需要的存储量超过了可用的存储量,并且不可能添加更多的存储量,那么sharding是最好的选择。

Mongodb监控意味着运行db.stats()和db.collection.stats()在mongo shell中以获取关于当前数据库的存储使用情况和其中的集合的统计信息,

负载意味着CPU和RAM利用率、I/O带宽、客户端请求使用的网络传输=>响应时间。

如果在某个时刻响应时间不符合客户的期望,那么它会触发一个shard决策。

Shard的决定取决于网络使用情况、磁盘使用情况、CPU使用情况和RAM使用情况。

分配

MongoDB的数据粒度分为四个级别:

  1. Document: MongoDB中最小的数据单位;文档表示系统中的单个对象(如关系数据库中的一行)。
  2. Chunk: 按文件中的值聚集的一组文档;块是一个仅存在于分片设置中的概念;块是根据文档的键或键集的值(称为分片键)通过对文档进行逻辑分组而创建的。
  3. Collection: 数据库中的一组命名的文档;集合允许用户将数据库分离为对应用程序有意义的逻辑分组。
  4. Database:一套文档集合;数据库名称和集合名称的组合在整个系统中是唯一的,通常称为名称空间。

数据在分片集群中的分布方式如下:
5. 在整个数据库的级别上,每个数据库及其所有集合都放在自己的分片上。
6. 在分区或集合块的级别上,集合中的文档本身根据文档中的一个键或一组键(shard key)的值分布在多个分片上。

Sharding模拟

建立分片集群的过程包括三个步骤:

  1. 通过生成组成集群的所有mongod和mongos进程来启动mongod和mongos服务器。

  2. 通过更新配置来配置集群,使得副本集被初始化,碎片被添加到集群中,并且节点能够彼此通信。

  3. sharding collections,以便它可以分布在多个切分上。

启动终端并处理以下shell命令来创建配置服务器(只有一个):

mkdir conf1
mkdir conf2

在一个新的终端窗口中处理以下命令:

mongod --configsvr --replSet conf --dbpath conf1 --port 4001

在另一个新的终端窗口中处理以下命令:

mongod --configsvr --replSet conf --dbpath conf2 --port 4002

接着在一个新的终端窗口中处理以下命令:

mongo -port 4001
rs.initiate()
rs.conf()
rs.add("localhost:4002")
rs.status()
exit
mongo --port 4002
rs.slaveOk()
exit

在新的终端窗口中处理以下命令,以创建数据碎片(两个复制集,每个复制集由两个服务器组成):

mkdir data1-1
mkdir data1-2

在一个新的终端窗口中处理以下命令:

mongod --shardsvr --replSet data1 --dbpath data1-1 --port 4003

在另一个新的终端窗口中处理以下命令:

mongod --shardsvr --replSet data1 --dbpath data1-2 --port 4004

在一个新的终端窗口中处理以下命令:

mongo -port 4003
rs.initiate()
rs.conf()
rs.add("localhost:4004")
rs.status()
exit
mongo --port 4004
rs.slaveOk()
exit

在新的终端窗口中处理以下命令:

mkdir data2-1
mkdir data2-2

在新的终端窗口中处理以下命令:

mongod --shardsvr --replSet data2 --dbpath data2-1 --port 4005

在新的终端窗口中处理以下命令:

mongod --shardsvr --replSet data2 --dbpath data2-2 --port 4006

在新的终端窗口中处理以下命令:

mongo -port 4005
rs.initiate()
rs.conf()
rs.add("localhost:4006")
rs.status()
exit
mongo --port 4006
rs.slaveOk()
exit

在新的终端窗口中处理以下命令以启动mongos:

mongos --configdb conf/localhost:4001,localhost:4002 --port 4000

在新的终端窗口中处理以下命令,在端口4003和4005上的复制集上创建碎片:

mongo --port 4000
sh.addShard("data1/localhost:4003")
sh.addShard("data2/localhost:4005")

列出数据碎片:

db.getSiblingDB("config").shards.find()

启用数据库分片:

sh.enableSharding("test")
db.getSiblingDB("config").databases.find()

将集合插入到shard中:

use test
db.testcol.insert({"fname":"James", "lname":"Bond", "shard-key":"000"})
db.testcol.insert({"fname":"James", "lname":"Bond", "shard-key":"001"})
db.testcol.insert({"fname":"James", "lname":"Bond", "shard-key":"002"})
db.testcol.insert({"fname":"James", "lname":"Bond", "shard-key":"003"})
db.testcol.insert({"fname":"James", "lname":"Bond", "shard-key":"004"})
db.testcol.insert({"fname":"James", "lname":"Bond", "shard-key":"005"})
db.testcol.insert({"fname":"James", "lname":"Bond", "shard-key":"006"})
db.testcol.insert({"fname":"James", "lname":"Bond", "shard-key":"007"})

在“shard-key”上创建索引:

db.testcol.createIndex({"shard-key":1})

Shard a collection:

sh.shardCollection("test.testcol", {"shard-key": 1})

获得统计数据:

db.test_collection.stats()

输出结果如下:

"shards" : {
	"data2" : {
		"ns" : "test.testcol",
		"size" : 592,
		"count" : 8,
		"avgObjSize" : 74,
		"storageSize" : 32768,

创建大型集合“test_collection”:

use test
var bulk = db.test_collection.initializeUnorderedBulkOp();
people = ["Marc", "Bill", "George", "Eliot", "Matt", "Trey", "Tracy",
"Greg", "Steve", "Kristina", "Katie", "Jeff"];
for(var i=0; i<1000000; i++){
	user_id = i;
	name = people[Math.floor(Math.random()*people.length)];
	number = Math.floor(Math.random()*10001);
	bulk.insert( { "user_id":user_id, "name":name, "number":number });
}
bulk.execute();

在“user_id”上创建索引:

db.test_collection.createIndex({user_id:1})

通过“user_id”分片一个集合:

sh.shardCollection("test.test_collection",{"user_id":1}) 

找到分片状态:

sh.status()
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
					config.system.sessions
						shard key: { "_id" : 1 }
						unique: false
						balancing: true
						chunks:
								data1 1
						{ "_id" : { "$minKey" : 1 } } -->>
						{ "_id" : { "$maxKey" : 1 } } on : data1 Timestamp(1, 0)
....

Production

理论上,要启动示例MongoDB shard集群,你必须启动总共9个进程(每个副本集有3个mongod,再加上3个配置服务器。
实际上在实践中不需要这么多过程。

复制的mongod是shard集群中资源最密集的进程,必须给它们自己的机器。

副本集仲裁程序的开销很小,而且它们不需要自己的服务器。

配置服务器存储的数据量相对较小。

这意味着那个配置服务器也不一定需要自己的机器。

References

  1. Banker K., Bakkum P., Verch S., Garret D., Hawkins T., MongoDB in Action, 2nd ed., Manning Publishers, 2016.
  2. MongoDB Manual, Sharding https://docs.mongodb.com/v3.6/sharding/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值