首页 > [mongodb翻译]选择合适的shard key

[mongodb翻译]选择合适的shard key

为一个集合(collection)选择合适的shard key非常重要。如果这个集合非常庞大,那么将来再来修改shard key将会很困难。如有任何疑问请到论坛或者IRC寻求帮助。

 

示例文档

view plain
  1. {  
  2. server : "ny153.example.com" ,  
  3. application : "apache" ,  
  4. time : "2011-01-02T21:21:56.249Z" ,  
  5. level : "ERROR" ,  
  6. msg : "something is broken"  
  7. }  



基数(cardinality)

一个集合中的所有数据会被分裂为多个数据块(chunk),一个数据块包含了某个范围shard key的数据。请选择合适的shard key,否则你将会得到很大的不能分裂的数据块。

使用上面的日志例子,如果你选择了:

view plain
  1. {server:1}  



作为shard key,那么所有关于某个server的数据会存在一个数据块中,你可以很容易想到一个server的数据会超过64MB(默认数据块大小)。如果shard key是:

view plain
  1. {server:1,time:1}  



你可以将单个服务器的数据分裂到毫秒级。只要你的单个服务器不会有200MB/S,就不会有不能分裂的数据块。

保持数据块在一个合适大小是很重要的,这样数据就可以在集群中均衡分布并且移动一个数据块代价也不会太大。

 

水平写

使用分片的一个主要原因就是分发写操作。为了达到这个目的,写操作应当尽可能的分散到不同的数据块。

再次使用前面的例子,选择:

view plain
  1. { time : 1 }  



作为shard key,会导致所有的写操作都集中到最新的数据块中。如果shard key选择:

view plain
  1. {server:1,application:1,time:1}  



那么每个服务器:应用映射都会被写到不同的地方。如果这里有100种服务器:应用映射,和10台服务器,那么每台服务器将会分配约1/10的写操作。

需要注意的是,由于ObjectId中很重要的一部分是基于时间生成的,使用ObjectId作为shard key等同于直接使用时间值。

 

查询隔离

另外一个考虑就是任何一个查询需要分发到多少个shard。理想情况下,一个查询操作经mongos直接分发到拥有期望数据的mongod。如果你知道大部分的查询使用了那些条件,那么使用这些条件属性作为shard key可以提高很多效率。

即使查询条件中没有包含shard key,查询依然可以工作。由于mongos不知道哪个shard拥有期望的数据,mongos会将这个请求顺序分发到所有的shard中,这会增加响应时间和网络数据流量及服务器负载。

 

排序

如果查询中包含了排序请求,这个查询请求会同以前没有排序要求时一样分发到需要的shard中。每一个shard执行查询然后在本地做排序(如果有 索引的话会使用索引)。mongos会合并从shard返回的已经排序好的结果,然后返回合并后的数据给客户端。这样的话,mongos只需要做少量的工 作和很少的RAM。

可靠性

分片的一个重要方面就是如果整个shard不能访问了(即使使用了可靠的复制组),这将会对整个系统带来多大的影响。

例如你有一个类似于twitter的系统,评论记录类似于:

view plain
  1. {  
  2. _id: ObjectId("4d084f78a4c8707815a601d7"),  
  3. user_id : 42 ,  
  4. time : "2011-01-02T21:21:56.249Z" ,  
  5. comment : "I am happily using MongoDB",  
  6. }  



由于系统对写操作是非常敏感的,如果你希望将写操作分散到各个服务器中,你需要使用"_id"或者"user_id"作为shard key。"_id"可以给你较好的颗粒度和写扩散性。但是一旦某个shard宕机了,它将会影响到几乎所有的用户(有些数据丢失了)。如果你使 用"user_id"作为shard key,此时一小部分用户会受到影响(比如在5个shard组成的集群中,这个百分比是20%),即使这些用户再也看不到他们的任何数据了。

 

索引最佳化

正如前面章节关于索引的描述,通常经常对一部分索引做读/更新会带来较好的性能表现,这是因为这“活跃”的部分可以大多数时间都驻留在RAM。前面 介绍的shard key虽然可以将写操作分散到各个shard中,但是他们还是属于每个mongod的索引的。作为替代,将时间戳分解为某种形式并作为shard key的前缀可以带来一些好处,这样可以减小经常访问的索引大小。

例如你有一个图片存储系统,图片记录类似于:

view plain
  1. {  
  2. _id: ObjectId("4d084f78a4c8707815a601d7"),  
  3. user_id : 42 ,  
  4. title: "sunset at the beach",  
  5. upload_time : "2011-01-02T21:21:56.249Z" ,  
  6. data: ...,  
  7. }  



你可以定制一个包含了上传时间的月份和唯一的标示符(如ObjectId,数据的md5值等)的_id来替代默认的_id,新的记录类似于:

view plain
  1. {  
  2. _id: "2011-01_4d084f78a4c8707815a601d7",  
  3. user_id : 42 ,  
  4. title: "sunset at the beach",  
  5. upload_time : "2011-01-02T21:21:56.249Z" ,  
  6. data: ...,  
  7. }  



使用它作为shard key,同时也是访问一个文档使用的_id.它可以很好的将写操作分散到所有shard中。并且它减小了大部分查询需要访问的索引的大小。

