熟悉SQL的人迁移到Cassandra/NoSQL的概述
在处理关系数据库多年之后,我很乐意接受规范化的洗脑,转而考虑表、列和行。卡桑德拉仍在我的脑海中闪过,完全被它包围着 我更了解持久化映射,您只能查询这些映射的唯一键,但我的理解是不完整的 是的,我是RTFM。不过,有人能给我一个很好的,简洁的描述卡桑德拉如何结构数据与SQL数据库?我是说,1000英尺的视野,它是如何工作的 例如,在易趣科技博客上,它说: 不要考虑关系表 相反,请考虑嵌套、排序的地图数据结构。 资料来源: 我几乎完全明白了。。。但不完全如此熟悉SQL的人迁移到Cassandra/NoSQL的概述,cassandra,nosql,Cassandra,Nosql,在处理关系数据库多年之后,我很乐意接受规范化的洗脑,转而考虑表、列和行。卡桑德拉仍在我的脑海中闪过,完全被它包围着 我更了解持久化映射,您只能查询这些映射的唯一键,但我的理解是不完整的 是的,我是RTFM。不过,有人能给我一个很好的,简洁的描述卡桑德拉如何结构数据与SQL数据库?我是说,1000英尺的视野,它是如何工作的 例如,在易趣科技博客上,它说: 不要考虑关系表 相反,请考虑嵌套、排序的地图数据结构。 资料来源: 我几乎完全明白了。。。但不完全如此 这些都是很好的答案。添加了一个悬赏,以
这些都是很好的答案。添加了一个悬赏,以查看它是否会激励任何人做出更权威的响应。使用关系数据库定义列,并且每行都有这些列(过于简化) 对于Cassandra(以及HBase),每一行都有自己的列,这些列表示为名称/值对。每行也有一个行键
您可以决定每一行将具有相同的列名(称为“静态模型”),这将模拟数据在关系数据库中的存储方式。或者您可以有一个动态的或“宽行”模型,其中列名在每行之间不一定相同。在后一种模型中,列名可以是时间戳,例如,对应的值是tweet、事务或天气指标。您可以随意实现所需的任何其他结构,只要每行保留一个行键和列名/值对。注意:由于最初的问题是关于Cassandra的,因此这将是本答案的重点。虽然Cassandra和其他非关系(NoSQL)数据存储通常共享类似的概念,但不能假设这里详细介绍的思想将适用于其他非关系数据存储 实现这一点的最佳方法是记住,构建数据模型以适应查询是标准实践(对于Cassandra而言)。主要区别在于,RDBMS表是以高效的数据存储为主要焦点构建的。在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
PRIMARY KEY (phone,username));
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
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)
users
表允许我通过username
查询我的用户,因为这是我们的主键。但如果我们想通过电话号码查询数据呢?您可能想在手机
上添加二级索引,但手机
的基数可能与用户名
一样高。解决这个问题的正确方法是创建一个新表,允许您通过电话进行查询。注意:此模型假设用户名
和电话
都是唯一的
CREATE TABLE usersbyphone (
phone TEXT,
username TEXT,
firstname TEXT,
lastname TEXT,
PRIMARY KEY (phone));
假设我们的下一个条目是针对用户“Hoban Washburne”和“Zoe Washburne”的。当他们结婚后,他们将拥有相同的(家庭)电话号码。Cassandra将不允许条目共享主键,并将覆盖(最后一个条目获胜)。因此,我们需要更改usersbyphone
表上的主键,如下所示:
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
PRIMARY KEY (phone,username));
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
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)
这里,phone
是我们的分区键(决定存储此行的分区的键),而username
是我们的集群键(决定磁盘排序顺序的键)。在复合主键中同时使用这两个将确保唯一性。这将允许我们像这样选择usersbyphone
表:
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
PRIMARY KEY (phone,username));
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
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)
需要注意的是,usersbyphone
表并不取代users
表……它与之协同工作。您将有一些关于该数据集的查询需要由users
表提供,还有一些查询需要由usersbyphone
表提供。缺点是保持两个表同步是在数据库之外完成的,通常是由应用程序代码完成的。这是一个例子,说明了在非关系范式中,您的思维必须有所不同
正如DataStax MVP John Berryman()所解释的那样,在引擎盖下,Casssandra将把我们的用户
数据存储在如下结构中:
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
PRIMARY KEY (phone,username));
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
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)
地图的概念(在易趣的文章中提到)肯定会在这里发挥作用。我们的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背景、具有熟悉语法的人,但重要的是要记住,CQL中没有许多SQL关键字和概念
- sqldbs主要依赖于B-tree数据结构。因此,您定义了一个数据模式,每个模式包含列、大小和RDBMS
- 有很多不同之处。我会尽力帮忙的: