首页 文章

用户数据的No-SQL(Cassandra)数据建模

提问于
浏览
8

How do you model user data in Cassandra?

  • 用户数据的单个表,按用户ID分区,不同的组件读/写到不同的列?

  • 具有相同键结构的多个表(每个组件一个),偶尔需要在分区键上一起"joined"?

我们拥有与客户相关的各种数据和元数据,我们目前将这些数据和元数据保存在具有相同分区和集群密钥的单独表中 .

这导致来自不同表(例如,分析)的用户的信息位,在其分区键上有效地“加入”两个或更多个Cassandra表 .

从积极的方面来说,插入表格是独立完成的 .

Is there a race condition when concurrently updating data under the same partition key but different columns? Or the deltas are gracefully merged on the SSTables?

Is having multiple tables with the same partition (and clustering) keys usual or an anti-pattern?

为了使这个更具体,让我们说:

CREATE TABLE example (
  pk text PRIMARY KEY
  col_a text
  col_b text
)

假设对于给定的分区键( pk ),最初 col_acol_b 都有一些值(即不为空) . 并且两个并发插入更新它们中的每一个 . 那里有没有竞争条件?尽管写入了不同的专栏,但丢失了两个更新中的一个?

1 回答

  • 1

    摘要

    写冲突是你不应该担心的 . 所有INSERTS / UPDATES / DELETES都是Cassandra的Upserts . Cassandra的所有内容都是以列为基础的 .

    Cassandra使用最后写赢策略来管理冲突 . 正如您在下面的示例中所看到的,无论何时更改值,都会更新与该列关联的时间戳 . 由于您正在运行并发更新,并且一个线程将更新 col_a 而另一个线程将更新 col_b .


    示例

    初始插入

    cqlsh:test_keyspace> insert into race_condition_test (pk, col_a, col_b ) VALUES ( '1', 'deckard', 'Blade Runner');
    cqlsh:test_keyspace> select * from race_condition_test ;
    
     pk | col_a   | col_b
    ----+---------+--------------
      1 | deckard | Blade Runner
    
    (1 rows)
    

    时间戳在初始插入中是相同的

    cqlsh:test_keyspace> select pk, col_a, writetime(col_a), col_b, writetime(col_b) from race_condition_test ;
    
     pk | col_a   | writetime(col_a) | col_b        | writetime(col_b)
    ----+---------+------------------+--------------+------------------
      1 | Deckard | 1526916970412357 | Blade Runner | 1526916970412357
    
    (1 rows)
    

    一旦 col_b 被提升,它的时间戳会发生变化以反映变化 .

    cqlsh:test_keyspace> insert into race_condition_test (pk, col_b ) VALUES ( '1', 'Rick');
    cqlsh:test_keyspace> select pk, col_a, writetime(col_a), col_b, writetime(col_b) from race_condition_test ;
    
     pk | col_a   | writetime(col_a) | col_b | writetime(col_b)
    ----+---------+------------------+-------+------------------
      1 | Deckard | 1526916970412357 |  Rick | 1526917272641682
    
    (1 rows)
    

    col_a 更新后,它的时间戳也更新为新值

    cqlsh:test_keyspace> insert into race_condition_test (pk, col_a) VALUES ( '1', 'bounty hunter');
    cqlsh:test_keyspace> select pk, col_a, writetime(col_a), col_b, writetime(col_b) from race_condition_test ;
    
     pk | col_a         | writetime(col_a) | col_b | writetime(col_b)
    ----+---------------+------------------+-------+------------------
      1 | bounty hunter | 1526917323082217 |  Rick | 1526917272641682
    
    (1 rows)
    

    建议

    我的建议是您使用一个满足查询需求的表 . 如果需要按 pk 查询,则创建一个包含所需列的单个表 . 这样,您将拥有一个可以有效回读的宽行,作为单个查询的一部分 .

    您在选项2中描述的数据模型有点关系,对于Cassandra来说不是最佳选择 . 你不能在cassandra中本地执行连接,你应该避免在客户端执行连接 .

    数据模式规则:

    Rule 1: 在群集中均匀分布数据您需要创建一个分区密钥,以确保数据在群集中均匀分布,并且您没有任何热点 .

    Rule 2: 最小化分区数读取每个分区可能驻留在不同的节点中,因此您应该尝试创建一个场景,为了性能,您的查询理想地只针对一个节点 .

    Rule 3: 围绕您的查询建模

    • 确定要支持的查询

    • 创建一个满足查询的表(意味着每个查询模式应使用一个表) .

    • 如果需要支持更多查询模式,请将数据反规范化为为这些查询提供服务的其他表 . 避免使用辅助索引和物化视图,因为它们目前不稳定,并且当您开始增加群集时,第一个可能会产生重大性能问题 .

    如果你想更多地阅读这个,我建议这个数据库页面:Basic Rules of Cassandra Data Modeling

相关问题