您的位置:首页 > 数据库 > Mongodb

MongoDB实战-分片概念和原理

2017-09-04 21:34 489 查看
1. 什么是分片

到目前为止,你都是把MongoDB当做一台服务器在用,每个mongod实例都包含应用程序数据的完整副本。就算使用了复制,每个副本也都是完整克隆了其他副本的数据。对于大多数应用程序而言,在一台服务器上保存完整数据集是完全可以接受的。但随着数据量的增长,以及应用程序对读写吞吐量的要求越来越高,普通服务器渐渐显得捉襟见肘了。尤其是这些服务器可能无法分配足够的内存,或者没有足够的CPU核数来有效处理工作负荷。除此之外,随着数据量的增长,要在一块磁盘或者一组RAID阵列上保存和管理备份如此大规模的数据集也变得不太现实。如果还想继续使用普通硬件或者虚拟硬件来托管数据库,那么这对这类问题的解决方案就是将数据库分布到多台服务器上,这种方法称之为分片。

为数众多的Web应用程序,知名的如Flicker和LiveJournal,都实现了手动分片,将负载分布到多台MySQL数据库上。在这些实现中,分片逻辑都寄生于应用程序上。要明白这是如何实现的,想象一下,假如你有很多用户,需要将Users表分布到多台数据库服务器上。你可以指定一台数据库作为元数据库。这台数据库包含每个用户ID(或者用户ID范围)到指定分片映射关系的元数据。因此,要查询一个用户实际涉及两次查询:第一次查询访问元数据库以获得用户的分片位置,第二次查询直接访问包含用户数据的分片。

对于这些Web应用程序而言,手动分片解决了负载问题,但气质并非无懈可击。最明显的问题就是迁移数据非常困难。如果单个分片负载过重,将其中的数据迁移到其他分片的过程完全是手动的。手动分片的第二个问题在于编写可靠的应用程序代码对于读写请求进行路由,并且将数据库作为一个整体进行管理,这也是非常困难的。最近也出现了管理手动分片的的框架,最著名的就是Twitter的Gizzard。

但正如那些手动分片数据库的人所说,要把事情做好并非易事。MongoDB中有一大块工作就是为了解决这个问题。因为分片是MongoDB的核心内容,所以用户无需担心在需求水平扩展时要自己设计外置分片框架。在处理困难的跨分片数据均衡问题时,这点尤为重要。这些代码并非那些大多数人在一个周末能够写出来的东西。

也许最值得一提的是MongoDB在设计时为应用程序提供了统一接口,无论是在分片前,还是在分片后。也就是说,在数据库需要转换为分片架构时,应用程序几乎无需改变。

2. 何时分片

这个问题的答案比你想的简单得多。我们之前已经说过把索引和工作数据集放在内存里时很重要的,这也是分片的主要原因。如果应用程序的数据集持续无限增长,那么迟早一天,内存会容纳不下这些数据。如果你正在使用亚马逊的EC2,那么这个阈值是68GB。或者你可以运行自己的硬件,并使用远高于68GB的内存,这样便能延后一段时间再做分片。但没有哪台机器内存时无限的,因此你早晚都会用到分片。

不可否认,还有一些其他的应对措施。举例来说,如果你有自己的硬件,而且可以将所有的数据都保存在固态硬盘上,那么可以增加数据内存比,而不会为性能带来负面影响。还有一种情况,工作集是总数据量中的一部分,这是可以使用相对较小的内存。另一方面,如果有特殊的写负载要求,那么可以在数据达到内存大小之前先进行适当的分片,原因是需要将负载分到多台机器上,以便能够获得想要的吞吐量。

无论哪种情况,对现有系统进行分片的决定都要基于以下几点--磁盘活动、系统负载以及最重要的工作集大小与可用内存的比例。

3. 分片的工作原理

要理解分片是如何工作的,你需要了解构成分片集群的组件,理解协调哪些组件的软件进程。

(1)分片组件

分片集群由分片、mongos路由器和配置服务器组成。如下图所示



分片

MongoDB分片集群将数据分布在一个或多个分片上。每个分片部署成一个MongoDB副本集,该副本集保存了集群整体数据的一部分。因为每个分片都是一个副本集,所以他们拥有自己的复制机制,能够自动进行故障转移。你可以直接连接单个分片,就像连接单独的副本集一样。但是,如果连接的副本集是分片集群的一部分,那么只能看到部分数据。

mongos路由器

如果每个分片都包含部分集群数据,那么还需要一个接口连接整个集群。这就是mongos。mongos进程是一个路由器,将所有的读写请求指引到合适的分片上。如此一来,mongos为客户端提供了一个合理的系统视图。

mongos进程是轻量级且非持久化的。它们通常运行与与应用服务器相同的机器上,确保对任意分片的请求只经过一次网络跳转。换言之,应用程序连接本地的mongos,而mongos管理了指向单独分片的连接。

配置服务器

如果mongs进程是非持久化的,那么必须有地方能持久保存集群的公认状态;这就是配置服务器的工作,其中持久化了分片集群的元数据,改数据包括:每个数据库,集合和特定范围数据的位置;一份变更记录,保存了数据在分片之间进行迁移的历史信息。配置服务器中保存的元数据是某些特定功能和集群维护是的重中之重。举例来说,每次有mongos进程启动,它都会从配置服务器中获取一份元数据的副本。没有这些数据,就无法获得一致的分片集群视图。该数据的重要性对配置服务器的设计和部署也有影响。

