第五部分 架构篇 第二十章 MongoDB Sharding 架构( 片键选择)

1、选择片键

选择一个好的片键非常关键,如果选择了一个糟糕的片键,它可以立马或者在访问量变大时毁了你的应用程序,也有可能潜伏着,等待着,没准什么时候突然毁了你的应用程序。

另外一方面,如果你选择了一个好片键,只要应用程序还在正常运行,而且只要发现访问量提高就赶紧添加服务器,MongoDB就会确保一直正确地运行下去。

正如在前面所学的,片键决定了数据在集群中的分布情况,因此你会希望存在这样一个片键,它既能把读写分散开来,又能把正在使用的数据保持在一起,这些看似互相矛盾的目标在现实中却往往是可以实现的。

我们先挑片键的几个反面例子找茬,然后再拿几个较好的例子来琢磨一番,MongoDB的wiki上也有一页与选择片键相关且很不错的内容,可以看看。

1.1、小基数片键

一些人并不真正理解或者信任MongoDB自动分配数据的方式,所以他们总是沿着这么个思路来向:我有4个分片,所以应该用一个由4个可能的字段来做片键,这是一个非常糟糕的想法,为什么呢?

假设我们有一个存储用户信息的应用程序,每个文档有一个continent字段代表用户所在地区,其字段值可以是"Africa"、"Antarctica"、"Asia"、"Australia"、"Europe"、"North Ameraica"或者"south America",考虑到我们在每个大洲都有一个数据中心--或许不包括南极洲,并且想从人们所在当地的数据中心为其提供用户数据,我们决定按该字段进行分片。

集合开始于某个数据中心的一个分片的初始化块(负无穷,正无穷),所有的插入和读书都落在这一个块上,一旦它变的足够大,就会被划分成两个块(区间分别为(负无穷,“Europe”)和["Europe",正无穷)),这样一来,所有来自非洲(Africa)、南极洲(Antarctica)、亚洲(Asia)和澳洲(Australia)的文档都会被分到第一个块上,而所有来自欧洲(Europe)、北美(North America)或者南美(South America)的数据都会被分到第二块上,随着更多文档被添加进来,集合最终会变成7个块,如下所示:

每个大洋洲一个分片

然后呢?

MongoDB不能再进一步分割这些块了,块只能变得越来越大,虽然暂时不会出问题,但是当服务器磁盘空间开始主键消耗尽时问题就会付出睡眠了,除了买块更大的磁盘,你什么也做不了。

由于片键值数据量有限,因此这种片键成为小基数片键(low-cardinality shard key)。如果选择了一个基数很小的片键,到头来肯定会得到一堆即巨大又无法移动,还不能分割的块,它们会让你极为不块,这样的生活你懂的。

如果这么做是为了手动分配数据,那就不要再用MongoDB内置的分片机制了,否则它会和你一直抗争到底,当然不管则样你还是可以手动对集合进行分片,编写自己的路由器,并将读写路由到任何一台服务器上,只不过选择一个号的片键并让MongoDB来替你做这一切更加容易一些。

  • 适用的键

这个规则适用于任何取值个数有限的键,一定要记得,如果在某集合中一个键有N个值,那就只能有N个数据块,因此也只能有N个分片。

如果打算采用小基数片键的原因是需要在那个字段上进行大量查询,请使用组合片键(一个片键包含两个字段),并确保第二个字段有非常多不同的值可以供MongoDB用来进行分割。

  • 例外

如果一个集合有生命周期(比如,每周都创建一个新的集合而且你知道,在一周时间里数据量不会接近任何分片的最大容量),就可以选择这个生命周期为片键。

  • 数据中心感知

这个例子不仅仅是关于选择小基数片键的,还与在MongoDB分片机制中添加数据中心感知(data-center awareness)支持的尝试有关,到目前为止,分片上不支持数据中心感知,如果对此感兴趣可以查看或者相关的问题投票。

靠使用者自己实现的问题在于其扩展性并不是很好,如果你的应用最近在日本很流行怎么办?你可能会想添加第二个分片来应付亚洲地区的访问量。

但是你打算如何迁移数据呢?一个数据块增长到好几GB大,你就没法迁移它了,而且也不能再分割块,因为整个块就只有一个片键值,由于片键值无法更新,因此也不可能通过更新所有文档来使用一个更独特的片键值,可以删除每个文档,更新片键值,然后再把它重新保存回去,但是对于大型数据库而言那并不是一个能迅速完成的操作。

你所能做的最好的事情就是在插入文档时开始用Asia,Japan替代简单的Asia,这样一来会有一批旧文档,其片键值应该Asia,Japan但却是Asia,因此应用程序逻辑就不得不同时支持两种情况,另外一旦开始拥有更细粒度的块,就不能保证MongoDB还会把它们放在你所期望的地方(除非关闭平衡器并手动处理所有事情)。

数据中心感知对于大型应用非常重要,而且它对MongoDB的开发者有很高的优先级,在这期间选择一个小技术片键绝不对是一个好的解决方案。

1.2、升序片键

