【问题标题】:Cassandra - How to denormalize two joined tables?Cassandra - 如何非规范化两个连接表?
【发布时间】:2015-07-29 03:07:03
【问题描述】:

我知道 cassandra 不支持连接,所以要使用 cassandra,我们需要对表进行非规范化。我想知道怎么做? 假设我有两张桌子

<dl>
<dt>Publisher</dt>
<dd>Id : <i>Primary Key</i></dd>
  <dd>Name</dd>
  <dd>TimeStamp</dd>
  <dd>Address</dd>
  <dd>PhoneNo</dd>
  
  <dt>Book</dt>
  <dd>Id : <i>Primary Key</i></dd>
  <dd>Name</dd>
  <dd>ISBN</dd>
  <dd>Year</dd>
  <dd>PublisherId : <i>Foreign Key - Referenes Publisher table's Id</i></dd>
  <dd>Cost</dd>
  </dt>
</dl>

请告诉我如何对这些表进行非规范化以有效地实现以下操作
1. 搜索特定出版商出版的所有图书。
2. 搜索在给定年份出版书籍的所有出版商。
3. 搜索在给定年份没有出版书籍的所有出版商。
4. 搜索所有到现在还没有出书的Publishers。

我看过几篇关于 cassandra 的文章。但无法得出上述操作的非规范化。请帮帮我。

【问题讨论】:

  • 搜索到现在还没有出版书籍的所有出版商(即搜索所有连一本书都没有出版的出版商)

标签: cassandra cql3


【解决方案1】:

设计一个完整的架构对于一个问题来说是一项相当大的任务,但一般而言,非规范化意味着您将在多个表中重复相同的数据,以便您可以读取单行来获取每种类型所需的所有数据查询。

因此,您将为每种类型的查询创建一个表,大致如下:

  1. 创建一个按出版商 ID 分区并以图书 ID 作为集群列的表。
  2. 创建一个按年份分区并以发布者 ID 作为集群列的表。
  3. 创建一个包含所有发布者列表的表。然后,您可以在应用程序中读取此列表,并以编程方式从表 2 中减去所需年份中存在的行。
  4. 我不确定“发布到现在”是什么意思。插入新书时,您可以检查表 3 中是否存在出版商。如果没有,则它是新出版商。

因此,在数据的每一行中,您将重复您希望通过查询返回的所有数据(即示例表中所有列的并集)。当您插入一本新书时,您会将其插入到您的所有表格中。

【讨论】:

  • 搜索到现在还没有出版过书籍的所有出版商(即,搜索所有连一本书都没有出版过的出版商)。
  • 另外,对于 RDBMS 中的两个表,我在 C* 中创建了 3 或 4 个表,正如您所提到的,即使对于单个 witten,我也需要在 2 个表中执行此操作。并且大多数连接操作都是在客户端然后在数据库级别完成的。这是正确的方法吗?我希望它会产生一些性能?还有其他最好的方法吗?
  • 写入多个表确实会导致一些性能损失,但是如果您异步发出写入,那么它们应该非常快,因为它们会散列到不同的节点。此外,由于您的问题听起来像是您的应用程序主要进行读取,因此非规范化将使读取速度更快,因为每次读取都将访问一个分区。
  • 你说When you insert a new book, you would insert it into all of your tables. 所以cassandra适合将数据插入多个表并在不同的表上执行多个SELECT,例如Select id FROM table1 然后SELECT cole FROM table2 where rowId = &lt;id from first table&gt;
  • @Manish 多次插入速度很快,因为您可以并行进行。如果您需要使用第一个选择的结果来执行第二个选择,那么多个选择将有更多的延迟,因为它们需要同步完成。通常你会设计你的架构,这样你就可以做一个选择来获得你需要的信息。
【解决方案2】:

这听起来可能会变得很大,所以我将采用第一个并逐步介绍如何处理它。你没有必须这样做,这只是一种方法。请注意,您可能必须为上述 4 个场景中的每一个创建查询表。此表仅适用于第一种情况。

首先,我将为发布者地址创建一个类型。

CREATE TYPE address (
  street text,
  city text,
  state text,
  postalCode text
);

接下来我将创建一个名为booksByPublisher 的表。我将使用我的address 类型作为publisherAddress。我将使用publisherid 作为分区键构建我的主键,并在bookYearisbn 上进行聚类。

由于您希望能够查询特定出版商的所有书籍,因此将其指定为分区键是有意义的。将结果按年份排序可能会很有帮助,或者至少能够查看特定出版商的特定年份,因此我将 bookYear 作为第一个聚类键。当然,要为出版商中的每本书创建一个唯一的 CQL 行,我将添加 isbn 以保持唯一性。