如上面结构图中所示,有三个配置服务器,但它们并不是以副本集的形式部署的。它们比异步复制要求更严格;mongos进程向配置服务器写入时,会使用两阶段提交。这能保证配置服务器之间的一致性。在各种生产环境的分片部署中,必须运行三个配置服务器,这些服务器都必须部署在独立的机器上以实现冗余。

(2) 核心分片操作

MongoDB分片集群在两个级别上分布数据。较粗的是以数据库为粒度的,在集群里新建数据库时,每个数据库都会被分配到不同的分片里。如果不进行别的设置,数据库以及其中的集合永远都会在创建它的分片里。 因为大多数的应用程序都会把所有的数据保存在一个数据库里,因此这种分布方式带来的帮助不大。你需要更细粒度的分布方式,集合的粒度刚好能够满足要求。MongoDB的分片是专门为了将单独的集合分布在多个分片里而设计的。

假设你正在构建一套基于云的办公软件,用于管理电子表格,并且要求将所有的数据都保存在MongoDB里。用户可以随心所欲地创建大量文档,每个文档都会保存为单独的MongoDB文档,放在一个spreadsheets集合里。随着时间的流逝,假设你的应用程序发展到了拥有100万用户。现在再想想那两个主要集合:users和spreadsheets。users集合还比较容易处理,就算有100万用户,每个用户文档1KB,整个集合大概也就1GB,一台机器就搞定了。但spreadsheets集合就大不一样了,假设每个用户平均拥有50张电子表格,平均大小是50KB,那么我们所谈论的就是1TB的spreadsheets集合。要是这个应用程序的活跃度很高,你会希望将数据放在内存里,要将数据放在内存里并且分布读写负载,就必须将集合分片。

分片一个集合

MongoDB的分片是基于范围的。也就是说分片集合里的每个文档都必须落在指定键的某个值范围里。MongoDB使用所谓的分片键(shard key)让每个文档在这些范围里找到自己的位置。(其他的分布式数据库里可能使用分区键partition key或分布键 distribution key来代替分片键这个术语)从假想的电子表格管理应用程序里拿出一个实例文档,这样能更好地理解分片键:

{
_id:ObjectId("4d6e9b89b600c2c196442c21")
filename:"spreadsheet-1"
updated_at:ISODate("2017-09-04T19:22:54.845z")
username:"banks"
data:"raw documnet data"
}
在对该集合进行分片时,必须将其中的一个或多个字段申明为分片键。如果选择_id,那么文档会基于对象ID的范围进行分布。但是,处于一些原因,你要基于username和_id声明一个复合分片键:因此,这些范围通常会表示Wie一系列用户名

现在你需要理解块(chunk)的概念,它是位于一个分片中的一段连续的分片键范围。举例来说,可以假设docs集合分布在两个分片A和B上,它被分成下表所示的多个块。每个块的范围都由起始值和终止值来标识。

起始值终止值分片
-无穷大abbotB
abbotdaytonA
daytonharrisB
harrisnorrisA
norris无穷大B
粗略扫视上表后,你会发现一个重要的、有些违反直觉的属性:虽然每个单独的块都表示一段连续范围的数据,但这些块能出现在任意分片上。关于块,第二个要点是它们是种逻辑上的东西,而非物理上的。换言之,块并不表示磁盘上连续的文档。从一定程度上来说,如果一个从harris开始到norris结束的块存在于分片A上,那么就认为可以在分片A的docs集合里找到分片键落在这个范围内的文档。这个集合里那些文档的排列没有任何必然关系。

拆分与迁移

分片机制的重点是块的拆分(spliting)与迁移(migration)

首先,考虑一下块拆分的思想。在初始化分片集群时,只存在一个块,这个块的范围涵盖了整个分片集合。那该如何发展到有多个块的分片集群呢?答案就是块大小达到某个阈值是就会对块进行拆分。默认的块的最大块尺寸时64MB或者100000个文档,先达到哪个标准就以哪个标准为准。在向新的分片集群添加数据时,原始的块最终会达到某个阈值,触发块的拆分。这是一个简单的操作,基本就是把原来的范围一分为二,这样就有两个块,每个块都有相同数量的文档。

请注意,块的拆分是个逻辑操作。当MongoDB进行块拆分时,它只是修改块的元数据就能让一个块变为两个。因此,拆分一个块并不影响分片集合里文档的物理顺序。也就是说拆分既简单又快捷。

你可以回想一下,设计分片系统时最大的一个困难就是保证数据始终均匀分布。MongoDB的分片集群是通过在分片中移动块来实现均衡的。我们称之为迁移,这是一个真实的物理操作。

迁移是由名为均衡器(balancer)的软件进程管理的,它的任务就是确保数据在各个分片中保持均匀变化。通过追踪各分片上块的数量,就能实现这个功能。虽然均衡的触发会随总数据量的不同而变化,但是通常来说,当集群中拥有块最多的分片与拥有块最少的分片的块数相差大于8时,均衡器就会发起一次均衡处理。在均衡过程中,块会从块较多的分片迁移到块较少非分片上,直到两个分片的块数大致相等为止。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  分片 mongos 块chunk