Cassandra (CQL) schema/tables 在我的场景中看起来与 RDBMS 相同
Cassandra (CQL) schema/tables look the same as RDBMS for my scenario
我看过 Twissandra 示例。几天前我问了一个类似的问题,并收到了我在这里实施的一些提示。但是,通过查看表(列族),我几乎看不出这与关系数据库有任何区别。
我的场景:
一个简单的地址簿,用户可以在其中创建自己的联系人并将它们分组(一个联系人可以放在多个组中,一个组可以包含多个联系人)。例如,一个联系人可能有多个地址。
我想检索所有居住在地址 x 中并位于组 y 中的联系人。因此,我做了以下操作:
CREATE TABLE if not exists User (user_id uuid, contact_id uuid, type varchar, email varchar, PRIMARY KEY(id));
CREATE TABLE if not exists Contact (contact_id uuid, firstname varchar,lastname varchar, photo blob, imagelength int, note varchar, PRIMARY KEY (id));
CREATE TABLE if not exists Address (address_id uuid, contact_id uuid, street varchar, number int, zipcode varchar, country varchar, PRIMARY KEY(address_id));
CREATE TABLE if not exists Group (group_id uuid, user_id, groupname varchar, PRIMARY KEY(group_id));
CREATE TABLE if not exists Group_Contact (group_id uuid, contact_id, PRIMARY KEY(id, contact_id));
然而,基于此,这实际上与关系数据库完全相同,除了我相信 Cassandra 以不同于 RDBMS 的方式将这些数据存储在磁盘上。我看不出如何在 Cassandra 中做得更好,也不知道我是否以正确的方式建模。它感觉就像一个普通的关系数据库。
我觉得我做错了什么,因为我 使用应用程序级连接来获取联系人的地址。我真的不知道如何将其反规范化以允许多个地址(甚至可能是电话、电子邮件)。
如有任何改进此场景的建议,我们将不胜感激!
很难从关系数据库建模切换到 Cassandra 建模,因为它们看起来非常相似:查询语言看起来几乎相同。但是 Cassandra 的第一条规则是为您的查询建模,而在关系数据库中我们为数据建模。这意味着:
- 考虑一下您在
上查询最多的内容
- 了解分区键和集群键
- 不要害怕数据重复
在 Cassandra 中有一个很好的数据建模示例:https://www.datastax.com/documentation/cql/3.1/cql/ddl/ddl_music_service_c.html
正如 jny 所指出的,数据复制、非规范化和基于查询的建模是构建良好的 Cassandra 数据模型的关键。如果我想使用上面的 tables,并构建一个 table 来支持基于国家/地区的 address/contact 查询,我可以这样做:
首先,我将为联系人地址创建一个 user defined type。
aploetz@cqlsh:Whosebug> CREATE TYPE contactAddress (
... street varchar,
... city varchar,
... zip_code varchar,
... country varchar);
接下来,我将创建一个名为 UserContactsByCountry
的 table 来存储用户联系信息以及任何用户联系地址:
aploetz@cqlsh:Whosebug> CREATE TABLE UserContactsByCountry (
... country varchar,
... user_id uuid,
... type varchar,
... email varchar,
... firstname varchar,
... lastname varchar,
... photo blob,
... imagelength int,
... note varchar,
... addresses map<text, frozen <contactAddress>>,
... PRIMARY KEY ((country),user_id));
这里有几点需要注意:
- 我正在使用
country
作为查询的分区键,并添加 user_id
作为唯一性的聚类键。
- 从技术上讲,
country
在每一行中存储了多个。一次作为分区键,一次作为每个地址。请注意,country
分区键是允许我们 运行 查询的分区键。
- 我假设用户联系人可以有多个地址,所以我将它们存储在一个类型为文本 (varchar)、contactAddress(上面创建的类型)的映射中。
接下来,我将插入三个用户联系人,每个都有两个地址,两个来自美国,一个来自英国。
aploetz@cqlsh:Whosebug> INSERT INTO usercontactsbycountry (country, user_id, type, email, firstname, lastname, note, addresses)
VALUES ('USA',uuid(),'Tech','brycelynch@network23.com','Bryce','Lynch','Head of R&D at Network 23',{'work':{street:'101 Big Network Drive',city:'New York',zip_code:'10023',country:'USA'},'home':{street:'8192 N. 42nd St.',city:'New York',zip_code:'10025',country:'USA'}});
aploetz@cqlsh:Whosebug> INSERT INTO usercontactsbycountry (country, user_id, type, email, firstname, lastname, note, addresses)
VALUES ('USA',uuid(),'Reporter','edisoncarter@network23.com','Edison','Carter','Reporter at Network 23',{'work':{street:'101 Big Network Drive',city:'New York',zip_code:'10023',country:'USA'},'home':{street:'76534 N. 62nd St.',city:'New York',zip_code:'10024',country:'USA'}});
aploetz@cqlsh:Whosebug> INSERT INTO usercontactsbycountry (country, user_id, type, email, firstname, lastname, note, addresses)
VALUES ('GBR',uuid(),'Reporter','theorajones@network23.com','Theora','Jones','Controller at Network 23',{'work':{street:'101 Big Network Drive',city:'New York',zip_code:'10023',country:'USA'},'home':{street:'821 Wembley St.',city:'London',zip_code:'W11 2BQ',country:'GBR'}});
现在我可以查询 table 美国的所有用户联系人:
aploetz@cqlsh:Whosebug> SELECT * FROM usercontactsbycountry WHERE country ='USA';
country | user_id | addresses | email | firstname | imagelength | lastname | note | photo | type
---------+--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+-----------+-------------+----------+---------------------------+-------+----------
USA | 2dee94e2-4887-4988-8cf5-9aee5fd0ea1e | {'home': {street: '8192 N. 42nd St.', city: 'New York', zip_code: '10025', country: 'USA'}, 'work': {street: '101 Big Network Drive', city: 'New York', zip_code: '10023', country: 'USA'}} | brycelynch@network23.com | Bryce | null | Lynch | Head of R&D at Network 23 | null | Tech
USA | b92612dd-dbaa-42f2-8ff2-d36b6c601aeb | {'home': {street: '76534 N. 62nd St.', city: 'New York', zip_code: '10024', country: 'USA'}, 'work': {street: '101 Big Network Drive', city: 'New York', zip_code: '10023', country: 'USA'}} | edisoncarter@network23.com | Edison | null | Carter | Reporter at Network 23 | null | Reporter
(2 rows)
可能还有其他方法可以对其进行建模,但我希望使用这种方法来帮助您理解一些可用的技术。
我看过 Twissandra 示例。几天前我问了一个类似的问题,并收到了我在这里实施的一些提示。但是,通过查看表(列族),我几乎看不出这与关系数据库有任何区别。
我的场景: 一个简单的地址簿,用户可以在其中创建自己的联系人并将它们分组(一个联系人可以放在多个组中,一个组可以包含多个联系人)。例如,一个联系人可能有多个地址。
我想检索所有居住在地址 x 中并位于组 y 中的联系人。因此,我做了以下操作:
CREATE TABLE if not exists User (user_id uuid, contact_id uuid, type varchar, email varchar, PRIMARY KEY(id));
CREATE TABLE if not exists Contact (contact_id uuid, firstname varchar,lastname varchar, photo blob, imagelength int, note varchar, PRIMARY KEY (id));
CREATE TABLE if not exists Address (address_id uuid, contact_id uuid, street varchar, number int, zipcode varchar, country varchar, PRIMARY KEY(address_id));
CREATE TABLE if not exists Group (group_id uuid, user_id, groupname varchar, PRIMARY KEY(group_id));
CREATE TABLE if not exists Group_Contact (group_id uuid, contact_id, PRIMARY KEY(id, contact_id));
然而,基于此,这实际上与关系数据库完全相同,除了我相信 Cassandra 以不同于 RDBMS 的方式将这些数据存储在磁盘上。我看不出如何在 Cassandra 中做得更好,也不知道我是否以正确的方式建模。它感觉就像一个普通的关系数据库。 我觉得我做错了什么,因为我 使用应用程序级连接来获取联系人的地址。我真的不知道如何将其反规范化以允许多个地址(甚至可能是电话、电子邮件)。
如有任何改进此场景的建议,我们将不胜感激!
很难从关系数据库建模切换到 Cassandra 建模,因为它们看起来非常相似:查询语言看起来几乎相同。但是 Cassandra 的第一条规则是为您的查询建模,而在关系数据库中我们为数据建模。这意味着:
- 考虑一下您在 上查询最多的内容
- 了解分区键和集群键
- 不要害怕数据重复
在 Cassandra 中有一个很好的数据建模示例:https://www.datastax.com/documentation/cql/3.1/cql/ddl/ddl_music_service_c.html
正如 jny 所指出的,数据复制、非规范化和基于查询的建模是构建良好的 Cassandra 数据模型的关键。如果我想使用上面的 tables,并构建一个 table 来支持基于国家/地区的 address/contact 查询,我可以这样做:
首先,我将为联系人地址创建一个 user defined type。
aploetz@cqlsh:Whosebug> CREATE TYPE contactAddress (
... street varchar,
... city varchar,
... zip_code varchar,
... country varchar);
接下来,我将创建一个名为 UserContactsByCountry
的 table 来存储用户联系信息以及任何用户联系地址:
aploetz@cqlsh:Whosebug> CREATE TABLE UserContactsByCountry (
... country varchar,
... user_id uuid,
... type varchar,
... email varchar,
... firstname varchar,
... lastname varchar,
... photo blob,
... imagelength int,
... note varchar,
... addresses map<text, frozen <contactAddress>>,
... PRIMARY KEY ((country),user_id));
这里有几点需要注意:
- 我正在使用
country
作为查询的分区键,并添加user_id
作为唯一性的聚类键。 - 从技术上讲,
country
在每一行中存储了多个。一次作为分区键,一次作为每个地址。请注意,country
分区键是允许我们 运行 查询的分区键。 - 我假设用户联系人可以有多个地址,所以我将它们存储在一个类型为文本 (varchar)、contactAddress(上面创建的类型)的映射中。
接下来,我将插入三个用户联系人,每个都有两个地址,两个来自美国,一个来自英国。
aploetz@cqlsh:Whosebug> INSERT INTO usercontactsbycountry (country, user_id, type, email, firstname, lastname, note, addresses)
VALUES ('USA',uuid(),'Tech','brycelynch@network23.com','Bryce','Lynch','Head of R&D at Network 23',{'work':{street:'101 Big Network Drive',city:'New York',zip_code:'10023',country:'USA'},'home':{street:'8192 N. 42nd St.',city:'New York',zip_code:'10025',country:'USA'}});
aploetz@cqlsh:Whosebug> INSERT INTO usercontactsbycountry (country, user_id, type, email, firstname, lastname, note, addresses)
VALUES ('USA',uuid(),'Reporter','edisoncarter@network23.com','Edison','Carter','Reporter at Network 23',{'work':{street:'101 Big Network Drive',city:'New York',zip_code:'10023',country:'USA'},'home':{street:'76534 N. 62nd St.',city:'New York',zip_code:'10024',country:'USA'}});
aploetz@cqlsh:Whosebug> INSERT INTO usercontactsbycountry (country, user_id, type, email, firstname, lastname, note, addresses)
VALUES ('GBR',uuid(),'Reporter','theorajones@network23.com','Theora','Jones','Controller at Network 23',{'work':{street:'101 Big Network Drive',city:'New York',zip_code:'10023',country:'USA'},'home':{street:'821 Wembley St.',city:'London',zip_code:'W11 2BQ',country:'GBR'}});
现在我可以查询 table 美国的所有用户联系人:
aploetz@cqlsh:Whosebug> SELECT * FROM usercontactsbycountry WHERE country ='USA';
country | user_id | addresses | email | firstname | imagelength | lastname | note | photo | type
---------+--------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+-----------+-------------+----------+---------------------------+-------+----------
USA | 2dee94e2-4887-4988-8cf5-9aee5fd0ea1e | {'home': {street: '8192 N. 42nd St.', city: 'New York', zip_code: '10025', country: 'USA'}, 'work': {street: '101 Big Network Drive', city: 'New York', zip_code: '10023', country: 'USA'}} | brycelynch@network23.com | Bryce | null | Lynch | Head of R&D at Network 23 | null | Tech
USA | b92612dd-dbaa-42f2-8ff2-d36b6c601aeb | {'home': {street: '76534 N. 62nd St.', city: 'New York', zip_code: '10024', country: 'USA'}, 'work': {street: '101 Big Network Drive', city: 'New York', zip_code: '10023', country: 'USA'}} | edisoncarter@network23.com | Edison | null | Carter | Reporter at Network 23 | null | Reporter
(2 rows)
可能还有其他方法可以对其进行建模,但我希望使用这种方法来帮助您理解一些可用的技术。