从RAM中读取数据要比从磁盘中读取快,所以目标是尽可能多地访问内存中的数据,因此,如果有些数据总是被一起访问,我 们就希望片键能够把它们保持到遗弃,对大部分应用程序而言,新数据被访问的次数总比老数据多,所以人们往往会尝试使用诸如时间戳或者ObjectId一类的字段来做片键,但是这并不像它们所期望的那样可行。

比如说我们有一个类似微博的服务,其中每个文档都包含一条短信息、发送人以及发送时间,我们按发送时间字段来分片,取值为自公元元年起经过的秒数。

和往常一样,还是从一个数据库(负无穷、正无穷)开始,全部插入都会落在这个分片上至到它分裂成两个块,由于是从片键中点把块分开来的,所以在我们分隔块的那一刻,时间戳很可能已经远大于中间值了,这意味着再往后所有的插入都会落到第二个块上,不会再有插入操作命中第一块,一旦第二个块填满了,它就会分裂成为另外两个块,如[1294516901,1294930163)和[1294930163,正无穷)两个块,但是因为从现在起时间都在1294930163之后,所有新的插入都会被添加到区间为[1294930163,正无穷)的块上,这个模式会持续下去,所有的数据总是被添加到最后,一个数据块上,即所有数据都会被添加到一个分片上,这个片键创造了一个单一且不可分散的热点。

  • 适用的键

这条规则适用于任何升序排列的键值,而并不必须是时间戳,其他例子包括ObjectId、日期、自增主键,只要键值趋向于无穷大,你就会面临这个问题。

  • 例外

基本上,这种片键总是一个坏主意,因为它导致热点必须存在,如果访问量不大且用一个分片就能承受所有读写,那还行的通,当然如果遇到一个访问量尖峰或者应用开始变得更受欢迎,那它会终止停止工作并且难以修复。

除非你非常清楚自己在干什么,否则不要使用升序片键,肯定还有更好的片键存在,应该避免使用这一个。

1.3、随机片键

有时为了避免热点,人们会选择一个取值随机的字段来分片,采用这种片键一开始还不错,但是随着数据量越变越大,它会变得越来越慢。

假设我们在分片集合中存储照片的缩略图,每个文档都包含了照片的二进制数据、二进制数据的MD5散列图值,以及一段描述、拍照时间和拍照人,我们决定在MD5散列值上做分片。

随着集合的增长,我们最终会得到一组均匀分布各分片的数据块,目前为止一切顺利,现在,假设我们非常忙而分片2上的而一个块填满并分裂了,配置服务器注意到分片2比分片1多出了10个块并判定应该抹平分片间的差距,这样MongoDB就需要随机加载5个块得数据到内存中并将其发送给分片1,考虑到数据序列的随机性。一般情况下这些数据可能不会出现在内存中,所以此时的MongoDB会给RAM带来更大的压力,而且还会引发大量磁盘IP。

除此之外,片键上必须有索引,因此如果选择了从不依据它进行的随机键,基本上可以说是浪费了一个索引,另外考虑到每增加一个索引都会让写操作变得更慢,所以保持索引数量尽可能低也是非常重要的。

1.4、好片键

我们真正需要的是一种将访问模式也考虑进去的方案,如果应用会规律地访问25GB的数据,我们就希望所有的分割和歉意都发生在这25GB数据上,而不是随机访问数据以至于不断地有新数据呗从磁盘中复制到内存里。

因此我们希望能找到这样一个片键,它具备有良好的数据局部性特征,但有不会因为太局部而导致热点出现。

  • 准升序键加搜索键

许多应用访问新数据比老数据更频繁,所以我们希望数据大致上按照时间排序,但是同时也要均匀分布,这样一来既能把我们正在读写的数据保持在内存汇总,又可以使负载均衡地分散在集群中。

举个例子,比如说有个分析程序,用户会定期通过它访问过去一个月的数据,而我 们希望能尽可能保持数据易于使用,因此可以在{month:1,user:1}上分片,其中month是一个粗粒度的升序字段,即每个月它都会有一个更新更大的值,user适合作为第二个字段,因为我们会经常查询某个特定用户的数据。

  • 常见问题

search字段可不可以也是升序字段?

不可以,如果是,则该片键会降级成一个升序片键,进而使你同堂面临普通升序键带来的热点问题。

search字段应该是什么?

search字段最好是应用程序可以用于查询的东西,比如用户信息(比如上面的例子)、文件名字段或者GUID等,它应该具备非升序、分布随机且基数适当的特点。

时间: 2024-10-13 10:49:34

第五部分 架构篇 第二十章 MongoDB Sharding 架构( 片键选择)的相关文章

第五部分 架构篇 第二十一章 MongoDB Sharding 架构(实践)

这是一种将海量的数据水平扩展的数据库集群系统,数据分别存储在sharding的各个节点上,使用者通过简单的配置就可以很方便地构建一个分布式MongoDB集群. MongoDB的数据分块成为chunk,每个chunk都是Collection中一段连续的数据记录,通常最大尺寸是200MB,超出则生成新的数据块. 要构建一个MongoDB Sharding Cluster需要以下三个角色: Shard Server 即存储实际数据的分片,每个Shard可以使一个mongod实例,也可以使一组mongo

