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

V-L*_*amp 8 cassandra nosql

你如何在Cassandra中建模用户数据?

  1. 用户数据的单个表,按用户ID分区,不同的组件读/写到不同的列?
  2. 具有相同键结构的多个表(每个组件一个),偶尔需要在分区键上"连接"在一起?

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

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

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

在同一分区键下同时更新数据但不同列时是否存在竞争条件?或者在SSTables上优雅地合并了三角洲?

是否有多个表具有相同的分区(和群集)键通常或反模式?

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

CREATE TABLE example (
  pk text PRIMARY KEY
  col_a text
  col_b text
)
Run Code Online (Sandbox Code Playgroud)

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

小智 1

概括

您无需担心写入冲突。所有插入/更新/删除都是 Cassandra 中的更新插入。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)
Run Code Online (Sandbox Code Playgroud)

时间戳与初始插入中的相同

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)
Run Code Online (Sandbox Code Playgroud)

一旦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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

推荐

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

您在选项 2 中描述的数据模型有点关系型,对于 Cassandra 来说并不是最佳的。您无法在 cassandra 中本地执行联接,并且应避免在客户端执行联接。

数据模式规则:

规则 1:在集群中均匀分布数据 您将需要创建一个分区键,以确保数据在集群中均匀分布并且没有任何热点。

规则 2:最小化读取分区的数量 每个分区可能驻留在不同的节点中,因此您应该尝试创建一种场景,在这种情况下,出于性能考虑,您的查询最好只发送到一个节点。

规则 3:围绕查询建模

  1. 确定支持哪些查询
  2. 创建一个满足您的查询的表(这意味着您应该为每个查询模式使用一个表)。
  3. 如果您需要支持更多查询模式,请将数据非规范化到服务这些查询的其他表中。避免使用二级索引和物化视图,因为它们目前不稳定,并且当您开始增加集群时,第一个索引可能会产生重大性能问题。

如果您想了解更多相关内容,我建议您阅读此 datastax 页面: Cassandra 数据建模的基本规则