【问题标题】:Cassandra, schema and process design for concurrent writesCassandra,并发写入的模式和流程设计
【发布时间】:2014-12-01 01:20:34
【问题描述】:

这是一个冗长的问题。它是关于 Cassandra 模式设计的。我来这里是为了从您尊敬的专家那里获得关于我正在研究的用例的意见。欢迎所有意见、建议和批评。这是我的问题。

我们希望收集用户对我们即将发表的一些论文的评论。对于每篇论文,我们寻求 3 条评论。但是我们向 3*2= 6 个用户发送评论邀请。所有 6 位用户都可以向我们的系统提交他们的评论,但仅限前 3 位;前 3 名审稿人将获得奖励。

在我们的 Cassandra DB 中,有三个表:USER、PAPER 和 REVIEW。 USER 和 PAPER 表很简单:每个用户对应 USER 表中具有唯一 USER_ID 的行;同样,每篇论文在 PAPER 表中都有一个唯一的 PAPER_ID。

REVIEW 表如下所示

CREATE TABLE REVIEW(
    PAPER_ID uuid,
    USER_ID uuid,
    REVIEW_CONTENT text,
    PRIMARY KEY(PAPER_ID, USER_ID)
    );

我们使用 PAPER_ID 作为 REVIEW 表的分区键,以便给定论文的所有评论都存储在单个 Cassandra 行中。对于我们拥有的每篇论文,我们选择 6 个用户,将 6 个条目插入到 REVIEW 表中,并向这些用户发送 6 个邀请。因此,对于论文“P1”,REVIEW 表中有 6 个条目如下所示

----------------------------------------------------
PAPER_ID      |  USER_ID        |  REVIEW_CONTENT  |
----------------------------------------------------
 P1           |  U1             |      null        |
----------------------------------------------------
 P1           |  U2             |      null        |
----------------------------------------------------
 P1           |  U3             |      null        |
----------------------------------------------------
 P1           |  U4             |      null        |
----------------------------------------------------
 P1           |  U5             |      null        |
----------------------------------------------------
 P1           |  U6             | This paper   ... |
 ---------------------------------------------------
 ...          |  ...            | ...              |

用户通过网络浏览器使用 http 提交评论。在后端,我们使用以下流程来处理提交的评论(以论文“P1”为例):

  1. 使用分区键“P1”从 REVIEW 表中取出所有 6 个条目。
  2. 找出这6个条目中有多少在REVIEW_CONTENT列有非空值(非空值表示对应的用户已经提交了他的评论。例如,在上表中,用户“U6”已提交他的评论,而其他 5 人还没有)。
  3. 如果这个数字 >=3,我们已经获得了足够多的评论,请返回给当前评论者并发送一条消息,例如“谢谢,我们已经获得了足够多的评论。”
  4. 如果此数字
  5. 如果此数字 =2,这是最复杂的情​​况,因为当前提交是我们将接受的最后一个。在这种情况下,我们首先将当前评论保存到 REVIEW 表中,然后我们找到所有三个提交评论的用户(包括当前用户)的 id,将他们的 id 记录到事务表中,以便稍后向他们支付奖励。

但是这个过程不起作用。问题是它不能正确处理并发提交。考虑以下情况:两个用户已经提交了他们的评论,同时另外 3 个用户正在通过上面显示的三个并发过程提交他们的评论。在第 5 步,三个人中的每个人都会认为他是第三个也是最后一个提交者,并将新记录插入到事务表中。这会导致重复计算:单个用户可能会因为他提交的相同评论而获得多次奖励。

这个过程的另一个问题是它可能永远不会到达第5步。假设REVIEW表中没有提交,并且有4个用户同时提交了他们的评论。他们都在第 4 步保存了他们的评论。在此之后,以后的提交者将始终被拒绝,因为已经有 4 个接受的评论。但是由于我们永远不会到达第 5 步,因此不会将任何 id 记录到交易表中,用户将永远不会获得任何奖励。

所以我的问题来了:我应该如何使用 Cassandra 作为后端数据库来处理我的用例? Cassandra COUNTER 会帮忙吗?如果是这样,怎么做?我还没有考虑过如何使用 COUNTER,但是这个博客 (http://aphyr.com/posts/294-call-me-maybe-cassandra) 警告说 Cassandra COUNTER 不安全(引用“因此,在网络分区期间,Cassandra 计数器将在很大范围内过度计数或计数不足。” ) Cassandra 的比较和设置 (CAS) 功能有帮助吗?如果是这样,怎么做?保存博客再次警告说“Cassandra 轻量级事务甚至还没有接近正确。”

【问题讨论】:

    标签: concurrency cassandra schema


    【解决方案1】:

    与其在您的评论表中创建空条目,我会考虑将其留空并仅在提交评论时填写。要处理并发,添加一个 timeuuid 字段作为排序键:

    CREATE TABLE review(
      paper_id uuid,
      submission_time timeuuid,
      user_id uuid,
      content text,
      PRIMARY KEY (paper_id, submission_time)
    );
    

    当用户提交时,将条目添加到表中。然后在写入成功后,查询表(仅在 paper_id 上)并找出用户的 id 是否是前三个之一。相应地响应用户。由于您致力于一小部分评论者,因此获取所有评论的额外开销应该是最小的(尤其是因为您不需要在查询中包含内容列)。

    如果您需要跟踪谁在审阅论文,请将一组用户 ID 添加到论文表并在其中写入六个用户 ID。

    【讨论】:

    • 我喜欢你的解决方案。似乎 Cassandra 的一般设计原则是始终附加到表,尽量不要更新它。一个快速的问题:如果一位审稿人 X 成功提交了他的审稿,但当下一个审稿 Y 获取所有审稿时,由于网络延迟,X 新提交的审稿不包括在内,会发生什么情况?我想这可能会发生。 ConsistencyLevel.ALL 会有帮助吗?
    • 由于您的用例需要这种级别的一致性,我建议您在 QUORUM 进行写作和阅读。假设您有 3 个或更多副本(标准),这将为您提供完美的一致性,而不会具有 ALL 的脆弱性。生成时间戳的位置也会产生影响。对于这种情况,最好使用 CQL 的 now() 函数。考虑到这两个因素(和 NTP),我认为您应该非常接近保证的一致性水平。
    • 如果“非常接近”不适合你,请阅读Cassandra's new light weight transactions
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 2016-07-19
    相关资源
    最近更新 更多