进一步注释:

  •  在每个月的开始,只有一台shard被访问知道均衡器开始分裂数据块。为了避免这种潜在的低性能和数据迁移,建议在时间前面增加了一个范围值(比如5或者更大的范围值如果你有5台服务器)。
  • 更进一步的改善,你可以将用户名(user id)纳入到图片id中,这样可以保持同一个用户的文档都存储到同一个shard中,例如:"2011-01_42_4d084f78a4c8707815a601d7"

 

GirdFS

根据不同的需要,这里有多种不同的方法可以对GridFS进行分片。一种基于已经存在的索引的分片方法是:

  • "files"集合不做分片。所有的文件记录都将存储在一个shard中,强烈建议保证该shard非常可靠(使用至少3节点的复制组)。
  • "chunks" 集合应当使用索引"files_id:1"进行分片。由驱动创建的已经存在的"files_id,n"索引不能被使用在"files_id"上面进行分片 (这是一个限制,将来会被fix)。所以你需要单独为"files_id"创建一个索引。使用"files_id"进行分片开始保证某个文件存储到同一个 shard中,这样"filemd5"命令就可以工作了。运行命令:
    view plain
    1. > db.fs.chunks.ensureIndex({files_id: 1});  
    2. > db.runCommand({ shardcollection : "test.fs.chunks", key : { files_id : 1 }})  
    3. { "collectionsharded" : "test.fs.chunks", "ok" : 1 }  

files_id默认使用ObjectId,因此files_id是递增的,所有新的GridFS数据块会被送往同一个shard。如果你的写负载太大以至于单台服务器无法应付,你可能需要考虑使用其他的shard key或者使用其他的值作为file的_id.

转载于:https://www.cnblogs.com/xinghebuluo/archive/2011/11/22/2266204.html

更多相关:

  • 本文是西门子开放式TCP通信的第2篇,上一篇我们讲了使用西门子1200PLC作为TCP服务器的程序编写,可以点击下方链接阅读:【公众号dotNet工控上位机:thinger_swj】基于Socket访问西门子PLC系列教程(一)在完成上述步骤后,接下来就是编写上位机软件与PLC之间进行通信。上位机UI界面设计如下图所示:从上图可以看出...

  • 我有一个大型数据集,列出了在全国不同地区销售的竞争对手产品。我希望通过使用这些新数据帧名称中的列值的迭代过程,根据区域将该数据帧分成几个其他区域,以便我可以分别处理每个数据帧-例如根据价格对每个地区的信息进行排序,以了解每个地区的市场情况。我给出了以下数据的简化版本:Competitor Region ProductA Product...

  • 作为一名IT从业者,我来回答一下这个问题。首先,对于具有Java编程基础的人来说,学习Python的初期并不会遇到太大的障碍,但是要结合自己的发展规划来制定学习规划,尤其要重视学习方向的选择。Java与Python都是比较典型的全场景编程语言,相比于Java语言来说,当前Python语言在大数据、人工智能领域的应用更为广泛一些,而且大...

  • 这段时间通过学习相关的知识,最大的变化就是看待事物更加喜欢去了解事物后面的本质,碰到问题后解决问题思路也发生了改变。举个具体的例子,我在学习数据分析,将来会考虑从事这方面的工作,需要掌握的相关专业知识这个问题暂且按下不表,那哪些具体的问题是我需要了解的呢,以下简单罗列:1、了解数据分析师这个岗位在各个地区的需求情况?2、数据分析师的薪...

  • 这一节将开始学习python的一个核心数据分析支持库---pandas,它是python数据分析实践与实战的必备高级工具。对于使用 Python 进行数据分析来说,pandas 几乎是无人不知,无人不晓的。今天,我们就来认识认识数据分析界鼎鼎大名的 pandas。目录一. pandas主要数据结构 SeriesDataFrame二...

  •     先吐为敬!   最近心血来潮研究nodejs如何完成微信支付功能,结果网上一搜索,一大堆“代码拷贝党”、“留一手”、“缺斤少两”、“不说人话”、“自己都没跑通还出来发blog”、“各种缺少依赖包”、“各种注释都没有”、“自己都不知道在写什么”的程序大神纷纷为了增加自己博客一个帖子的名额而发布了各种千奇百�...

  • 阅读ceph源码过程中需要明确当前操作是由哪个线程发出,此时需要根据线程id来确认线程名称 C++获取线程id是通过系统调用来直接获取 函数描述 头文件: 函数名称:syscall(SYS_gettid) 该函数直接返回了一个pid_t int类型的数字,即为当前线程id 此外函数pthread_s...

  • 面试题 分库分表之后,id 主键如何处理? 面试官心理分析 其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全局唯一的 id 来支持。所以这都是你实际生产环境中必须考虑的问题。 面试题剖析 基于数据库的实现方案 数据库自增 id 这个就是说你的...

  • ORM操作    单表、一对多表操作 1 from django.db import models 2 3 4 class UserGroup(models.Model): 5 title = models.CharField(max_length=32) 6 7 8 class UserInfo(m...

  • 建立如下表: 建表语句: class表创建语句 create table class(cid int not null auto_increment primary key, caption varchar(32) not null)engine=innodb default charset=utf8;student表创建语句 c...