NOSQL schema建立原则

(1)数据规模php

Bigtable类数据库系统(HBase,Cassandra等)是为了解决海量数据规模的存储须要设计的。这里说的海量数据规模指的是单个表存储的数据量是在TB或者PB规模,单个表是由千亿行*千亿列这样的规模组成的。提到这个数据规模的问题,不得不说的就是如今在NoSQL市场中,最火的四种NoSQL系统依次是Mongodb,Redis,Cassandra,HBase。咱们知道Cassandra和HBase都是Bigtable类系统,并且都是名门出身(获得了Facebook,Yahoo,Twitter等的大力支持)。那么为何最火的是Mongodb呢?难道是由于HBase不够优秀么?我认为缘由很简单,毕竟大部分公司的数据规模还达不到Facebook,Yahoo等那么大,使用Mongodb足以知足他们的需求。Mongodb所提供的Auto-sharding, schema-less等功能,正好解决了这样数据规模的公司在使用RDBMS过程当中遇到的问题。mysql

(2)数据模型sql

并且Bigtable类数据库系统的数据模型相对简单,通常不涉及多表的JOIN操做。在这样的规模下,传统的RDBMS应用愈来愈受到限制,维护和升级的成本愈来愈高。并且传统RDBMS因为基于share-storage的设计,scale-out的能力不强。把基于share-storage的RDBMS作成分布式数据库,须要用户来开发Proxy层。上述种种问题使得咱们在面对海量数据的时候不得不考虑像Bigtable这样的NoSQL存储方案。那么对于习惯了为RDBMS设计schema的DBA们来讲,迁移到Bigtable类NoSQL系统时的schema设计问题,就须要换一个思路来考虑这个问题了。这篇文章就是介绍在Bigtable类系统中如何设计table的schema,以及随着数据规模的扩展,一些传统RDBMS应用向Bigtable系统迁移过程当中须要注意的问题。数据库

NoSQL数据库把可扩展性放到了首位,那么必然会形成必定量的数据冗余,经过数据冗余的方式实如今RDBMS中的不一样表格之间的关系表达。并且在Bigtable类系统中,不会提供SQL类的复杂查询表达和各类优化功能,仅仅提供海量的数据存储能力。因此,就像在Facebook的统一消息系统中同样,不少时候是用一行来存储一个用户的全部信息。那么在Bigtable类系统中,一行所能存储的数据量就是很是大的。前段时间在微博上有rumor说apple的siri系统后台是用的HBase,我想若是是真的话,那么一个用户的我的助理信息也应该是存在一行里吧,呵呵。更有意思的是,apple的保密工做作的真好,并且声东击西。明明用的是HBase,招聘的时候非说会Cassandra和Mongodb的有加分。。。服务器

在Bigtable类系统schema设计中还须要注意的就是列族特性。由于Bigtable类系统本质上是按照列族存取的,同一个列族里不一样列有一个共同点就是数据类型相同。相同的数据类型就会使得数据在磁盘和内存之间IO时的压缩率很是高,这是全部面向列的存储系统的共同优点。那么咱们在考虑一行所要存储的信息时,就能够按照各个属性的数据类型的不一样存放到相应的列族中。因为Bigtable是一个稀疏的表格系统,因此可能某一行具有的某一属性在其余全部的行里都不存在,可是这个属性的数据类型(例如int)的属性在其余行基本上确定会存在因此在实际的存储中,同一列族的属性是存放到一块儿的。架构

(3)非规范化app

在NoSQL系统数据建模中,常常提到一个Denormalization的概念,就是非规范化。举个简单的例子就是在RDBMS中的Entity和这些Entity之间的关系存储到NoSQL中的同一个表格中。例如在RDBMS的规范化数据建模中,有两个表格:Student(StudentID,StudentName,Tutor,CourseID), Course(CourseID,CourseName)。而在Bigtable类NoSQL系统中,只有一个表格Student(StudentID,StudentName,Tutor,CourseID,CourseName)。那么对于在传统RDBMS中须要读取两个表格的信息,而后JOIN在一块儿得到或者聚合某些用户的信息,在NoSQL系统中只须要读取一次就能够获取某些用户的信息了。less

(4)Row Key异步

Bigtable类系统schema设计须要注意的另一个问题就是Row的自然有序性。Bigtable类系统把Row Key都是解释成String的,而且按照String的字母顺序来组织Row的。因此这一特性就能够被咱们的schema设计所利用。例如咱们的应用常常须要用到某一属性的索引或者几个属性组合的索引,那么就能够用这一属性或者属性组合来作Row Key。这一点很是相似于RDBMS中的索引和组合索引,只不过在Bigtable类系统中,这是自然存在的。须要注意的是,在HBase系统中属性组合做为Row Key时,须要用特殊的符号将各个单独的组成部分拼接起来,可是“/”是不能做为Row Key中不一样属性的分割符的,咱们能够用“_”。分布式

(5)数据一致性和事务

