解读BigTable类NoSQL数据库的选型与设计
数据规模
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过程中遇到的问题。
数据模型
而且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类系统中,一行所能存储的数据量就是非常大的。前段时间在微博上有传闻说Apple的siri系统后台是用的HBase,我想如果是真的话,那么一个用户的个人助理信息也应该是存在一行里吧,呵呵。更有意思的是,Apple的保密工作做的真好,而且声东击西。明明用的是HBase,招聘的时候非说会Cassandra和MongoDB的有加分。
在BigTable类系统schema设计中还需要注意的就是列族特性。因为BigTable类系统本质上是按照列族存取的,同一个列族里不同列有一个共同点就是数据类型相同。相同的数据类型就会使得数据在磁盘和内存之间IO时的压缩率非常高,这是所有面向列的存储系统的共同优势。那么我们在考虑一行所要存储的信息时,就可以按照各个属性的数据类型的不同存放到相应的列族中。由于BigTable是一个稀疏的表格系统,所以可能某一行具备的某一属性在其他所有的行里都不存在,但是这个属性的数据类型(例如int)的属性在其他行基本上肯定会存在所以在实际的存储中,同一列族的属性是存放到一起的。
非规范化
在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系统中只需要读取一次就可以获取某些用户的信息了。
Row Key
BigTable类系统schema设计需要注意的另外一个问题就是Row的天然有序性。BigTable类系统把Row Key都是解释成String的,并且按照String的字母顺序来组织Row的。所以这一特性就可以被我们的schema设计所利用。例如我们的应用经常需要用到某一属性的索引或者几个属性组合的索引,那么就可以用这一属性或者属性组合来做Row Key。这一点非常类似于RDBMS中的索引和组合索引,只不过在BigTable类系统中,这是天然存在的。需要注意的是,在HBase系统中属性组合作为Row Key时,需要用特殊的符号将各个单独的组成部分拼接起来,但是“/”是不能作为Row Key中不同属性的分割符的,我们可以用“_”。
数据一致性和事务
在数据一致性方面,在传统的RDBMS系统中,每一列的属性可以规范成NOT NULL, UNIQUE或者CHECK等,由RDBMS系统来为用户保证数据的一致性需求。在BigTable类系统中,这一需求在DB层并没有保证,而是由用户层程序来保证的。由于开源系统HBase具备行一致性和行原子性,而且一般一行存放一个用户的信息,所以维护数据一致性的代价相对较小。如果BigTable类系统的schema设计不佳,造成复杂的数据冗余,那么对于应用层来维护数据一致性的代价就很大了。
关于BigTable类系统的事务支持说起来就很复杂了。简单的就是HBase只支持行级的锁,如果打算实现类似于RDBMS的事务特征,就得结合HBase和Zookeeper了。关于这方面在本文不做详细讨论,后面会专门发文讨论Google的关于Percolator和Megastore的paper。这两篇paper主要讨论了如何在利用NoSQL系统实现事务,如何打通NoSQL和SQL的。
索引
关于索引是每个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。
7月21日前,通过TechTarget中国专用注册码“TECH13PR”注册参加甲骨文全球大会,即可享受最低折扣!还有50元手机充值卡等您拿!活动详情请见: http://www.searchdatabase.com.cn/edm/oracle/20130515/index.html