Posts Tagged ‘cassandra’

8月云计算相关开源新闻

August 28th, 2010

Hadoop 0.21 发布

http://hadoop.apache.org/common/docs/r0.21.0/releasenotes.html
8 月23日,Hadoop 0.21发布,距离上一个版本0.20的发布已经过去了16个月,而之前两个版本,从0.18到0.20也仅仅用了一半的时间,8个月。这个千呼万唤始出来的版本是Hadoop发展历史上的一个重要里程碑,但是,很可能将成为一个过渡版本,而不会被广泛使用。

Hadoop 0.21有一个代码结构上的大调整,HDFS和MapReduce分离成了两个独立的子项目,而公共部分,如 RPC 和一些辅助工具则由 Common 子项目来完成,这一拆分是造成发布姗姗来迟的主要原因之一。这一版本在功能上的新特征包括:支持Backup Node来改善NameNode的单点问题,扩大了新MapReduce API的支持范围,提供了新的 FileContext API等,同时对测试框架进行了改进,引入了新的自动测试框架Herriot。

尽管从结构上和开发框架上有了很多调整,但从功能看,Hadoop 0.21.0 实际上只是 0.20.2+,在接口上保持了和 0.20.2 的兼容性,新增加的功能或者比较有限,或者还在完善中,很重要的安全方面的功能 Kerberos 的支持也要到 0.22 才会提供,因此,包括 Yahoo 在内的很多重要用户都表示很可能会跳过 0.21 而在 0.22 成熟时直接演进到 0.22,0.21 的前途尚不明朗,它很可能只是 0.22 前进路上的一个重要的铺路石。

Cassandra 0.7.0-beta1和0.6.5发布

https://svn.apache.org/repos/asf/cassandra/tags/cassandra-0.7.0-beta1/NEWS.txt
开源分布式非关系型数据库NoSQL一直在我们的视野之中,自从成为Apache项目以来,发展十分迅速,当然也经历了一些波折,面对着一些质疑,此次 0.7.0 beta的发布,使他再次出现在我们的视野之中,0.7中,行的尺寸从2GB提升到了20亿列,同时增加了输出到Hadoop的输出格式,便于两者的互动,此外,还在功能和性能上进行了很多其他改进。

OpenStack 社区迅速成长

http://openstack.org/blog/2010/08/openstack-community-update/
如果说上半年的明星项目是Cassandra的话,迄今为止,下半年的明星项目当属RackSpace和NASA在7月联合发布的OpenStack,一个多月来,这个项目一直留在我们的视野当中,并于8月25日举行了一次聚会。目前,OpenStack的计算部分(Nova)支持了KVM、Xen和 VirtualBox三种虚拟机,而存储部分则开始进行对象存储系统swift的开发。这些具体的工作让 OpenStack 开始逐步走向现实。

[译文]Cassandra实例

May 27th, 2010

