cassandra理解

原文: 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日php

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

Cassandra 数据模型实际和传统的数据库差别很是大,足够让人眩晕,并且不少误解都须要修正。git

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

问题在于很难去用类比的方法来确切解释一个新的东西,并且若是比较的不许确的话经常把人搞糊涂。我仍然指望有人能解释清楚这个数据模型,但同时我以为确切的例子可能更容易说明白一些。web

Twitter

尽管 Twitter 自己就是 Cassandra 的一个实际的应用场景,它仍然是一个不错的教学实例,由于它众所周知并且易于抽象。在例子中,和不少站点同样,每一个用户都有一份用户数据(显示名称、密码、email等),这些信息连接到朋友(译注:用户follow的人)和 follower(译注:follow用户的人)。此外,若是没有那些短 tweets 的话也就不是 twitter 了,tweet每条140个字符,它们都关联着诸如时间戳和唯一的id这样的元数据,这个id咱们能够从URL里看到。sql

如今咱们在一个关系数据库里来直接进行建模,咱们首先须要一个表来存放用户。数据库

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

咱们还须要两张表来存储一对多的follow关系。apache

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。nosql

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 邮件列表上也有不少能够提供帮助的人。


权限: 公开 来自:labs                    
相关文章
相关标签/搜索