Avn*_*arr 6 database cql cassandra database-normalization scylla
我是Cassandra的新手,正在寻找有关如何建模具有以下通用结构的数据的最佳实践:
数据是基于"用户"的(每个客户),每个数据提供大约500K-2M条目的大数据文件(每天定期更新几次 - 有时完全更新,有时只有增量)
每个数据文件都有一些必需的数据字段(约20个必填项),但可以自行添加其他列(最多约100个).
该附加的数据字段是NOT不一定用于不同用户(字段的名称或类型的那些字段的)相同的
示例(csv格式:)
user_id_1.csv
| column1 (unique key per user_id) | column2 | column3 | ... | column10 | additionalColumn1 | ...additionalColumn_n |
|-----------------------------------|-----------|----------|---------|------------|---------------------|------------------------|
| user_id_1_key_1 | value | value | value | value | ... | value |
| user_id_1_key_2 | .... | .... | .... | .... | ... | ... |
| .... | ... | ... | ... | ... | ... | ... |
| user_id_1_key_2Million | .... | .... | .... | .... | ... | ... |
user_id_XXX.csv (notice that the first 10 columns are identical to the other users but the additional columns are different - both the names and their types)
| column1 (unique key per user_id) | column2 | column3 | ... | column10 | additionalColumn1 (different types than user_id_1 and others) | ...additional_column_x |
|-----------------------------------------------------------|-----------|----------|---------|------------|-----------------------------------------------------------------|-------------------------|
| user_id_XXX_key_1 | value | value | value | value | ... | value |
| user_id_XXX_key_2 | .... | .... | .... | .... | ... | ... |
| .... | ... | ... | ... | ... | ... | ... |
| user_id_XXX_key_500_thousand (less rows than other user) | .... | .... | .... | .... | ... | ... |
Run Code Online (Sandbox Code Playgroud)
我考虑过几个选项:
选项1:
将user_id列连接到大表的所有其他列(包括非强制列).主键变为user_id +"column_1"(column_1对于每个user_id是唯一的)
Keyspace
+--------------------------------------------------------------------------+
| |
| |
| Data_Table |
| + +--------+-------+--------------------------+-----+ |
| | | | | | | |
| | +-------------------------------------------------+ |
| | | | | | | |
| many rows | +-------------------------------------------------+ |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | Many columns | | |
| | | | +------------------------> | | |
| | | | | | | |
| | +-------------------------------------------------+ |
| v +-------------------------------------------------+ |
| |
+--------------------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)我立即注意到的一些事情:
选项2:
按User_id创建Keyspace
每个键空间创建表"数据"
+-----------------------------------------------------------------------------------+
| column_1 | column_2 | ... | column_n | additional_column_1 | additional_column_n |
+-----------------------------------------------------------------------------------+
keyspace_user1 keyspace_user2 keyspace_user_n
+----------------+ +---------------+ +---------------+
| | | | | |
| | | | | |
| +-+-+--+-+ | | +-+--+--+ | | +--+--+---+ |
| | | | | | | | | | | | | many keyspaces | | | | | |
| | | | | | | | | | | | | +-------------> | | | | | |
| | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | |
| +--------+ | | +-------+ | | +---------+ |
+----------------+ +---------------+ +---------------+
Run Code Online (Sandbox Code Playgroud)
笔记:
选项3:
1)创建全局键空间2)为每个user_id创建一个表(必填列以及每个表的附加列)
+---------------------------------------------------------------+
| Keyspace |
| |
| user_1 user_2 user_n |
| +--+---+--+ +--+--+--+ +--+--+--+ |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | |
| | | | | | | | | | | | | |
| +--+---+--+ +--+--+--+ +--+--+--+ |
| |
| |
+---------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)
笔记
选项4 :(这有意义吗?)
创建多个键空间(例如"x"个键空间),每个键空间包含一系列表(每个用户的表)
keyspace_1 keyspace_x
+---------------------------------------------------------------+ +---------------------------------------------------------------+
| | | |
| | | |
| user_1 user_2 user_n/x | | user_n-x user_n-x+1 user_n |
| +--+---+--+ +--+--+--+ +--+--+--+ | | +--+------+ +--+--+--+ +--+--+--+ |
| | | | | | | | | | | | | | "X" keyspaces | | | | | | | | | | | | | |
| | | | | | | | | | | | | | +---------------------> | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | |
| +--+---+--+ +--+--+--+ +--+--+--+ | | +--+---+--+ +--+--+--+ +--+--+--+ |
| | | |
| | | |
+---------------------------------------------------------------+ +---------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)
笔记:
选项5:
将数据拆分为多个表和多个键空间
注意:1.在某些情况下需要从多个表"加入"信息2.似乎更复杂
所有场景的一般说明:
这种类型的集成挑战通常通过关系系统中的EAV(实体属性值)数据模型来解决(如 Ashrafaul 演示的那样)。考虑 EAV 模型时的关键考虑因素是无限数量的列。当然,EAV 数据模型可以在 Cassandra 或 ScyllaDB 等 CQL 系统中进行模仿。EAV 模型非常适合写入,但在读取时会带来挑战。您还没有真正详细说明您的阅读注意事项。您需要恢复所有列还是需要恢复每个用户的特定列?
文件
话虽如此,Cassandra 和 ScyllaDB 固有的一些进一步的考虑因素可能会引导您在问题中描述的某些设计上采用统一的 EAV 模型。Cassandra 和 ScyllaDB 都将键空间和数据库作为磁盘上的文件进行布局。文件数量基本上是键空间数量乘以表数量的乘积。因此,您拥有的键空间、表或两者的组合越多,磁盘上的文件就越多。这可能是文件描述符和其他操作系统文件处理问题的问题。由于您提到的访问长尾,可能会出现每个文件一直打开的情况。这并不是那么理想,尤其是从冷启动启动时。
[为了清楚起见进行编辑]在所有条件相同的情况下,一个键空间/表生成的文件总是比许多键空间/表生成的文件少。这与存储的数据量或压缩策略无关。
宽行
但回到数据模型。Ashraful 的模型有一个主键 (userid) 和另一个集群键 (key->column1)。由于每个用户文件中的“条目”数量 (500K-2M) 并假设每个条目是由平均 60 列组成的行,您基本上要做的是为每个分区键创建 500k-2m * 60 平均列行创建非常大的分区。Cassandra 和 Scylla 通常不喜欢非常大的分区。当然,他们可以处理大分区吗?实际上,大分区确实会影响性能。
更新或版本控制
你提到更新。基本 EAV 模型仅代表最新更新。没有版本控制。您可以做的是将时间添加为聚类键,以确保随着时间的推移保持列的历史值。
读
如果您想要返回所有列,您可以将所有内容序列化为 json 对象并将其放入单个列中。但我想这不是你想要的。在 Cassandra 和 Scylla 等基于键/值的系统的主键(分区键)模型中,您需要知道键的所有组成部分才能取回数据。如果将column1唯一行标识符放入主键中,您将需要提前知道它,同样,如果其他列名称也放入主键中,也需要知道它们。
分区和复合分区键
分区的数量决定了集群的并行性。总分区数或总语料库中分区的基数会影响集群硬件的利用率。更多分区=更好的并行性和更高的资源利用率。
我在这里可能要做的就是修改PRIMARY KEY以包含column1. 然后我将用作column集群键(它不仅规定分区内的唯一性,还规定排序顺序 - 因此在列命名约定中考虑这一点)。
在下表定义中,您需要在子句中提供userid和column1作为等式WHERE。
CREATE TABLE data (
userid bigint,
column1 text,
column text,
value text,
PRIMARY KEY ( (userid, column1), column )
);
Run Code Online (Sandbox Code Playgroud)
我可能还有一个单独的表,columns_per_user记录每个 的所有列userid。就像是
CREATE TABLE columns_per_user (
userid bigint,
max_columns int,
column_names text
PRIMARY KEY ( userid )
);
Run Code Online (Sandbox Code Playgroud)
其中max_columns是该用户的总列数,column_names是实际的列名称。您可能还有一列用于显示每个用户的条目总数,基本上user_entries int就是每个用户 csv 文件中的行数。