原文: http://www.rackspacecloud.com/blog/2010/05/12/cassandra-by-example/#
原作者:Eric Evan 
原文发布日期:May 12, 2010
译者:王旭(http://wangxu.me/blog/ , @gnawux)
翻译时间:2010年5月15,25,26日

近来 Cassandra 备受瞩目,很多人正在评估是否可以应用 Cassandra。由于这些人更多的追求速度,相应的,我们的文档就过于粗浅了。这些文章中,最差的是为有关系数据库基础的人解释Cassandra数据模型的那些。

Cassandra 数据模型实际和传统的数据库差异非常大,足够让人眩晕,而且很多误解都需要修正。

有些人把这个数据模型描述成存放map的map,或对于super column的场景,是存放map的map的map。这些解释经常用类似 JSON 标记的视觉辅助展示方法来进行佐证。其他人则把列族看做是系数表,还有人把列族看作是存放列对象的集合容器。甚至有人有时把列看走势三元组。我觉得所有这些解释都不够好。

问题在于很难去用类比的方法来确切解释一个新的东西,而且如果比较的不准确的话常常把人搞糊涂。我仍然期望有人能解释清楚这个数据模型,但同时我觉得确切的例子可能更容易说明白一些。

Twitter

尽管 Twitter 本身就是 Cassandra 的一个实际的应用场景,它仍然是一个不错的教学实例,因为它众所周知而且易于抽象。在例子中,和很多站点一样,每个用户都有一份用户数据(显示名称、密码、email等),这些信息链接到朋友(译注:用户follow的人)和 follower(译注:follow用户的人)。此外,如果没有那些短 tweets 的话也就不是 twitter 了,tweet每条140个字符,它们都关联着诸如时间戳和惟一的id这样的元数据,这个id我们可以从URL里看到。

现在我们在一个关系数据库里来直接进行建模,我们首先需要一个表来存放用户。

CREATE TABLE user (
    id INTEGER PRIMARY KEY,
    username VARCHAR(64),
    password VARCHAR(64)
);

我们还需要两张表来存储一对多的follow关系。

CREATE TABLE followers (

    user INTEGER REFERENCES user(id),

    follower INTEGER REFERENCES user(id)

);

 
CREATE TABLE following (

    user INTEGER REFERENCES user(id),

    followed INTEGER REFERENCES user(id)

);

显然,我们还需要表来存储tweets。

CREATE TABLE tweets (
    id INTEGER,
    user INTEGER REFERENCES user(id),
    body VARCHAR(140),
    timestamp TIMESTAMP
);

由于仅仅是个例子,我已经极大简化了情况,但仅仅是这个极度简化的模型,也还有很多需要做的工作。例如,要以可行的方法达到达到数据归一化就需要一个外部键值约束,而因为我们需要从多张表join信息,我们需要对任意值建索引,以保证高效。

但是让一个分布式系统正常工作相当有挑战性,几乎不可能不做任何折衷。对Cassandra来说也是如此,而且这也是为什么上述数据模型对我们来说是无法工作的的原因。对于入门者,没有可供参考的完整性,缺乏次索引使得join很难进行,所以,你必须反归一化。另一方面,你被迫思考你要进行的查询的方式和期望结果,因为这差不多就是数据模型看起来的样子。

Twissandra

那么如何把上述模型翻译到Cassandra中呢?十分幸运,我们只需要看看 Twissandra,这是 Eric Florenzano 写的一个 Twitter 的简化版克隆,用作例子。那么让我们来使用 Twitter 和 Twissandra 作为例子来看看 Cassandra 的数据模型是如何的。

Schema

Cassandra 是一种无 schema 的数据存储方式,但为你的应用做一些特定的配置还是必要的。Twissandra 给出了一个可以工作的 Cassandra 配置,不过研究一下关于数据模型方面的配置还是物有所值的。

Keyspaces

Keyspaces 是 Cassandra 中最顶层的命名空间。在未来版本的 Cassandra 中,将可以动态创建 keyspace,正如在 RDBMS 中创建数据库一样,但是对于 0.6 和以前的版本,这些都在主配置文件中定义,如:

<Keyspaces>
  <Keyspace Name="Twissandra">
  ...
  </Keyspace>
</Keyspaces>
Column Families

对于每个 keyspace,都可以有一个或多个列族。列族是用于关联类型相近的记录的命名空间。Cassandra 在写操作时,在一个列族内部允许有记录级的原子性,对它们进行查询非常高效。这些特性十分重要,在进行你的数据建模前必须记牢,它们会在下面讨论到。

和keyspace类似,列族也在主配置文件中定义,虽然在将来的版本中你将可以在运行时创建列族,正像在RDBMS中创建表一样。

<Keyspaces>
  <Keyspace Name="Twissandra">
    <ColumnFamily CompareWith="UTF8Type"  Name="User"/>
    <ColumnFamily CompareWith="BytesType" Name="Username"/>
    <ColumnFamily CompareWith="BytesType" Name="Friends"/>
    <ColumnFamily CompareWith="BytesType" Name="Followers"/>
    <ColumnFamily CompareWith="UTF8Type"  Name="Tweet"/>
    <ColumnFamily CompareWith="LongType"  Name="Userline"/>
    <ColumnFamily CompareWith="LongType"  Name="Timeline"/>
  </Keyspace>
</Keyspaces>

需要指出的是,上面的配置片段中,指定名字的时候同时指定了一个比较者类型。这凸显了 Cassandra 和传统数据库的又一个重大不同,记录按照设计的顺序存储,在之后不能轻易改变。

这些列族都是什么?

一下子看所有的七个Twissandra列族是干什么的可能不那么直观,所以,我们来逐个仔细看一下:

  • User

User用于存储用户信息,大致相当于上面描述的用户表。列族中的每条记录以UUID为键值,并包含用户名和密码列。

  • Username

在User列族中查询一个用户需要知道用户的键值,但从用户名怎么找到这个UUID键值呢?在上面描述的SQL关系数据库里的话,我们就在User表里来一个匹配用户名的SELECT语句(WHERE username = ‘jericevans’)就行了。但这对于Cassandra来说却不可能。

首先,关系数据库可以顺序地扫描全表来进行这样一个 SELECT,但由于记录是基于键值分布在 Cassandra 集群中的,这个匹配将可能会在多个节点上进行,可能是很多节点。而且,即使是数据就在一个节点上,仍然有一个原因会让这一操作远没有关系数据库效率高,因为关系数据库可以对username列有索引。前面提到过,Cassandra是不支持第二索引的。

解决方案就是,建立一个我们自己的反向索引,进行用户名到UUID键值的映射,这就是Username列族的用途。

  • Friends
  • Followers

Friends 和 Follower 列族可以回答这些问题:用户X follow了哪些人?谁follow了用户X?这两个列族的键值都是这个唯一的用户ID,其中包含了哪些有follow关系的用户以及它们创建的时间。

  • Tweet

Tweet 列族用于存放所有的tweets。这个列族以每个 tweet 的 UUID为键值,还包含了用户id,tweet内容以及tweet时间这些列。

  • Userline

这是属于每个用户的时间线。记录的键值是用户的ID,其他的列中,包含有一个数字时间戳到Tweet列族中的tweet ID的映射。

  • Timeline

最后,Timeline列族类似于Userline,只是这里存储着每个用户的朋友的tweet的时间线视图。

有了上面这些列祖,现在我们可以看一些常用的操作都是如何发生的。

把这些列族放在一起来试一下

添加一个新用户

首先,新用户需要一个方法来注册一个账户,当他们注册的时候,组要将他们添加到Cassandra数据库中去。对于Twissandra,我们来看看里面的内容:

username = 'jericevans'
password = '**********'
useruuid = str(uuid())   
columns = {'id': useruuid, 'username': username, 'password': password}   
USER.insert(useruuid, columns)
USERNAME.insert(username, {'id': useruuid})

Twissandra是用Python写成的,使用 Pycassa 作为访问 Cassandra的客户端,上述大写的 USER 和 USERNAME 是 pycassa.ColumnFamily 的实例,它们需要在使用之前的某个位置被分别初始化。

这里说明一下,这不是从 Twissandra 里原样摘出来的。我让他们更加简单而且是自包含的。比如,在上面的例子中,如果没有对用户名和密码的赋值的话,可能不那么好理解,不过一个 web 应用只能从用户注册表单里得到这些内容。

从这个例子中回来,有两个不同的 Cassandra 写操作(insert()),第一个创建了一个用户列族,另一个更新了用户名到用户 UUID 键值的反向映射表。在两个例子中,参数都是用于查找记录的键值,以及包含列名和值的map。

Following 一个朋友
frienduuid = 'a4a70900-24e1-11df-8924-001ff3591711'   
FRIENDS.insert(useruuid, {frienduuid: time.time()})
FOLLOWERS.insert(frienduuid, {useruuid: time.time()})

这里我们再来两个不同的insert()操作,这次是加入一个用户到我们的朋友列表,并加入反向关系:给被 follow 用户添加一个 follower。

发出Tweet
tweetuuid = str(uuid())
body = '@ericflo thanks for Twissandra, it helps!'
timestamp = long(time.time() * 1e6)   
columns = {'id': tweetuuid, 'user_id': useruuid, 'body': body, '_ts': timestamp}
TWEET.insert(tweetuuid, columns)   
columns = {struct.pack('&gt;d', timestamp: tweetuuid}
USERLINE.insert(useruuid, columns)   
TIMELINE.insert(useruuid, columns)
for otheruuid in FOLLOWERS.get(useruuid, 5000):
    TIMELINE.insert(otheruuid, columns)

要存储一条新的tweet,我们需要使用一个新的UUID作为键值,在 Tweet列族创建一个记录,其中的列包含作者的用户ID,创建的时间,当然还有tweet的文本内容本身。

此外,用户的 Userline 中也要加入tweet的时间和它的id。如果这是用户的第一条tweet的话,这个insert()会产生一条新的纪录,后面的只是为这条记录添加新列。

最后要给发出tweet的用户和其他follower的 timeline 列族添加这条tweet的ID和时间。

值得注意的一件事是,这里,时间戳使用的是64位长整型变量,而当它成为一个列的名字的时候,它会被打包为网络字节序的二进制值。这是因为Userline和Timeline列族使用了一个LongType Comparator,允许我们使用数值区间指定查找指定范围,所以它们被按照数值来存放起来。

接收一个用户的 tweets
timeline = USERLINE.get(useruuid, column_reversed=True)
tweets = TWEET.multiget(timeline.values())

接收一个用户的tweet,首先从Userline获取tweet ID的一个列表,然后从Tweet列族通过multiget()方法莱读取这些tweet。得到的结果将是通过着数值表示的时间戳逆序排列的,因为Userline使用了LongTyper comparator,并且reversed设置为了True。

获取一个用户的时间线
start = request.GET.get('start')
limit = NUM_PER_PAGE   
timeline = TIMELINE.get(useruuid, column_start=start,
column_count=limit, column_reversed=True)
tweets = TWEET.multiget(timeline.values())

和上一个例子类似,这次是从 Timeline 读取 tweet ID,不过这次我们还使用了 start 和 limit 来控制读取列的范围。这样有助于输出结果的分页。

那么,下一步呢?

希望这足够提供给你一个大致的概念。重复一下,我从代码中提取了一些例子,为了简明起见,略去了一些操作,所以现在可能是 check out 出 Twissandra 的源代码并进行下一步深入研究的好时候了。有很多功能,诸如 retweet 和 lists,都还空着没有实现,可以作为一个练习的起点。如果你已经熟悉 Python 和 Django 的话,那你可以考虑实现一下这些方法。

Cassandra 的 wiki 包含了大量的信息,而且还在不断增多,还包括一个实时更新的其他人贡献的文章与幻灯片的列表。

如果你喜欢IRC的话,你可以加入 irc.freenode.net 的 #cassandra 频道,来和那里的人聊天,他们总是热衷于提供帮助和回答问题。如果你更青睐 email 的话,cassandra-user 邮件列表上也有很多可以提供帮助的人。

[译文]理解Cassandra源代码

May 13th, 2010

原文: http://prettyprint.me/2010/05/02/understanding-cassandra-code-base/
原作者:Ran Tavory 
原文发布日期:May 2nd, 2010
译者:王旭(http://wangxu.me/blog/ , @gnawux)
翻译时间:2010年5月12日

最近我为 cassandra 添加了一些小特性,于是我花了些时间来更仔细地考察了这个系统的内部设计。诸如 embedded service 这样的特性实际上并不需要对源代码和设计的深入理解,但其他特性,比如 truncate 就需要对系统中使用的不同算法有深入的了解,如写操作时如何进行的,读操作时如何进行的,值时如何删除的(提示:他们没有……)等等。

源代码虽然不是非常长,有大约91136行,但非常密级,而且有很多算法,所以直接读这些代码对我来说并不是非常简单。我是用如下的外科手段来进行行数计数的($ cassandra/trunk $ find * -name *.java -type f -exec cat {} \;|wc -l

我写这篇文章希望可以帮助其他人能更快地阅读这些代码。我不会介绍那些基础信息,比如“什么是Cassandra”,如何部署,如何检出代码,如何编译,如何下载thrift等。我也不想介绍算法最复杂的部分,比如ae-service使用的merkle-tree如何 ,Cassandra 中不同部分都是用的 bloom filter 是什么、如何工作,以及 gossip 是如何使用的。我不认为我适于解释所有这些问题,而且在cassandra的开发者wiki上也已经介绍这些了。我要写的就是我学习cassandra的途径,以及我在这个过程中学到了什么。我没有在其他地方发现过类似文档(当我完全完成的时候,可能我会把这些写入wiki),所以我觉得这对我下次再深入新的源代码会非常有用。

最后是一个免责声明:这里仅仅是我对系统如何工作的个人理解,它们是不完整、不确切的,特此警告。注意我也只是在学习,或多或少也是Cassandra菜鸟。也请注意,Cassandra 是一个运动目标,它一直在快速开发者,任何一个代码的快照或早或晚都会发生些变化。在本文写作的时候,官方版本是 0.6.1,不过我工作在 trunk 上,这个分支将来会成为 0.7.0。

这里是我所采取的几个步骤以及我学到的东西的一个描述。

下载,配置,运行…

首先你需要下载代码并运行单元测试。如果你使用 eclipes,IDEA,netbeans,vi,emacs等等这些的话,可能还需要配置一下。这非常简单,这里有更多介绍。

阅读

接下来你需要读一些背景材料,这依赖于你想搞哪个部分。我希望理解读操作、写操作以及值如何删除,所以我把下面每个文档都看了差不多五遍。没错,每个五遍。它们包含了大量的信息,我发现每次读我都能被更多的一些细节所吸引。我先读过这些文档,然后读源代码,确定我理解了算法如何在类和方法中实现,然后再读文档,然后再读源代码,读单元测试(并用debugger运行它们)等等。这是这些文档:

http://wiki.apache.org/cassandra/ArchitectureInternals

SEDA paper

http://wiki.apache.org/cassandra/HintedHandoff

http://wiki.apache.org/cassandra/ArchitectureAntiEntropy

http://wiki.apache.org/cassandra/ArchitectureSSTable

http://wiki.apache.org/cassandra/ArchitectureCommitLog

http://wiki.apache.org/cassandra/DistributedDeletes

我还看了 Google BigTable 的论文和让人着迷的亚马逊的 Dynamo 的论文,不过这都是很久以前的事情了。它们是很好的背景材料,不过对于理解实际代码并不是必须的。

好了,读完所有这些文档,我开始知道能做什么、如何做了,但我感觉我还没有到达能写新特性的阶段。在读代码几次之后,我发现我有点晕了,还是不了解诸如“值到底是不是真的被删除了”,那个累负责哪个功能,有几个Stage,Stage之间的数据流是什么样的,还有“如何标记整个列族为删除”,这是我在truncate操作中真正想做的。

Stages

Cassandra 的操作使用的并发模型在 SEDA 论文中有介绍。这个并发模型大致是这样的,和很多其他兵法系统不同,一个操作,比如一个写操作,并不是在同一个线程中开始和结束的。相反,一个操作在一个线程中开始,之后把操作(异步)交给了另一个线程,然后再传递到下一个进程,直至完成。事实上,操作并不是在线程间流转,而是在stage间流转。操作从一个stage转向另一个。每个stage都和一个线程池相关联,这个线程池在方便的时候来执行这个操作。一些操作是 IO bound 的(译注:这里应该是CPU吧,猜的),另一些则受限于磁盘或网络,所谓“方便”取决于资源的可用性。SEDA 论文把这个过程解释得非常好(很好的文章,值得一读),简单地说你从中得到的是更高级别的并发性和更好的资源管理,资源包含 CPU、磁盘、网络等。

所以,要理解 Cassandra 的数据流,你首先需要理解 SEDA。然后你需要了解 Cassandra 中有哪些 Stage,以及这些 stage 之间数据时如何流动的。

十分幸运,作为一个起点,StageManager 类中包含了一个不完整 stage 列表:

public final static String READ_STAGE = "ROW-READ-STAGE";
public final static String MUTATION_STAGE = "ROW-MUTATION-STAGE";
public final static String STREAM_STAGE = "STREAM-STAGE";
public final static String GOSSIP_STAGE = "GS";
public static final String RESPONSE_STAGE = "RESPONSE-STAGE";
public final static String AE_SERVICE_STAGE = "AE-SERVICE-STAGE";
private static final String LOADBALANCE_STAGE = "LOAD-BALANCER-STAGE";

 

我就不具体介绍每个 stage 都负责什么了(因为我也不知道……)但我可以说大致说,ROW-READ-STAGE 在读操作中,ROW-MUTATION-STAGE 参与了写和删除操作,而 AE-SERVICE-STAGE 负责 anti-entropy (译注:整理?不知道怎么确切用中文表达了)。这不是一个完整的 stage 列表,根据你感兴趣的代码路径,用这个方法,你可以找到更多。比如,查看文件ColumnFamilyStore你可以找到更多的stage,如FLUSH-SORTER-POOL, FLUSH-WRITER-POOL 和 MEMTABLE-POST-FLUSHER。在 Cassandra 中,stage 由 ExecutorService 的实例来唯一标识,这差不多是一个线程池,他们有全大写的名字,如 MEMTABLE-POST-FLUSHER。

我画了一张混有类和stage的图来便于理解。这不是合法的UML,但我觉得这对于了解数据在系统中如何流动是个很好的方法。这不是全部类和stage的完整的图示,仅仅是我感兴趣的一部分。

cassandra-1 
[SSTableTracker], [ColumnFamilyStore]->[Memtable (memtable_)], [CommitLog|CommitLogExecutor], [DeletionService|FILEUTILS-DELETE-POOL], [StorageLoadBalancer| lb_:LB-OPERATIONS; lbOperations_:LB-TARGET], [StorageService| consistencyManager_:CONSISTENCY-MANAGER], [StageManager| READ_STAGE; MUTATION_STAGE; STREAM_STAGE; GOSSIP_STAGE; RESPONSE_STAGE; AE_SERVICE_STAGE; LOADBALANCE_STAGE; MIGRATION_STAGE]">yUML source

Debugging

可以使用一个 debugger 来读代码,运行一个单元测试是了解事情如何工作的非常棒的方法。我不是一个debugger的铁杆粉丝,但是他们有一个可取之处就是通过单步执行单元测试来学习新代码。所以我所做的证实单步执行代码中的单步执行。这非常酷。我还运行了 Hector 的单元测试,它使用 thrift 接口并运行一个嵌入的 cassandra 服务器,这个方法一针见血、界面友好而且还能学到更多东西。

类图

接下来我所做的是使用一个工具来从已有代码中提取类图。这不是非常有用。

好,我使用的工具不是很棒,但这不是最关键的问题,问题是 cassandra 的代码书写方法使得类图对于理解代码作用很有限。UML 类图对于面向对象设计非常有效。类图的必要性在于列出类、成员以及它们的关系。比如类 A 是类 B 的一个列表,这样通过 UML 类图可以看出 A 是 B 的聚合,而且仅仅通过类图就可以学到很多。比如一架飞机有很多乘客。

Cassandra 是一个拥有坚实算法后台和优秀性能的系统,但是,老实说,依我之见,从好的面向对象实践的视角来看,它可不是一个很好的研究案例……它的类包含很多静态方法和成员,而且在很多地方,你可以看到一个类调用另一个类的静态方法。纯粹的C风格,所以我发现类图尽管从类的可视化以及类之间的关系方面有些帮助,但并不是非常有用。

我放弃了类图,继续进入下一种兔——序列图。

序列图

序列图非常适于实体之间的交互的抽象和可视化。这里,一个实例可能是一个类,一个 STAGE 或一个 thrift 客户端。很幸运,使用序列图,你不必太专注于序列图里实体的类型,你只需要把他们都表示为 actor 就行了(至少我觉得这么做就够了,希望UML大神们原谅)。

下面的图通过运行 Hector 的单元测试并使用一个(单节点)嵌入式 Cassandra 服务器得到。这个序列图并不很通用,它仅仅描述了一种可能的执行路径,而实际上可能有很多种,但我尼克让它们更简单一些,尽管有点不太精确。

我使用了一个简单的在线序列图编辑器(http://www.websequencediagrams.com)来生成他们。

读操作:

cassandra-2

写操作:

cassandra-3

Table is a Keyspace

最后提示:作为 Cassandra 的用户我应该使用 Keyspace, ColumnFamily, Column 这些名词。不过,代码中使用了 Table 这个名词。啥是 Table 呢?……原来,Table实际上就是 Keyspace…… 就是一个提示,仅此而已。

研究代码是一项艰巨而有成就感的工作,我希望这篇文章帮助你也有个好的起点,快点跑起来。

HBase vs Cassandra: 我们迁移系统的原因

March 25th, 2010

原文: http://ria101.wordpress.com/2010/02/24/hbase-vs-cassandra-why-we-moved/
原作者:Dominic Williams
原文发布日期:February 24, 2010 at 7:27 pm
译者:王旭(http://wangxu.me/blog/ , @gnawux)
翻译时间:2010年3月21-25日

我的团队近来正在忙于一个全新的产品——即将发布的网络游戏 www.FightMyMonster.com。这让我们得以奢侈地去构建一个全新的 NOSQL 数据库,也就是说,我们可以把恐怖的 MySQL sharding 和昂贵的可伸缩性抛在脑后了。最近有很多人一直在问,为什么我们要把注意力从 HBase 上转移到 Cassandra 上去。我确认,确实有这样的变化,实际上我们基本上已经把代码移植到了 Cassandra 上了,这里我将给出解释。

为了那些不熟悉 NOSQL 的读者,后面的其他文章中,我会介绍为什么我们将会在未来几年中看到地震式的从 SQL 到 NOSQL 的迁移,这正和向云计算的迁移一样重要。后面的文章还会尝试解释为什么我认为 NOSQL 可能会是贵公司的正确选择。不过本文我只是解释我们选择 Cassandra 作为我们的 NOSQL 解决方案的选择。

免责声明——如果你正在寻找一个捷径来决定你的系统选择,你必须要明白,这可不是一个详尽而严格的比较,它只是概述了另一个初创团队在有限时间和资源的情况下的逻辑。

Cassandra 的血统是否预言了它的未来

我最喜欢的一个工程师们用来找 bug 的谒语是“广度优先而非深度优先”。这可以可能对那些解决技术细节的人来说很恼人,因为它暗示着如果他们只是看看的话,解决方法就会简单很多(忠告:只对那些能够原谅你的同事说这个)。我造出这个谒语的原因在于,我发现,软件问题中,如果我们强迫我们自己在进入某行代码的细节层面之前,先去看看那些高层次的考虑的话,可以节省大量时间。

所以,在谈论技术之前,我在做 HBase 和 Cassandra 之间的选择问题上先应用一下我的箴言。我们选择切换的技术结论可能已经可以预测了:Hbase和Cassandra有着迥异的血统和基因,而我认为这会影响到他们对我们的业务的适用性。

严格的说,Hbase 和它的支持系统源于著名的 Google BigTable 和 Google 文件系统设计(GFS 的论文发于 2003 年,BigTable 的论文发于 2006 年)。而 Cassandra 则是最近 Facebook 的数据库系统的开源分支,她在实现了 BigTable 的数据模型的同时,使用了基于 Amazon 的 Dynamo 的系统架构来存储数据(实际上,Cassandra 的最初开发工作就是由两位从 Amazon 跳槽到 Facebook 的 Dynamo 工程师完成的)。

在我看来,这些不同的历史也导致Hbase更加适合于数据仓库、大型数据的处理和分析(如进行Web页面的索引等),而 Cassandra 则更适合于实时事务处理和提供交互型数据。要进行系统研究来证明这个观点超出了本文的范畴,但我相信你在考虑数据库的时候总能发现这个差异的存在。

注意:如果你在寻找一个简单的证明,你可以通过主要 committer 的关注点来进行验证:大部分 HBase 的 committer 都为 Bing 工作(M$ 去年收购了他们的搜索公司,并允许他们在数月之后继续提交开源代码)。与之对应,Cassandra 的主要 committer 来自 Rackspace,用来可以自由获得的支持先进的通用的 NOSQL 的解决方案,用来和 Google, Yahoo, Amazon EC2 等提供的那些锁定在专有的 NOSQL 系统的方案相抗衡。

Malcolm Gladwell 会说只是根据这些背景的不同就可以简单地选择了 Cassandra。不过这是小马过河的问题。但当然,闭着眼睛就进行一个商业选择是相当困难的……

哪个 NOSQL数据库风头更劲?

另一个说服我们转向 Cassandra 的原因是我们社区中的大风向。如你所知,软件平台行业里,大者恒大——那些被普遍看好的平台,会有更多人聚集在这个平台周围,于是,从长远看,你可以得到更好的生态系统的支持(也就是说,大部分支持的软件可以从社区中获得,也有更多的开发者可以雇佣)。

如果从 HBase 开始时,我的印象就是它后面有巨大的社区力量,但我现在相信,Cassandra 更加强大。最初的印象部分来源于 StumpleUpon 和 Streamy 的两位 CTO 的两个非常有说服力的出色的讲演,他们是 Web 行业中两个在 Cassandra 成为一个可选系统之前的 HBase 的两个重要的贡献者,同时也部分来源于快速阅读了一篇名为“HBase vs Cassandra: NoSQL 战役!”的文章(大部分内容都被广泛证实了)。

势头是很难确证的,你不得不自己进行研究,不过我可以找到的一个重要的标志是 IRC 上的开发者动向。如果你在 freenode.org 上比较 #hbase 和 #cassandra 的开发这频道,你会发现 Cassandra 差不多在任何时候都有两倍的开发者在线。

如果你用考虑 HBase 一般的时间来考察 Cassandra,你就能发现 Cassandra 的背后确实有非常明显的加速势头。你可能还会发现那些逐渐出现的鼎鼎大名,如 Twitter,他们也计划广泛使用 Cassandra(这里)。

注:Cassandra 的网站看起来比 HBase 的好看多了,但认真的说,这可能不仅是市场的趋势。继续吧。

深入到技术部分: CAP 和 CA 与 AP 的神话

对于分布式系统,有个非常重要的理论(这里我们在讨论分布式数据库,我相信你注意到了)。这个理论被称为 CAP 理论,由 Inktomi 的 联合创始人兼首席科学家 Eric Brewer 博士提出。

这个理论说明,分布式(或共享数据)系统的设计中,至多只能够提供三个重要特性中的两个——一致性、可用性和容忍网络分区。简单的说,一致性指如果一个人向数据库写了一个值,那么其他用户能够立刻读取这个值,可用性意味着如果一些节点失效了,集群中的分布式系统仍然能继续工作,而容忍分区意味着,如果节点被分割成两组无法互相通信的节点,系统仍然能够继续工作。

Brewer教授是一个杰出的人物,许多开发者,包括 HBase 社区的很多人,都把此理论牢记在心,并用于他们的设计当中。事实上,如果你搜索线上的关于 HBase 和 Cassandra 比较的文章,你通常会发现,HBase 社区解释他们选择了 CP,而 Cassandra 选择了 AP ——毫无疑问,大多数开发者需要某种程度的一致性 (C)。

不过,我需要请你注意,事实上这些生命基于一个不完全的推论。CAP 理论仅仅适用于一个分布式算法(我希望 Brewer 教授可以统一)。但没有说明你不能设计一个系统,在其中的各种操作的底层算法选择上进行这种。所以,在一个系统中,确实一个操作职能提供这些特性中的两个,但被忽视的问题是在系统设计中,实际是可以允许调用者来选择他们的某个操作时需要哪些特性的。不仅如此,现实世界并不简单的划分为黑白两色,所有这些特性都可以以某种程度来提供。这就是 Cassandra。

这点非常重要,我重申:Cassandra 的优点在于你可以根据具体情况来选择一个最佳的折衷,来满足特定操作的需求。Cassandra 证明,你可以超越通常的 CAP 理论的解读,而世界仍然在转动。

我们来看看两种不同的极端。比如我必须从数据库中读取一个要求具有很高一致性的值,也就是说,我必须 100%保证能够读取到先前写入的最新的内容。在这种情况下,我可以通过指定一致性水平为“ALL”来从 Cassandra 读取数据,这时要求所有节点都有数据的一致的副本。这里我们不具有对任何节点失效和网络分裂的容错性。在另一个极端的方面,如果我不特别关心一致性,或仅仅就是希望最佳性能,我可以使用一致性级别“ONE”来访问数据。在这种情况下,从任意一个保存有这个副本的节点获取数据都可以——即使数据有三个副本,也并不在意其他两个有副本的节点是否失效或是否有不同,当然,这种情况下我们读到的数据可能不是最新的。

不仅如此,你不必被迫生活在黑白世界中。比如,在我们的一个特定的应用中,重要的读写操作通常使用“QUORUM”一致性级别,这意味着大部分存有此数据的节点上的副本是一致的——我这里是个简要描述,具体写你的 Cassandra 程序之前最好还是仔细研究一下。从我们的视角看,这这提供了一个合理的节点失效与网络分裂的耐受性,同时也提供了很高的一致性。而在一般情况下,我们使用前面提到的“ONE”一致性级别,者可以提供最高的性能。就是这样。

对我们来说,这是 Cassandra 的一个巨大的加分项目。我们不仅能轻易地调整我们的系统,也可以设计它。比如,当一定数量的节点失效或出现网络连接故障时,我们的大部分服务仍然可以继续工作,只有那些需要数据一致性的服务会失效。HBase并没有这么灵活,它单纯地追求系统的一个方面(CP),这让我再次看到了 SQL 开发者和查询优化人员们之间的那道隔阂——有些事情最好能够超越它,HBase!

In our project then, Cassandra has proven by far the most flexible system, although you may find your brain at first loses consistency when considering your QUORUMs.在我们的项目之后,卡桑德拉已被证明是迄今为止最灵活的系统,虽然你可能发现一致性第一失去你的大脑在考虑您的法定人数。

在我们的项目中,Cassandra 已经证明了它是有史以来最灵活的系统,虽然你可能在对这个问题进行投票(QUORUM)的时候发现的大脑失去了一致性。

什么时候单体会比模块化强?

Cassandra 和 HBase 的一个重要区别是, Cassandra 在每个节点是是一个单 Java 进程,而完整的 HBase 解决方案却由不同部分组成:有数据库进程本身,它可能会运行在多个模式;一个配置好的 hadoop HDFS 分布式文件系统,以及一个 Zookeeper 系统来协调不同的 HBase 进程。那么,这是否意味着 HBase 有更好的模块化结构呢?

虽然 HBase 的这种架构可能确实可以平衡不同开发团队的利益,在系统管理方面,模块化的 HBase 却无法视为一个加分项目。事实上,特别是对于一些小的初创公司,模块化倒是一个很大的负面因素。

HBase的下层相当复杂,任何对此有疑惑的人应该读读 Google 的 GFS 和 BigTable 的论文。即使是在一个单一节点的伪分布式模式下来架设 HBase 也很困难——事实上,我曾经费力写过一篇快速入门的教程(如果你要试试HBase的话看看这里)。在这个指南里你可以看到,设置好 HBase 并启动它实际包含了两个不同系统的手工设置:首先是 hadoop HDFS,然后才是 HBase 本身。

然后,HBase 的配置文件本身就是个怪兽,而你的设置可能和缺省的网络配置有极大的不同(在文章里我写了两个不同的Ubuntu的缺省网络设置,以及 EC2 里易变的 Elastic IP 和内部分配的域名)。当系统工作不正常的时候,你需要查看大量的日志。所有的需要修复的东西的信息都在日志里,而如果你是一个经验丰富的管理员的话,就能发现并处理问题。

但是,如果是在生产过程中出现问题,而你又没有时间耐心查找问题呢?如果你和我们一样,只有一个小的开发团队却有远大的目标,没有经历去 7*24 的进行系统监控管理会怎么样呢?

严肃地说,如果你是一个希望学习 NoSQL 系统的高级 DB 管理员的话,那么选择 HBase。这个系统超级复杂,有灵巧双手的管理员肯定能拿到高薪。

但是如果你们是一个向我们一样尽力去发现隧道尽头的小团队的话,还是等着听听别的闲话吧

胜在 Gossip

Cassandra 是一个完全对称的系统。也就是说,没有主节点或像 HBase 里的 region server 这样的东西——每个节点的角色是完全一样的。不会有任何特定的节点或其他实体来充当协调者的角色,集群中的节点使用称为 “Cossip” 的纯 P2P 通信协议来协调他们的行为。

对 Gossip 的详细描述和使用 Gossip 的模型超过了本文的内容,但 Cassandra 所采用的 P2P 通信模型都是论证过的,比如发现节点失效的消息传播到整个系统的时间,或是一个客户应用的请求被路由到保存数据的节点的时间,所有这些过程所消耗的时间都毫无疑问的非常的短。我个人相信,Cassandra 代表了当今最振奋的一种 P2P 技术,当然,这和你的 NOSQL 数据库的选择无关。

那么,这个基于 Gossip 的架构究竟给 Cassandra 用户带来什么显示的好处呢。首先,继续我们的系统管理主体,系统管理变得简单多了。比如,增加一个新节点到系统中就是启动一个 Cassandra 进程并告诉它一个种子节点(一个已知的在集群中的节点)这么简单。试想当你的分布式集群可能运行在上百个节点的规模上的时候,如此轻易地增加新节点简直是难以置信。更进一步,当有什么出错的时候,你不需要考虑是哪种节点出了问题——所有节点都是一样的,这让调试成为了一个更加易于进行且可重复的过程。

第二,我可以得出结论,Cassandra 的 P2P 架构给了它更好的性能和可用性。这样的系统中,负载可以被均衡地三步倒各个节点上,来最大化潜在的并行性,这个能力让系统面临网络分裂和节点失效的时候都能更加的无缝,并且节点的对称性防止了 HBase 中发现的那种在节点加入和删除时的暂时性的性能都懂(Cassandra 启动非常迅速,并且性能可以随着节点的加入而平滑扩展)。

如果你想寻找更多更多的证据,你会对一个原来一直关注 hadoop 的小组(应该对 HBase 更加偏爱)的报告很感兴趣……

一份报告胜过千言万语。我是指图表

Yahoo!进行的第一个 NOSQL 系统的完整评测。研究似乎证实了 Cassandra 所享有的性能优势,从图表上看,非常倾向于 Cassandra。

目前这些论文还是草稿,你可以从这里找到这些论文:
http://www.brianfrankcooper.net/pubs/ycsb-v4.pdf
http://www.brianfrankcooper.net/pubs/ycsb.pdf

注意:这份报告中 HBase 仅在对一个范围的记录进行扫描这一项上优于 Cassandra。虽然 Cassandra 团队相信他们可以很快达到 HBase 的时间,但还是值得指出,在通常的 Cassandra 配置中,区间扫描几乎是不可能的。我建议你可以无视这一点,因为实际上你应该在 Cassandra 上面来实现你自己的索引,而非使用区间扫描。如果你对区间扫描和在 Cassandra 中存储索引相关问题有兴趣,可以看我的这篇文章

最后一点: 这篇文章背后的 Yahoo!研究团队正尝试让它们的评测应用通过法律部门的评估,并将它发布给社区。如果他们成功的话,我当然希望他们成功,我们将能够看到一个持续的竞争场面,不论 HBase 还是 Cassandra 无疑都会进一步提高他们的性能。

锁和有用的模块性

毫无疑问,你会从 HBase 阵营听到这样的声音:HBase 的复杂结构让它可以提供 Cassandra 的 P2P 架构无法提供的东西。其中一个例子可能就是 Hbase 提供给开发者行锁机制,而 Cassandra 则没有(在 HBase 中,因为数据副本发生在 hadoop 底层,行锁可以由 region server 控制,而在 Cassandra 的 P2P 架构中,所有节点都是平等的,所以也就没有节点可以像一个网管囊样负责锁定有副本的数据)。

不够,我还是把这个问题返回到关于模块化的争论中,这实际是对 Cassandra 有理的。Cassandra 通过在对称节点上分布式存储数据来实现了 BigTable 的数据模型。它完整地实现了这些功能,而且是以最灵活和高性能的方式实现的。但如果你需要锁、事务和其它功能的话,这些可以以模块的方式添加到你的系统之中——比如,我们发现我们可以使用 Zookeeper 和相关的工具来很简单地为我们的应用提供可扩展的锁功能(对于这个功能,Hazelcast 等系统可能也可以实现这个功能,虽然我们没有进行研究)。

通过为一个窄领域目的来最小化它的功能,对我来说,Cassandra 的设计达到了它的目的——比如前面指出可配置的 CAP 的折衷。这种模块性意味着你可以依据你的需求来构建一个系统——需要锁,那么拿来 Zookeeper,需要存储全文索引,拿来 Lucandra ,等等。对于我们这样的开发者来说,这意味着我们不必部署复杂度超出我们实际需要的系统,给我们提供了更加灵活的构建我们需要的应用的终极道路。

MapReduce,别提 MapReduce!

Cassandra 做的还不够好的一件事情就是 MapReduce!对于不精通此项技术同学简单的解释一句,这是一个用于并行处理大量数据的系统,比如从上百万从网络上抓取的页面提取统计信息。MapReduce 和相关系统,比如 Pig 和 Hive 可以和 HBase 一起良好协作,因为它使用 HDFS 来存储数据,这些系统也是设计用来使用 HDFS 的。如果你需要进行这样的数据处理和分析的话,HBase 可能是你目前的最佳选择。

记住,这就像小马过河!

因此,我停止了对 Cassandra  的优点的赞美,实际上,HBase 和 Cassandra 并不一定是一对完全的竞争对手。虽然它们常常可以用于同样的用途,和 MySQL 和 PostgreSQL 类似,我相信在将来它们将会成为不同应用的首选解决方案。比如,据我所知 StumbleUpon 使用了 HBase 和 hadoop MapReduce 技术,来处理其业务的大量数据。Twitter 现在使用 Cassandra 来存储实时交互的社区发言,可能你已经在某种程度上使用它了。

作为一个有争议的临别赠言,下面我们进入下一个话题。

注意:在继续下一个小节之前,我要指出,Cassandra 在 0.6 版本会有 hadoop 支持,所以 MapReduce 整合能获得更好的支持。

兄弟,我不能失去数据…

作为先前 CAP 理论争议的一个可能结果,可能有这样的印象,HBase 的数据似乎比 Cassandra 中的数据更安全。这是我希望揭露的最后一个关于 Cassandra 的秘密,当你写入新数据的时候,它实际上立刻将它写入一个将要存储副本的仲裁节点的 commit log 当中了,也被复制到了节点们的内存中。这意味着如果你完全让你的集群掉电,只可能会损失极少数据。更进一步,在系统中,通过使用 Merkle tree 来组织数据的过分不一致(数据熵),更加增加了数据的安全性:)

事实上,我对 HBase 的情况并不是非常确切——如果能有更细节的情况,我回尽快更新这里的内容的——但现在我的理解是,因为 hadoop 还不支持 append,HBase 不能有效地将修改的块信息刷入 HDFS (新的对数据变化会被复制为多个副本并永久化保存)。这意味着会有一个更大的缺口,你最新的更改是不可见的(如果我错了,可能是这样,请告诉我,我回修正本文)。

所以,尽管希腊神话中的 Cassandra 非常不幸(译注:Cassandra 是希腊神话里,特洛伊的那个可怜的女先知的名字,如果你不知道详情的话,可以参考wiki),但你的 Cassandra 中的数据不会有危险。

注意:Wade Amold 指出, hadoop .21 很快就会发布,其中将会解决 HBase 的这个问题。

Switch to our mobile site