对于熟悉SQL的人转向Cassandra/NoSQL的概述

4
多年来处理关系型数据库,我已经习惯了考虑表、列和行。但Cassandra仍然让我感到困惑。
我理解它更多的是持久化映射,你只能查询这些映射的唯一键,但我的理解还不完整。
是的,我已经读了文档。但是,有人能给我一个很好、简洁的描述,说明Cassandra如何与SQL数据库结构不同吗?我的意思是,从1000英尺的高度来看,它是如何工作的?
例如,在eBay技术博客中,它说:
不要把它当作关系表。 相反,考虑嵌套、排序的映射数据结构。 来源:http://www.ebaytechblog.com/2012/07/16/cassandra-data-modeling-best-practices-part-1/ 我差不多完全懂了......但还不够。
这些都是很好的答案。我加了一份赏金,希望能激励更多人给出更权威的答复。

1
参考一下,我认为你一定读过Jay Patel的博客文章http://www.ebaytechblog.com/2012/07/16/cassandra-data-modeling-best-practices-part-1/。 - Niels Bech Nielsen
3个回答

6
注意:由于原问题涉及到Cassandra,因此本答案将重点讨论它。尽管Cassandra和其他非关系型(NoSQL)数据存储通常共享类似的概念,但不能假定这里详细介绍的想法适用于其他非关系型数据存储。
最好的方法是记住,按照查询构建数据模型是标准实践(对于Cassandra)。主要区别在于,RDBMS表的构建主要关注数据的高效存储。在Cassandra(非关系型)世界中,主要关注点转向了你想要查询的样子。通常,这可能意味着以多种方式键入相同的冗余数据……这很好。这在DataStax文档比较Cassandra数据模型和关系数据库中有解释。
假设我有以下用户表:
CREATE TABLE users (
    username TEXT,
    firstname TEXT,
    lastname TEXT,
    phone TEXT,
    PRIMARY KEY (username));

插入一些样本数据后,该表格如下:

username  | firstname | lastname | phone
------------------------------------------------
mreynolds | Malcolm   | Reynolds | 111-555-1234
jcobb     | Jayne     | Cobb     | 111-555-3464
sbook     | Derial    | Book     | 111-555-2349
stam      | Simon     | Tam      | 111-555-8899

users表将允许我通过username查询我的用户,因为这是我们的PRIMARY KEY。但是,如果我们想按电话号码查询数据怎么办?您可能会想在phone上添加一个二级索引,但是phone的基数可能与username一样高。解决此问题的正确方法是创建一个新表,以允许您通过电话查询。注意:此模型假定usernamephone都是唯一的。

CREATE TABLE usersbyphone (
    phone TEXT,
    username TEXT,
    firstname TEXT,
    lastname TEXT,
    PRIMARY KEY (phone));

假设我们的下一个条目是针对用户“霍班·沃什伯恩”和“佐伊·沃什伯恩”的。由于他们已婚,他们将拥有相同的(家庭)电话号码。Cassandra不允许条目共享主键,并将覆盖(以最后一个条目为准)。因此,我们需要更改usersbyphone表上的主键,如下所示:
PRIMARY KEY (phone,username));

这里,phone 是我们的分区键(决定存储此行的分区的键),而 username 是我们的聚簇键(决定磁盘上排序顺序的键)。将这两个键组合在一起作为主键将确保唯一性。这将使我们能够像这样选择我们的 usersbyphone 表:

SELECT username, firstname, lastnamea, phone FROM usersbyphone;

username  | firstname | lastname | phone
------------------------------------------------
hwashburne| Hoban     | Washburne| 111-555-1212
jcobb     | Jayne     | Cobb     | 111-555-3464
mreynolds | Malcolm   | Reynolds | 111-555-1234
sbook     | Derial    | Book     | 111-555-2349
stam      | Simon     | Tam      | 111-555-8899
zwashburne| Zoe       | Washburne| 111-555-1212

重要的是要注意,usersbyphone 表并不会 替换 users 表...它与之相辅相成。你将需要对该数据集进行一些查询,这些查询需要由 users 表提供服务,而另一些则需要由 usersbyphone 表提供服务。缺点是,保持两个表同步是在数据库外部完成的,通常是通过应用程序代码完成的。这是一个例子,展示了在非关系型范式中您的思考必须有所不同。
正如 DataStax MVP John Berryman 所解释的那样(Understanding How CQL3 Maps To Cassandra’s Internal Data Structure),在底层,Casssandra 将以类似于以下结构存储我们的 users 数据:
RowKey:mreynolds
=> (column=, value=, timestamp=1374546754299000)
=> (column=firstname, value=Malcolm, timestamp=1374546754299000)
=> (column=lastname, value=Reynolds, timestamp=1374546754299000)
=> (column=phone, value=111-555-1234, timestamp=1374546754299000)
------------------------------------------------------
RowKey:hwashburne
=> (column=, value=, timestamp=1374546757815000)
=> (column=firstname, value=Hoban, timestamp=1374546757815000)
=> (column=lastname, value=Washburne, timestamp=1374546757815000)
=> (column=phone, value=111-555-1212, timestamp=1374546757815000)
------------------------------------------------------
RowKey:zwashburne
=> (column=, value=, timestamp=1374546761055000)
=> (column=firstname, value=Zoe, timestamp=1374546761055000)
=> (column=lastname, value=Washburne, timestamp=1374546761055000)
=> (column=phone, value=111-555-1212, timestamp=1374546761055000)