在数据一致性方面,在传统的RDBMS系统中,每一列的属性能够规范成NOT NULL, UNIQUE或者CHECK等,由RDBMS系统来为用户保证数据的一致性需求。在Bigtable类系统中,这一需求在DB层并无保证,而是由用户层程序来保证的。例如Bigtable类系统是一个大的稀疏的表格,那么对于表格中若是某个cell为NULL,那么在实际物理存储中是不存储这个cell的,也就是Bigtable类系统中不保存内容为NULL的value。因为开源系统HBase具有行一致性和行原子性,并且通常一行存放一个用户的信息,因此维护数据一致性的代价相对较小。若是Bigtable类系统的schema设计不佳,形成复杂的数据冗余,那么对于应用层来维护数据一致性的代价就很大了。

关于Bigtable类系统的事务支持提及来就很复杂了。简单的就是HBase已只支持单行的事务,今天据说HBase也开始支持单机多行事务。可是若是打算实现相似于RDBMS的事务特征,就得结合HBase和Zookeeper了。关于这方面在本文不作详细讨论,后面会专门发文讨论Google的关于Percolator和Megastore的paper。这两篇paper主要讨论了如何在利用NoSQL系统实现事务,如何打通NoSQL和SQL的。可是就像在Cloud Foundry中Chris说到的同样,将来的数据库系统是polyglot persistent storage,不会出现大而全的数据库一统天下。那么在Bigtable/HBase上开发愈来愈复杂的feature也得有本身的取舍,试图打造NoSQL领域的DB通是不现实的。

(6)索引

关于索引是每一个DB系统都须要考虑的问题。从Bigtable的论文中能够看出其为每列维护特殊的单列索引,容许建立多列索引。这些索引由Bigtable自动维护,查询时由Bigtable自动选择使用哪些索引。这一点和RDBMS就比较接近了。而开源实现的HBase除了自动有序的Row Key做为索引之外,只提供一个自动维护secondary index。可是查询时该使用那些索引,得由应用层来决定。关于HBase的secondary index的实现有多种方式,貌似最近还喝coprocessor扯上了关系,能够参考这个http://kenwublog.com/hbase-secondary-index-and-join。 HBase同时容许建立和使用存储在文件系统上的Lucene索引。关于HBase和Lucene的结合,能够参考这里http://www.infoq.com/articles/LuceneHbase

以上主要是从理论上说明了如何为Bigtable类系统设计schema。下一篇文章会以一些实例的方式说明这篇文章的内容,其中会涉及到Facebook统一消息系统的例子。

转自:http://yanbohappy.sinaapp.com/?p=20

上一篇文中从理论上讲了如何针对应用需求选择Bigtable类系统和Bigtable类系统schema设计的原则。这一篇文章举例说明目前在工业界你们是怎么作的。一下以Facebook统一消息系统的设计为例来讲明这个问题。

We’re launching a new version of Messages today that combines chat, SMS, email, and Messages into a real-time conversation.

(1)   统一消息系统的规模和应用需求

每月多于350 million的用户会发送多于 135 billion的person-to-person messages, 120 billion的chat messages。即便这些messages都被限制在160个字符以内,那也意味着21,600,000,000,000 bytes的数据。并且数据是在一直增加的,由于用户收到消息或者邮件以后通常是不会删除的。并且统一消息系统所存储的数据主要有如下两种类型:一是常常变化的较小的临时数据集(聊天内容),一是不多被访问的不断增长的数据集(邮件,阅读了收件箱里的邮件之后就不多去再看它一眼。可是还不能使用archive的方式(相似于磁带)存储,由于这些数据必须available all times and with low latency)。同时,对于每一个用户,咱们须要维护这个用户的全部消息的逆向索引,由于每一个用户都想像使用gmail同样经过关键字的形式搜索本身以往的邮件或者聊天记录。

同时消息系统须要high write throughput。如今的状况是Facebook Message天天约80亿的消息,75亿以上的读和写操做。高峰时期每分钟150万的读和写操做,其中约55%的读和45%的写操做,每次的写操做会插入16个记录。NoSQL系统的Denormalization特性带来了schema-less的优点,同时因为同一份信息可能在不一样的列中冗余存在,也使得写的次数增长。

(2)为何选择HBase?

在统一消息系统创建之初,对于Infrastructure最重要的就是数据库的选择了。 Facebook的Data team面临的选择主要有三种:Cassandra, MySQL和HBase。通过在evaluate clusters上的测试,他们最终选择了HBase。缘由以下:MySQL首先是不能很好的auto sharding的问题,也不能很好的处理长尾数据,并且随着数据集的不断增加,更新索引的性能会降低,统计一些变量会变得很慢。而Cassandra的最终一致性模型对于消息系统来讲又是很是不靠谱的。Cassandra保证每次读都能读到,这个是高可用的,然而读到的数据却不保证是最新的数据。若是不是最新的数据,会发生一个read repair process。而HBase在可扩展性和性能不会随着数据量增加而降低方面击败了MySQL,又具有比Cassandra更简单的一致性模型。(这是否是说明HBase的强一致性、低可用性打败了Cassandra的最终一致性和高可用性呢?笔者一直认为Cassandra/Dynamo的思路在设计上很是先进和超前,可是实际系统中应用起来很是麻烦。工业界仍是但愿简单可依赖的东西。) Facebook在HBase上作了一些工做以后,发现HBase的许多feature(例如auto load balance, auto failover, compression support, multiple shards per server等)正好知足这个统一消息系统的需求。HBase底下的HDFS的诸多优势(replication, end-to-end checksum and automatic rebalance)也很是吸引人,并且Facebook在Hadoop/Hive上面有不少使用经验。由于在这以前,Facebook的数据仓库就已经转到Hadoop平台上了。