CREATE TABLE booksByPublisher (
  publisherid UUID,
  publisherName text,
  publisherAddress frozen<address>,
  publisherPhoneNo text,
  bookName text,
  isbn text,
  bookYear bigint,
  bookCost bigint,
  bookAuthor text,
  PRIMARY KEY (publisherid, bookYear, isbn)
);

INSERT INTO booksByPublisher (publisherid, publishername, publisheraddress, publisherphoneno, bookname, isbn, bookyear, bookcost, bookauthor)
VALUES (b7b99ee9-f495-444b-b849-6cea82683d0b,'Crown Publishing',{ street: '1745 Broadway', city: 'New York', state:'NY', postalcode: '10019'},'212-782-9000','Ready Player One','978-0307887443',2005,812,'Ernest Cline');

INSERT INTO booksByPublisher (publisherid, publishername, publisheraddress, publisherphoneno, bookname, isbn, bookyear, bookcost, bookauthor)
VALUES (b7b99ee9-f495-444b-b849-6cea82683d0b,'Crown Publishing',{ street: '1745 Broadway', city: 'New York', state:'NY', postalcode: '10019'},'212-782-9000','Armada','978-0804137256',2015,1560,'Ernest Cline');

INSERT INTO booksByPublisher (publisherid, publishername, publisheraddress, publisherphoneno, bookname, isbn, bookyear, bookcost, bookauthor)
VALUES (uuid(),'The Berkley Publishing Group',{ street: '375 Hudson Street', city: 'New York', state:'NY', postalcode: '10014'},'212-333-2354','Rainbox Six','978-0425170342',1999,867,'Tom Clancy');

现在我可以像这样查询 Crown Publishing (publisherid=b7b99ee9-f495-444b-b849-6cea82683d0b) 出版的所有书籍(在我的 3 行中):

aploetz@cqlsh:stackoverflow2> SELECT * FROM booksbypublisher 
    WHERE publisherid=b7b99ee9-f495-444b-b849-6cea82683d0b;

 publisherid                          | bookyear | isbn           | bookauthor   | bookcost | bookname         | publisheraddress                                                              | publishername    | publisherphoneno
--------------------------------------+----------+----------------+--------------+----------+------------------+-------------------------------------------------------------------------------+------------------+------------------
 b7b99ee9-f495-444b-b849-6cea82683d0b |     2005 | 978-0307887443 | Ernest Cline |      812 | Ready Player One | {street: '1745 Broadway', city: 'New York', state: 'NY', postalcode: '10019'} | Crown Publishing |     212-782-9000
 b7b99ee9-f495-444b-b849-6cea82683d0b |     2015 | 978-0804137256 | Ernest Cline |     1560 |           Armada | {street: '1745 Broadway', city: 'New York', state: 'NY', postalcode: '10019'} | Crown Publishing |     212-782-9000

(2 rows)

如果我愿意,我也可以查询皇冠出版社在 2015 年的所有书籍:

aploetz@cqlsh:stackoverflow2> SELECT * FROM booksbypublisher
    WHERE publisherid=b7b99ee9-f495-444b-b849-6cea82683d0b AND bookyear=2015;

 publisherid                          | bookyear | isbn           | bookauthor   | bookcost | bookname | publisheraddress                                                              | publishername    | publisherphoneno
--------------------------------------+----------+----------------+--------------+----------+----------+-------------------------------------------------------------------------------+------------------+------------------
 b7b99ee9-f495-444b-b849-6cea82683d0b |     2015 | 978-0804137256 | Ernest Cline |     1560 |   Armada | {street: '1745 Broadway', city: 'New York', state: 'NY', postalcode: '10019'} | Crown Publishing |     212-782-9000

(1 rows)

但我不能仅通过bookyear查询:

aploetz@cqlsh:stackoverflow2> SELECT * FROM booksbypublisher WHERE bookyear=2015;
InvalidRequest: code=2200 [Invalid query] message="Cannot execute this query as it might 
involve data filtering and thus may have unpredictable performance. If you want to execute
this query despite the performance unpredictability, use ALLOW FILTERING"

并且不要听错误信息并添加ALLOW FILTERING。对于 3 行(甚至 300 行)的表,这可能工作得很好。但它不适用于具有 300 万行的表(您会超时)。当您通过完整的分区键查询时,Cassandra 效果最佳。因为publisherid 是我们的分区键,所以这个查询会执行得很好。但是如果你需要通过bookYear查询,那么你应该创建一个使用bookYear作为其分区键的表。

【讨论】:

    猜你喜欢
    • 2016-05-13
    • 2013-01-08
    • 1970-01-01
    • 2013-08-13
    • 1970-01-01
    • 2017-04-01
    • 2021-10-20
    • 2018-07-31
    • 1970-01-01
    相关资源
    最近更新 更多