第五部分 架构篇 第十七章 MongoDB Sharding 架构(理解块)

1.如何创建块 在前面的已经了解MongoDB分片架构中块的基本概念,那么在此来讲述如何创建块?当你决定分配数据时,必须为块区间选择一个键(前面我们一直在用username)这个键叫做片键(shard key),片键可以是任意字段或者字段的组合,比如如下文档中: <pre name="code" class="java"><pre name="code" class="java">{"user

第四部分 性能篇 第十章 MongoDB 索引

1.简介 MongoDB提供了多样性的索引支持,索引信息被保存在system.indexes中,且默认总是为_id创建索引,它的索引使用基本和MySQL的关系型数据库一样,其实可以这样说说,索引是凌驾于数据存储系统之上的另外一层系统,所以各种结构迥异的存储都有相同或者相似的索引实现及使用接口并不足为奇. 2.基础索引 在字段age上创建索引,1(升序),-1(降序) <span style="font-family:SimHei;font-size:14px;">db.us

第五部分 架构篇 第十九章 MongoDB Sharding 架构( mongos)

1.mongos mongos是用户和集群间的交互点,其职责是隐藏分片内部的复杂性并向用户提供一个简洁的单服务器接口,这个抽象层中也存在一些缝隙,不过大多数情况下mongos允许你把一个集群当做一台服务器. 使用集群时,应该连接一个mongos并向它发送所有的读写操作,无论如何,你都不应该直接访问分片(但如果想的话能做到). mongos会将所有用户请求转发到恰当的分片上,如果用户插入一份文档,mongos会查看文档的片键,对照数据块,并把文档发送到持有相应块的分片上. 举个例子,比如说我们要插

第五部分 架构篇 第十六章 MongoDB Sharding 架构(理解分片)

1.简介 这是一种将海量的数据水平扩展的数据库集群系统,数据分表存储在sharding的各个节点上,使用者通过简单的配置就可以很方便地构建一个分布式的MongoDB集群. 那么首先我们应该理解何为分片(sharding)以及它的基本工作模式. 2.什么是分片 分片(sharding)是MongoDB用来将大型集合分割到不同服务器(或者说一个集群)上所采用的方法,尽管分片起源于关系型数据库分区,但它(像MongoDB大部分方面一样)完全是另外一回事. 和你可能使用过的任何分区方案相比,MongoD

第五部分 架构篇 第十八章 MongoDB Sharding 架构(平衡)

1.平衡简介 如果存在多个可用的分片,只要块得数量足够多,MongoDB就会把数据迁移到其他分片上,这个迁移过程叫做平衡(balancing),由叫做平衡器(balancer)的进程负责执行. 2.平衡工作流程 平衡器会把数据块从一个分片挪到另外一个分片上,其优点在于自动化,即你无需担心如何保持数据在分片间的均匀分布,这项工作已经由平衡器替你搞定,不过这也是它的缺点,因为自动意味着如果你不喜欢塔做负载均衡的方式,那只能算你不走运,如果不想让某个块存在于分片3上,你可以手动移动到分片2上,但是平衡

第五部分 架构篇 第十四章 MongoDB Replica Sets 架构(自动故障转移/读写分离实践)

说明:该篇内容部分来自红丸编写的MongoDB实战文章. 1.简介 MongoDB支持在多个机器中通过异步复制达到故障转移和实现冗余,多机器中同一时刻只有一台是用于写操作,正是由于这个情况,为了MongoDB提供了数据一致性的保障,担当primary角色的服务能把读操作分发给Slave(详情请看前两篇关于Replica Set成员组成和理解). MongoDB高可用分为两种: Master-Slave主从复制:只需要在某一个服务启动时加上-master参数,而另外一个服务加上-slave与-so

第五部分 架构篇 第十二章 MongoDB Replica Sets 架构(简介)

1.Replication简介 MongoDB Replication即是在多个服务器中同步复制数据,数据复制的目的为提供冗余和提高数据的可用性,数据复制的操作即在不同的数据库服务器上保存多份复制的数据集,以此来避免单机故障进而丢失数据,复制机制也允许你恢复硬件故障和服务中断,进而起到数据的容灾和备份作用. 在MongoDB中,一个复制集就是一组mongod实例,一个mongod实例作为primary也就是所谓的master服务,接收所有的写操作,其他的mongod实例作为secondaries

MongoDB Sharding Cluster分片集群

MongoDB Sharding Cluster 第1章 分片技术简述: sharding是MongoDB用来将大型集合分割高不同服务器上所采用的方法,尽管分片起源于关系型数据库分区,但MongoDB分片完全又是另一回事 和mysql分区方案相比,MongoDB的最大区别在于它几乎能自动完成所有事情,只要告诉MongoDB要分配数据,它就能自动维护数据在不同的服务器之间的均衡 1.1 分片的目的: ?  垂直扩展:增加更多的cpu和存储资源来扩展容量,也属于硬件扩展 ?  水平扩展:将数据集分布