注:这里不得不说Hadoop/HDFS的replication策略和Cassandra的replication策略的不一样。Cassandra在某一服务器收到用户的写入数据以后,异步的写到其余副本服务器上。并且屡次写操做在不一样副本的时间前后关系是不能保证一致的。可是HDFS是经过同步的pipeline的方式写入的,那么一旦给客户端返回成功,就能保证全部副本数据的一致性。并且HBase可以保证屡次写入之间的顺序关系,这在消息系统中是很是重要的。由于多条消息的前后到达关系若是混乱,会给用户带来麻烦。可是pipeline的复制有个缺点,就是木桶的短板效应,一旦3副本中有一个写入的速度变慢,就会致使总体pipeline变慢,那么HBase的写入吞吐率就会下降。

(3)Facebook Messages后台存储系统HBase的schema设计

包括Messages, Chats, Emails, SMS四种消息类型。因为本文主要讨论后台HBase系统的schema设计问题,关于Facebook统一消息系统的架构和数据流描述详细请见这篇文章http://blog.huihoo.com/?p=688。归纳起来就是用 HBase 做为Backend storage infrastructure,每一个用户数据存储在 HBase 的单独一行里,每一个实体都存储在本身的HBase列中 ;邮件的大附件是存储在Facebook本身开发的专用存储系统 HayStack 中;使用 Apache Lucene 维护反向索引列表。在整个数据流中,用户的每一个行为都是经过应用服务器代理用户完成的。因此整个数据流就是应用服务器与后台存储基础设施之间的互动。针对同一个用户的不一样更新可能同时发生,也就是对HBase中一行的操做须要同步,这也是由应用服务器来控制的。也就是说每一个用户在某一时间段本质上全部的操做都会经过某一台同步服务器控制的。

这是统一消息系统中Hbase的schema设计图。从这张图中能够看出,一个用户是Hbase中的一行,包括Metadata Entities, Metadata indexes, Action logs, Keyword search index四个列族。

 

用户消息系统中的任何更新操做(如发表和删除消息,标记为已读等)会当即append到Action Logs列族中,这称为一个action log。一些很小的消息实体也存储在每一个对应的action log中。这个设计很像Log-Structured File System(http://www.cs.berkeley.edu/~brewer/cs262/LFS.pdf),把每块数据都append到磁盘,而后维护索引以保持文件总体性和结构。这里也是经过Metadata indexes列族来维护消息数据的结构。例如咱们要加载未读邮件列表,那么先经过Metadata indexes获取指定tag/time区间的邮件的索引,而后去Metadata Entities中寻找索引对应的实体(好比邮件内容)显示出来。因此能够这么理解,Metadata Entities列族中每一列存放着不一样的消息实体。

 

Actin log的做用和文件系统或者数据库中log的做用相似,能够经过replay action log的方式来构造或者恢复用户邮箱的当前状态。经过最后的action log id和Metadata entities, metadata indexes配合来恢复或者更新用户消息系统的内容。Action log能够经过批量执行大量的异步写节省带宽,提升IO吞吐率。并且action log还能够被复制到其余地方以实现数据的备份。

 

Keyword search index列族是为了用户经过关键字搜索本身消息系统中内容而设计的,维护了一个从keyword到匹配的消息的逆向索引。每一列是一个keyword和存在这个keyword的消息列表。当用户写入一个消息是,经过Lucene去解析和转化它为一个(keyword,messageID,position)的tuple,而后append到这个Hbase列族中相应关键字的列中。这样全部的消息都被实时索引。关于如何集成Lucene和Hbase,请看这篇文章http://www.infoq.com/cn/articles/LuceneHbase 。

 

(4)Hbase在Facebook的部署状况

这是Facebook的部署方案:每一个rack里放20台server,每一个集群里至少5个racks。

据称Facebook的消息系统的Hbase集群的规模已经超过了1000台servers。能够说是目前那Hbase用做online storage应用的最大规模的集群了。并且目前在微博上有rumor说Facebook正准备把用户数据从mysql移到hbase。看来Facebook要全面拥抱Hbase了。

参考文献:

http://www.facebook.com/note.php?note_id=454991608919

http://dbpedias.com/wiki/HBase:Facebook_Messaging_-_HBase_Comes_of_Age

http://blog.huihoo.com/?p=688

转自:http://yanbohappy.sinaapp.com/?p=24
相关文章
相关标签/搜索