Posts Tagged ‘cloud’

中国移动研究院的Hadoop开发工作

September 5th, 2010

下面是我在 HIC2010 大会的幻灯片,相关内容欢迎进一步交流。

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 开始逐步走向现实。

中国移动大云非官方广告桌面

August 13th, 2010

昨天晚上跟风PS的,嗯,凡客的广告影响很大啊,呵呵

love_big_cloud

呵呵,参考凡客的那个韩寒的广告,灵感是受到酷壳的启发,这个BIGCLOUD的针织马甲还不错吧,哈哈。

[译文]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 的这个问题。

[译文] Observers: 让ZooKeeper更具可伸缩性

December 22nd, 2009

原文: http://www.cloudera.com/blog/2009/12/15/observers-making-zookeeper-scale-even-further/
Tuesday, December 15th, 2009 at 10:30 am by Henry Robinson
译文: 王旭(http://wangxu.me, @gnawux)2009年12月16,21日

看过我们之前相关文章的读者都知道,ZooKeeper是一个分布式协作服务,用于实现锁和并发事务排队等协作原语。ZooKeeper 的一个优势是可伸缩性(Scalability)。五台或七台机器集群可以满足很多大型应用的需求。

最近我们给ZooKeeper增加了一个新的特性,进一步增强了它可伸缩性——一种新的称为 Observers 的服务器。在这篇文章中,我想要介绍一下添加这个特征的动机,并解释这个服务器如何帮助我们的系统更具有可伸缩性。可伸缩性对不同的人意味着不同的事情,而在这里是说,如果我们的工作负载可以通过给系统分配更多的资源来分担,那么这个系统就是可伸缩的;一个不可伸缩的系统却无法通过增加资源来提升性能,甚至会在工作负载增加时,性能急剧下降。

要了解 Observers 为何能影响 ZooKeeper 的可伸缩性,我们需要首先了解一下这个服务时如何工作的。宽泛地说,ZooKeeper 集群上的任何操作不是读操作就是写操作。ZooKeeper 确保所有读和写操作在所有客户端看来,都具有完全相同的顺序,这样他们就不会为操作的顺序而疑惑了。

在提供强一致性保障的同时,ZooKeeper同时给出高可用性承诺,这可以被简单地解释为它可以在多台服务器失效的情况下仍然为客户端提供服务。ZooKeeper使用一个传统的手段来达到可用性——通过将数据读写分布到几台机器上来实现,这样如果一台失效了,其它的可以接管它的服务,而无需让客户端更聪明。

然而,一致性和可用性这两个属性是很难同时达到的,目前,ZooKeeper 必须确保集群中的每个副本都对读写操作是顺序性的。它通过一个一致性协议来达到这一目标。简单地说,这个协议由一个选定的领导者将新操作高速其他服务器,所有节点投票支持并反馈给领导。一旦领导节点收集到过半数的投票,它就认为投票已经获得了通过,并将进一步消息传送给服务器们,以使他们可以继续工作,将操作提交到内存中。

这个从始至终的数据流如下图所示。客户进程将一个值提交给它连接的服务器。服务器将消息转送给领导节点,它发起这个一致性协议,一旦最初的服务器从领导节点得到结果,它就可以返回给用户了。

Simplified flow of a ZooKeeper write request

图1:简化的写请求工作流

Observers 的需求源于 ZooKeeper 服务器在上述协议中实际扮演了两个角色。它们从客户端接受连接与操作请求,之后对操作结果进行投票。这两个职能在 ZooKeeper集群扩展的时候彼此制约。如果我们希望增加 ZooKeeper 集群服务的客户数量(我们经常考虑到有上万个客户端的情况),那么我们必须增加服务器的数量,来支持这么多的客户端。然而,从一致性协议的描述可以看到,增加服务器的数量增加了对协议的投票部分的压力。领导节点必须等待集群中过半数的服务器响应投票。于是,节点的增加使得部分计算机运行较慢,从而拖慢整个投票过程的可能性也随之提高,投票操作的会随之下降。这正是我们在实际操作中看到的问题——随着 ZooKeeper 集群变大,投票操作的吞吐量会下降。

所以,这让我们不得不在增加客户节点数量的期望和我们希望保持较好吞吐性能的期望间进行权衡。要打破这一耦合关系,我们引入了不参与投票的服务器,称为 Observers。 Observers 可以接受客户端的连接,将写请求转发给领导节点。但是,领导节点不会要求 Observers 参加投票。相反,Observers 不参与投票过程,仅仅在上述第3歩那样,和其他服务节点一起得到投票结果。

这个简单的扩展给 ZooKeeper 的可伸缩性带来了全新的镜像。我们现在可以加入很多 Observers 节点,而无须担心严重影响写吞吐量。规模伸缩并非无懈可击——协议中的一歩(通知阶段)仍然与服务器的数量呈线性关系。但是,这里的穿行开销非常低。我们可以认为在通知服务器阶段的开销无法成为主要瓶颈。

Observers Write Throughput Benchmark

图2: Observers 写吞吐量 Benchmark

图2 显示了一个简单评测的结果。纵轴是我能够从一个单一的客户端发出的每秒钟同步写操作的数量(一个调优的 ZooKeeper 可以得到更好的每秒钟操作数——这里我们更感兴趣的是相对值)。横轴是 ZooKeeper 集群的尺寸。蓝色的是每个服务器都是 voting 服务器的情况,而绿色的则只有三个是 voting 服务器,其它都是 Observers。图中显示,我们扩充 Observers,写性能几乎可以保持不便,但如果同时扩展 voting 节点的数量的话,性能会明显下降。显然 Observers 是有效的。

Observers 同样提升读性能的可伸缩性

增加客户端的数量是 Observers 的一个重要用例,但是实际上它们还给集群带来很多其它的好处。

作为一个优化,ZooKeeper 服务器可以直接读取它们的本地数据存储,而无需经过投票过程,这面临一定的“时光旅行”风险,可能在读到新值之后又读到老值,但这只在服务器故障时才会发生。事实上,在这种情况下客户端可以请求一个 ‘sync’ 操作来保证下一个值是最新的。

因此,在大量读操作的工作负载下,Observers 是个巨大的性能提升。写操作直接进入标准的投票路径,这样,与客户端可扩展性类似,提高投票服务器数量来承担读操作会影响写性能。Observers 允许我们将读性能和写性能分开。这适用于 ZooKeeper 的很多应用场景,大部分客户端很少写,但经常读。

Observers 提供了广域网能力

Observers 还能做更多。Observers 对于跨广域网连接的客户端来说是很好的候选方案。这有三个原因。为了获得很好的读性能,有必要让客户端离服务器尽量近,这样往返时延不会太高。然而,将 ZooKeeper 集群分散到两个集群是非常不可取的设计,因为良好配置的 ZooKeeper 应该让投票服务器间用低时延连接互联——否则我们将会遇到上面提到的低反映速度的问题。

而 Observers 可以被部署在需要访问 ZooKeeper 的任意数据中心中。这样,投票协议不会受到数据中心间链路的高时延的影响,性能得到提升。投票过程中 Observers 和领导节点间的消息远少于投票服务器和领导节点间的消息。这有助于在远程数据中心高写负载的情况下降低带宽需求。

最后,由于Observers即使失效也不会影响到投票集群,这样如果数据中心间链路发生故障,不会影响到服务本身的可用性。这种故障的发生概率要远高于一个数据中心中机架间的连接的故障概率,所以不依赖于这种链路是个优点。

如何开始使用 Observers

Observers 还没有成为某个 ZooKeeper release 的一部分,所以要使用它,你需要从 Subversion trunk 获取源代码。

下面的内容提取自 Observers 用户手册,可以在源代码的 docs/zooKeeperObservers.html 文件中看到。

如何使用 Observers

注意,在ZOOKEEPER-578 解决之前,你必须在每个服务器的配置文件中设置 electionAlg=0 。否则当你启动服务的时候会抛出一个异常。

原因:因为 Observers 并不参与领导节点的选举,它们依赖于投票 Followers 来获知领导的变动。目前,只有基本选举算法启动一个线程来响应 Observers 确定当前领导的请求。其他 JIRA 上的工作将会让其他所有的领导选举协议都支持这一功能。

设置 ZooKeeper 使用 Observers 非常简单,只需要在配置文件中有两处改动。首先是每个 Observer 的配置文件中都要有这么一行:

peerType=observer

这行让服务器作为一个 Observer 来工作。之后,在每个服务器配置文件中,你必须在服务器定义行给每个 Observer 加入 :o bserver  。比如:

server.1:localhost:2181:3181:observer

这让每个其他服务器知道 server.1 是一个 Observer,就不会期望它进行投票了。这就是要加入一个 Observer 的时候,所有你需要做的配置。现在可以将它作为一个正常的 Follower 来看待了。可以这么试试:

bin/zkCli.sh -server localhost:2181

这里 localhost:2181 是 Observer 在每个配置文件中指定的主机名和端口号。你应该看到命令行提示了,这时就可以查询 Zookeeper 服务了。

下一步工作

Observers 特性还有很多工作要做。短期内,我们将致力于让 Observers 与 ZooKeeper 中的所有领导选举算法完全兼容 —— 我们希望这个可以在未来几天内完成。长期看,我们希望能研究一下进行性能优化,比如基于 Observers 的集群的批量和离线读取,来更好的利用 Observers 不像一般 ZooKeeper 服务器一样严格要求时延的好处。

我们希望 Observers 能进入明年年初的 ZooKeeper 3.3.0。我们会很高兴能听到你的反馈,不管是在邮件列表里还是直接发送email。ZooKeeper 长期招募贡献者,我们有足够多的有趣的问题来解决,所以,如果你想加入的话请联系我,我回很高兴能帮你开始的。

[译文] NOSQL们背后的共有原则

December 20th, 2009

原文: http://natishalom.typepad.com/nati_shaloms_blog/2009/12/the-common-principles-behind-the-nosql-alternatives.html
Posted by Nati Shalom at 12:01 PM Dec 15, 2009
译文: 王旭(http://wangxu.me , @gnawux)2009年12月16/19日

几个星期之前,我写了一篇文章描述了常被称作 NOSQL 的一类新型数据库的背后驱动。几个星期之前,我在Qcon上发表了一个演讲,其中,我介绍了一个可伸缩(scalable)的 twitter 应用的构建模式,在我们的讨论中,一个显而易见的问题就是数据库的可扩展性问题。要解答这个问题,我试图寻找隐藏在各种 NOSQL 之后的共有模式,并展示他们是如何解决数据库可扩展性问题的。在本文中,我将尽力勾勒出这些共有的原则。

nosql

实现们的共有原则

假设失效是必然发生的

与我们先前通过昂贵硬件之类的手段尽力去避免失效的手段不同,NOSQL实现都建立在硬盘、机器和网络都会失效这些假设之上。我们需要认定,我们不能彻底阻止这些时效,相反,我们需要让我们的系统能够在即使非常极端的条件下也能应付这些失效。Amazon S3 就是这种设计的一个好例子。你可以在我最近的文章 Why Existing Databases (RAC) are So Breakable! 中找到进一步描述。哪里,我介绍了一些来自 Jason McHugh 的讲演的面向失效的架构设计的内容(Jason 是在 Amazon 做 S3 相关工作的高级工程师)。

对数据进行分区

通过对数据进行分区,我们最小化了失效带来的影响,也将读写操作的负载分布到了不同的机器上。如果一个节点失效了,只有该节点上存储的数据受到影响,而不是全部数据。

保存同一数据的多个副本

大部分 NOSQL 实现都基于数据副本的热备份来保证连续的高可用性。一些实现提供了 API,可以控制副本的复制,也就是说,当你存储一个对象的时候,你可以在对象级指定你希望保存的副本数。在 GigaSpaces,我们还可以立即复制一个新的副本到其他节点,甚至在必要时启动一台新机器。这让我们不比在每个节点上保存太多的数据副本,从而降低总存储量以节约成本。

你还可以控制副本复制是同步还是异步的,或者两者兼有。这决定了你的集群的一致性、可用性与性能三者。对于同步复制,可以牺牲性能保障一致性和可用性(写操作之后的任意读操作都可以保证得到相同版本的数据,即使是发生失效也会如此)。而最为常见的 GigaSpaces 的配置是同步副本到被分界点,异步存储到后端存储。

动态伸缩

要掌控不断增长的数据,大部分 NOSQL 实现提供了不停机或完全重新分区的扩展集群的方法。一个已知的处理这个问题的算法称为一致哈希。有很多种不同算法可以实现一致哈希。

一个算法会在节点加入或失效时通知某一分区的邻居。仅有这些节点受到这一变化的影响,而不是整个集群。有一个协议用于掌控需要在原有集群和新节点之间重新分布的数据的变换区间。

另一个(简单很多)的算法使用逻辑分区。在逻辑分区中,分区的数量是固定的,但分区在机器上的分布式动态的。于是,例如有两台机器和1000个逻辑分区,那么每500个逻辑分区会放在一台机器上。当我们加入了第三台机器的时候,就成了每 333 个分区放在一台机器上了。因为逻辑分区是轻量级的(基于内存中的哈希表),分布这些逻辑分区非常容易。

第二种方法的优势在于它是可预测并且一致的,而使用一致哈希方法,分区之间的重新分布可能并不平稳,当一个新节点加入网络时可能会消耗更长时间。一个用户在这时寻找正在转移的数据会得到一个异常。逻辑分区方法的缺点是可伸缩性受限于逻辑分区的数量。

更进一步的关于这一问题的讨论,建议阅读 Ricky Ho 的文章 NOSQL Patterns

查询支持

在这个方面,不同的实现有相当本质的区别。不同实现的一个共性在于哈希表中的 key/value 匹配。一些市县提供了更高级的查询支持,比如面向文档的方法,其中数据以 blob 的方式存储,关联一个键值对属性列表。这种模型是一种无预定义结构的(schema-less)存储,给一个文档增加或删除属性非常容易,无需考虑文档结构的演进。而 GigaSpaces 支持很多 SQL 操作。如果 SQL查询没有指出特定的简直,那么这个查询就会被并行地 map 到所有的节点去,由客户端完成结果的汇聚。所有这些都是发生在幕后的,用户代码无需关注这些。

使用 Map/Reduce 处理汇聚

Map/Reduce 是一个经常被用来进行复杂分析的模型,经常会和 Hadoop 联系在一起。 map/reduce 常常被看作是并行汇聚查询的一个模式。大部分 NOSQL 实现并不提供 map/reduce 的内建支持,需要一个外部的框架来处理这些查询。对于 GigaSpaces 来说,我们在 SQL 查询中隐含了对 map/reduce 的支持,同时也显式地提供了一个称为 executors 的 API 来支持 map/reduce。在质疑模型中,你可以将代码发送到数据所在地地方,并在该节点上直接运行复杂的查询。

这方面的更多细节,建议阅读 Ricky Ho 的文章 Query Processing for NOSQL DB

基于磁盘的和内存中的实现

NOSQL 实现分为基于文件的方法和内存中的方法。有些实现提供了混合模型,将内存和磁盘结合使用。两类方法的最主要区别在于每 GB 成本和读写性能。

最近,斯坦福的一项称为“The Case for RAMCloud”的调查,对磁盘和内存两种方法给出了一些性能和成本方面的有趣的比较。总体上说,成本也是性能的一个函数。对于较低性能的实现,磁盘方案的成本远低于基于内存的方法,而对于高性能需求的场合,内存方案则更加廉价。

内存云的显而易见的缺点就是单位容量的高成本和高能耗。对于这些指标,内存云会比纯粹的磁盘系统差50到100倍,比使用闪存的系统差5-10倍(典型配置情况和指标参见参考文献[1])。内存云同时还比基于磁盘和闪存的系统需要更多的机房面积。这样,如果一个应用需要存储大量的廉价数据,不需要高速访问,那么,内存云将不是最佳选择。
然而,对于高吞吐量需求的应用,内存云将更有竞争力。当使用每次操作的成本和能量作为衡量因素的时候,内存云的效率是传统硬盘系统的 100 到 1000 倍,是闪存系统的 5-10 倍。因此,对于高吞吐量需求的系统来说,内存云不仅提供了高性能,也提供了高能源效率。同时,如果使用 DRAM 芯片提供的低功耗模式,也可以降低内存云的功耗,特别是在系统空闲的时候。此外,内存云还有一些缺点,一些内存云无法支持需要将数据在多个数据中心之间进行数据复制。对于这些环境,更新的时延将主要取决于数据中心间数据传输的时间消耗,这就丧失了内存云的时延方面的优势。此外,跨数据中心的数据复制会让内存云数据一致性更能难保证。不过,内存云仍然可以在夸数据中心的情况下提供低时延的读访问。

仅仅是炒作?

近来我见到的最多的问题就是 “NOSQL 是不是就是炒作?” 或 “NOSQL 会不会取代现在的数据库?”

我的回答是——NOSQL 并非始于今日。很多 NOSQL 实现都已经存在了十多年了,有很多成功案例。我相信有很多原因让它们在如今比以往更受欢迎了。首先是由于社会化网络和云计算的发展,一些原先只有很高端的组织才会面临的问题,如今已经成为普遍问题了。其次,已有的方法已经被发现无法跟随需求一起扩展了。并且,成本的压力让很多组织需要去寻找更高性价比的方案,并且研究证实基于普通廉价硬件的分布式存储解决方案甚至比现在的高端数据库更加可靠。(进一步阅读)所有这些导致了对这类“可伸缩性优先数据库”的需求。这里,我引用 AWS团队的接触工程师、VP, James Hamilton 在他的文章 One Size Does Not Fit All 中的一段话:

“伸缩性优先应用是那些必须具备无限可伸缩性的应用,能够不受限制的扩展比更丰富的功能更加重要。这些应用包括很多需要高可伸缩性的网站,如 Facebook, MySpace, Gmail, Yahoo 以及 Amazon.com。有些站点实际上使用了关系型数据库,而大部分实际上并未使用。这些服务的共性在于可扩展性比功能公众要,他们无法泡在一个单一的 RDBMS 上。”

总结一下——我认为,现有的 SQL 数据库可能不会很快淡出历史舞台,但同时它们也不能解决世上的所有问题。NOSQL 这个名词现在也变成了 Not Only SQL,这个变化表达了我的观点。

参考:

[译文] Avro: 大数据的数据格式

November 3rd, 2009

Monday, November 2nd, 2009 at 8:00 am by Doug Cutting, filed under data collection, general, hadoop, mapreduce.
王旭 [ gnawux(at)gmail.com , @gnawux, http://wangxu.me ]于11月2-3日译

译注:Doug Cutting 是 Hadoop 的大佬,目前在 Cloudera,Avro 基本上将成为未来 Hadoop 的数据描述和 RPC 的基础,今天看到这篇,就立刻翻译了,水平有限且译的比较草,请见谅,且欢迎纠错。

Avor 是 Apache 的 Hadoop 项目族的一个新成员。Avro 定义了一种用于支持大数据应用的数据格式,并为这种格式提供了不同的编程语言的支持。

背景

我们希望处理大数据的应用可以更加动态化:人们应该能够快速的从不同的数据源合并数据集。我们希望能够让新颖的、创造性的数据分析变得更方便。比如说,有人需要完美地将销售各个网点的交易、网站的访问量以及外部的统计数据关联在一起,而不需要很多的准备工作。这应该可以使用脚本和交互工具即时完成。

目前的数据格式通常都做的不是很好。XML 和 JSON 可以承载很多信息,但它们本身就很大,处理起来很慢。当你处理上 PB 的数据的时候,尺寸和速度绝对是个大问题。

Google 使用一个称为 Protocol Buffers 的系统来解决这个问题。(还有其他的系统,比如 Thrift,与 Protocol Buffers 很类似,我不会在这里深入讨论它们,但我对 Protocol Buffers 的评价对它们同样适用。)Google 已经开放了 Protocol Buffers,但它对我们的目的来说也不完美。

通用数据

在 Protocol Buffers 中,用户首先定义数据结构,然后生成可以有效读写这些数据的代码。不过,如果你需要在一个脚本语言中直接使用 Protocol Buffer 的数据,你必须首先确定数据结构定义的位置;为它生成代码;最后,在获取数据之前装载代码。这么做可能还算是不错,但如果我们想要一个能浏览任意数据集的通用工具,它将不得不首先定位定义,再为每个数据集生成与装在代码。这让很多本来简单的事情变得复杂了。

与 Protocol Buffer 不同,Avro 格式将数据结构的定义以一种易于处理的形式存储在数据之中。这样,Avro 的实现可以在运行时使用这些定义,将数据以一种通用的方式展现给应用,而不需要生成代码。

代码生成在 Avro 中是可选的:如果某些编程语言要对某些数据结构是由代码生成的话也相当不错,比如需要频繁串行化的数据类型。但是,对于像 HivePig 这样的脚本来说,代码生成可能会是一种过分的负担,所以,Avro 不需要代码生成。

把数据结构定义存储在数据中的一个附加的好处是允许数据可以被更快和更小巧地存储。Protocol Buffers 为数据增加了注释,以保证在定义和数据不完全匹配的时候仍然能够得到处理。然而这些注释会让数据略微增大、处理稍稍变慢。一些评测结果显示,Avro 这样不需要这些注释的数据和其他串行化系统相比,更小而且处理起来更快。

Avro Schemas

Avro 使用 JSON 来定义数据结构的 schema。比如,一个二维平面上的点可以定义为如下的 Avro 记录:


{"type": "record", "name": "Point",
"fields": [
  {"name": "x", "type": "int"},
  {"name": "y", "type": "int"},
]
}

指针的每个实例都包含两个整数,不包含附加的每个记录或每个域的注释。整数使用变长 zig-zag 编码存储。所以小的正负值可能只需要两个字节:100个点可能只需要两百个字节。

在记录和数值类型之外,Avro 支持数组、map、每句、变长与定长二进制数据以及字符串。它还定义了一个容器文件格式,这样可以更好的支持 MapReduce 和其他大数据处理框架。对于更多的细节信息,可以参考 Avro 的规范

兼容性

应用程序都在演进,在这一过程中数据结构是可以改变的。我们希望应用的新版本仍然能够处理老版本创建的数据,反之亦然。Avro 和 Protocol Buffers 处理这个问题的方式比较类似。当应用需要的域没有出现的时候,Avro 会提供一个 schema 中规定的缺省值。Avro 忽略掉数据中的意外值。这并不能处理所有的后向兼容问题,但它让大部分的兼容性问题更容易处理。

RPC

Avro 也定义了一个 RPC 协议。尽管 RPC 中使用的数据类型和数据集中的类型通常是不同的,使用一个通用的串行化系统仍然是有用的。大数据需要一个分布式的基于 RPC 的框架。所以,在我们需要处理数据集文件的所有地方,我们也需要能够使用 RPC。这样,将 RPC 和数据集都建立在同一个基础之上将会最小化地减少那些代码可以处理数据,却无法使用分布式框架来这么做的可能性。

与 Hadoop 集成

我们希望在 Hadoop MapReduce 中可以简单地使用 Avro 数据。目前这项工作仍然在进展中。目前 issue MAPREDUCE-1126MAPREDUCE-815 在处理这个问题。

注意,Avro 数据结构可以定义它们的排序方式,所以一个编程语言中创建的复杂的数据可以在另一个语言中被排序。Avro 可以在不还原序列化的情况下进行排序,这可以加快处理速度。

我们希望 Avro 可以替换掉 Hadoop 中现有的 RPC。Hadoop 目前需要客户端和服务器必须使用相同版本的 Hadoop。我们希望使用 Avro 可以允许一个 Hadoop 应用能和多个运行不同版本的 HDFS 和/或 MapReduce 的多个集群交互工作。

Finally, we hope that Avro will permit Hadoop applications to be more easily written in languages besides Java.  For example, once Hadoop’s built on Avro, we hope to support native MapReduce and HDFS clients in languages like Python, C and C++.

最后,我们希望 Avro 可以允许 Hadoop 应用更容易的使用 Java 之外的语言开发。比如,一旦 Hadoop 构建在 Avro 之上,我们希望支持 Python,C 和 C++ 等语言的 HDFS 和 MapReduce 的原生客户端。

Hadoop World 访谈

很多话题都已经包含在了我最近在 Hadoop World 的访谈,我很乐于在这篇 blog 里包含这段视频。(译注:译者撞墙看不到视频,不贴了哈。)

移动云计算我之见

August 27th, 2008

前不久钱五哥曾经论述过云计算,虽然同属一个研究小组,但我们的意见并不完全相同,作为小字辈,斗胆在这里论述一下我的个人观点。

何谓“云计算”?综合各方观点,我仍然坚持认为目前通行的“云计算”是一种商业模式或说服务模式,即,更细粒度的计算能力的贩售方式——用户按需购买所需的计算资源(计算能力以及存储资源),而无需关注计算服务的实现是由什么计算设施完成的。

基于这样一种商业模式,“云计算”的技术应该是一种可以提供满足用户需求的计算能力的技术,尽管由于 Google 的强大影响力,Google 的基础设施常常被认为是“云计算”的代名词,但任何一种可以用于满足用户计算能力需求的技术都可以成为“云”中的技术。

作为“云计算”的支撑技术,需要的是:低成本的计算服务——这方面的杰出代表就是 Google;适用于用户需求的计算模型,仍以 Google 为例,大规模的并行化的 MapReduce 和相应的基础设施就是很适合某类计算。

上面的要点就是:“云计算”是要考虑需求的,是按需的计算。

说到这里,我们实际需要思考的一个问题是——移动不是 Google,移动需要自己的蓝海,移动的计算系统需要什么样的“云”(或者是否需要)?移动的客户需要我们提供什么样的“云”?这几个问题我甚至不知道有没有人思考过。

先 从移动自己的需求说起,移动的支撑系统消耗大量的计算资源,是否有必要引入这种云计算?长远来看,支撑系统全面构建在“云”上有利于资源配置的优化和“绿 色计算”,尽管这个移植可能不是一蹴而就的,就像SOA的引入一样。但近期看,目前的技术仍然很难将低成本和高性能、高可靠性、高可用性完美结合起来,很 难直接应用于生产系统中,但其他非生产系统仍是有可能的。不过,不论难易,展开研究、进行尝试都是有益的。

对移动用户而言,从盈利角度看,主要应该是集团客户,应该考虑如何将集团业务和计算服务结合起来;从应用模式看,主要应该是个人性和位置相关性业务和个人计算服务的结合。通信和内容/计算服务从来都是相互促进的,提供“云计算”服务也可能会产生类似效应。

嗯,个人是个技术爱好者,不是营销专家或专业“观察家”、“评论家”,以上言论纯粹业余水平的闲聊,请拍。

Switch to our mobile site