“地图中的地图”概念(在eBay文章中提到)在这里确实发挥了作用。我们的“usersbyphone”表格将与我们的“users”表格略有不同:
RowKey:111-555-1234
=> (column=mreynolds, value=, timestamp=1374546754299000)
=> (column=mreynolds:firstname, value=Malcolm, timestamp=1374546754299000)
=> (column=mreynolds:lastname, value=Reynolds, timestamp=1374546754299000)
------------------------------------------------------
RowKey:111-555-1212
=> (column=hwashburne, value=, timestamp=1374546757815000)
=> (column=hwashburne:firstname, value=Hoban, timestamp=1374546757815000)
=> (column=hwashburne:lastname, value=Washburne, timestamp=1374546757815000)
=> (column=zwashburne, value=, timestamp=1374546761055000)
=> (column=zwashburne:firstname, value=Zoe, timestamp=1374546761055000)
=> (column=zwashburne:lastname, value=Washburne, timestamp=1374546761055000)

由于主键结构,可以注意到 Wash(hwashburne)和 Zoe(zwashburne)实际上存储在同一行中。这种结构允许我们快速查询所有共享相同电话号码的用户。
总之:
- 表(列族)应以最有效的方式用于查询数据。 RDBMS 鼓励使用规范化来最有效地存储数据。 - 使用复合键,Cassandra 将类似的数据组合在附近的行中。 - Cassandra 利用磁盘(聚类)排序顺序来优化操作。 - Cassandra 主键始终是唯一的。对具有相同主键的数据进行不同的写入将互相覆盖(最后的写入获胜)。 - CQL(Cassandra 查询语言)是 SQL 的有限子集。虽然 CQL 提供了与 RDBMS 背景相似的语法,但重要的是要记住许多 SQL 关键字和概念在 CQL 中不存在。

很棒的回答,是否愿意再加点内容来赢取悬赏? :) - mtyson
@mtyson 我有计划,但我一直在工作中处理问题...唉。今晚我会添加更多内容。保证。 - Aaron
巴,工作! :) 期待额外的细节。 - mtyson
1
非常启发和有益的,谢谢!这些例子真的很到位。 - mtyson

2
使用关系型数据库,您需要定义列,每行都有这些列(为了简化)。
使用Cassandra(以及HBase),每行都有自己的列,这些列表示为名称/值对。每行也有一个行键。
您可以决定每行将具有相同的列名(称为“静态模型”),这将模仿数据存储在关系数据库中的方式。或者您可以使用动态或“宽行”模型,在该模型中,列名不一定从一行到另一行相同。在后一种模型中,列名可以是时间戳,例如,相应的值可以是推文、交易或天气指标。您可以实现任何其他结构,只要保持每行的行键和列名/值对即可。

谢谢您的回答,如果您愿意添加更多细节,我已经为该问题设置了赏金! - mtyson

2
有很多不同之处。我会尽力帮助一下:
SQL数据库主要依赖于B树数据结构。因此,您可以使用列、大小定义数据架构,RDBMS在原地执行查找/更新您的数据。使用这种类型的数据结构更容易组织自动索引。以下是MySQL中B树的基本描述:http://guptavikas.wordpress.com/2012/12/17/b-tree-index-in-mysql/ Cassandra的记录可能分布在磁盘/固态硬盘上。每个列在相同记录中可能被物理地定位得非常远。这使得Cassandra具有极高的写入速度。当新数据到达时,它只需将其写入文件末尾(简化)。在更新的情况下,旧数据会被标记为墓碑,并保留磁盘空间,直到压缩发生,而这并不经常发生。在大多数情况下,SQL数据库试图通过更有效地利用磁盘空间来在相同的物理位置上重新编写现有记录。
此外,Cassandra专为高可扩展性和多节点集群工作而设计,但这是另一个故事。在这里,您将了解有关Cassandra内部数据结构以及更多信息:http://www.slideshare.net/planetcassandra/a-deep-dive-into-understanding-apache-cassandra 由于结构上的差异,有些情况下SQL/Cassandra可能比另一个更好/更差,例如:
- 向SQL表架构(生产中的大型表格,使用频率很高)添加新列通常是相当困难的操作。对于Cassandra来说,这是非常轻量级的。 - Cassandra对事务的支持有限,集中在单个行周围。而SQL数据库具有非常强大的事务机制。 - 在Cassandra中,通过二级索引访问数据受到限制,没有连接,也没有多表查询。 - 由于Cassandra具有内置的无主复制,因此很容易消除SPOF(单点故障)。在SQL数据库中,您经常会拥有一种主从复制,其中主是SPOF。 SQL世界中的主-主解决方案相当困难,并且有自己的问题。使用有限的Cassandra操作实现可伸缩性高、高度可用的系统要容易得多。SQL数据库在数据处理功能方面更加强大,但也有其成本。
等等。

谢谢,您愿意为奖励再添加更多内容吗? :) - mtyson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接