最佳实践模型数据的数据库

问题描述:

我是新来的卡桑德拉和正在寻找如何有这种一般结构如下数据模型中的最佳实践:最佳实践模型数据的数据库

的数据是基于“用户”(每用户) ,每个提供大约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) | ....  | .... | .... | ....  |               ... | ...     | 

我考虑过的几个选项:

选项1:

  1. 创建一个“全球性的”密钥空间
  2. 创建一个大表“数据”包含一切
  3. 连接一个user_id列于所有其他列的大表(包括非强制性列)。主键变成USER_ID + “COLUMN_1”(COLUMN_1为每USER_ID唯一的)

            Keyspace 
    +--------------------------------------------------------------------------+ 
    |                   | 
    |                   | 
    |          Data_Table       | 
    |    + +--------+-------+--------------------------+-----+ | 
    |    | |  |  |       |  | | 
    |    | +-------------------------------------------------+ | 
    |    | |  |  |       |  | | 
    | many rows | +-------------------------------------------------+ | 
    |    | |  |  |       |  | | 
    |    | |  |  |       |  | | 
    |    | |  |  |       |  | | 
    |    | |  |  |  Many columns   |  | | 
    |    | |  |  +------------------------> |  | | 
    |    | |  |  |       |  | | 
    |    | +-------------------------------------------------+ | 
    |    v +-------------------------------------------------+ | 
    |                   | 
    +--------------------------------------------------------------------------+ 
    

,我注意到马上有几件事情:

  1. user_ID的自身重复多次的条目每用户
  2. 对于其他列(空值为空值 值),行非常稀疏,因为用户不一定共享它们
  3. 用户数rel atively小,所以许多附加列 的不是很大(10K列最大值)
  4. 我可以每用户的附加列中的数据压缩到名为“元数据”一列,占所有用户

分享

选项2:

每密钥空间

每USER_ID

CREATE TABLE “数据” 创建密钥空间个

+-----------------------------------------------------------------------------------+ 
| column_1 | column_2 | ... | column_n | additional_column_1 | additional_column_n | 
+-----------------------------------------------------------------------------------+ 

keyspace_user1   keyspace_user2      keyspace_user_n 
+----------------+ +---------------+     +---------------+ 
|    | |    |     |    | 
|    | |    |     |    | 
| +-+-+--+-+ | | +-+--+--+ |     | +--+--+---+ | 
| | | | | | | | | | | | | many keyspaces | | | | | | 
| | | | | | | | | | | | | +-------------> | | | | | | 
| | | | | | | | | | | | |     | | | | | | 
| | | | | | | | | | | | |     | | | | | | 
| +--------+ | | +-------+ |     | +---------+ | 
+----------------+ +---------------+     +---------------+ 

笔记:

  1. 许多keyspaces(每用户密钥空间)
  2. 避免了增加每每一行 “USER_ID” 值(我可以使用密钥空间名称作为用户ID)
  3. 非常每密钥空间几个表(在这个例子中仅每密钥空间1个表)

选项3:

1)创建一个全局密钥空间 2)创建每USER_ID的表(强制性列以及每其表及其附加列)

+---------------------------------------------------------------+ 
|       Keyspace       | 
|                | 
|  user_1  user_2       user_n  | 
| +--+---+--+ +--+--+--+      +--+--+--+ | 
| | | | | | | | |      | | | | | 
| | | | | | | | |      | | | | | 
| | | | | | | | |      | | | | | 
| | | | | | | | |      | | | | | 
| | | | | | | | |      | | | | | 
| +--+---+--+ +--+--+--+      +--+--+--+ | 
|                | 
|                | 
+---------------------------------------------------------------+ 

  1. 全局按键空间
  2. 每个user_id的表(“许多”表)
  3. 避免重复用户标识每行

方案四:(这是否有道理?)

创建多个keyspaces(keyspaces的例如 “×” 号)各保持一个范围的表(每个用户台)

     keyspace_1                    keyspace_x 
+---------------------------------------------------------------+       +---------------------------------------------------------------+ 
|                |       |                | 
|                |       |                | 
|  user_1  user_2      user_n/x |       |  user_n-x  user_n-x+1      user_n  | 
| +--+---+--+ +--+--+--+      +--+--+--+ |       | +--+------+ +--+--+--+      +--+--+--+ | 
| | | | | | | | |      | | | | |  "X" keyspaces | | | | | | | | |      | | | | | 
| | | | | | | | |      | | | | | +---------------------> | | | | | | | | |      | | | | | 
| | | | | | | | |      | | | | |       | | | | | | | | |      | | | | | 
| | | | | | | | |      | | | | |       | | | | | | | | |      | | | | | 
| | | | | | | | |      | | | | |       | | | | | | | | |      | | | | | 
| +--+---+--+ +--+--+--+      +--+--+--+ |       | +--+---+--+ +--+--+--+      +--+--+--+ | 
|                |       |                | 
|                |       |                | 
+---------------------------------------------------------------+       +---------------------------------------------------------------+ 

注:

  1. 多keyspaces
  2. 每个用户多个表
  3. 需要“查找”来确定哪个键空间包含所需的表

选项5:

分割数据,以多个表和多个keyspaces

注: 1.需要在某些情况下 2.好像是 “加入” 从多个表中的信息更复杂


一般n所有场景OTES:

  1. 有一个幅度较小的写入速度比读取
  2. 数以百万计的每天读取每USER_ID
  3. 流量有波动 - 一些user_ids有大量的流量和一些user_ids有少得多交通。需要调整按照该指标
  4. 一些user_ids被更新(写)的频率高于其他
  5. 我们拥有跨地域的多个数据中心,并应同步
  6. 没有每个主键的长尾巴(有些键访问许多次,而其他按键都很少被访问)
+0

我是cassandra自己的新手,但选项1对我最有意义。卡桑德拉是为稀疏列建造的。另外,看看复合主键 - PRIMARY KEY(key_part_one,key_part_two)。看看这里:https://*.com/a/24953331/1277048。这为连接方法的检索提供了一些灵活性:您可以在一个请求中读取所有包含key_part_one的行,或者只读取与(key_part_one,key_part_two)匹配的行。 – FuzzyAmi

+1

列出所有您选择的查询,然后根据您的查询设计您的数据模型。 –

这种类型的集成挑战的通常是通过在关系系统的EAV(实体属性值)的数据模型来解决(就像Ashrafaul演示的那样)。考虑EAV模型时的关键考虑因素是无限数量的列。 EAV数据模型当然可以在像Cassandra或ScyllaDB这样的CQL系统中模拟。 EAV模型很适合写作,但在阅读时会带来挑战。你没有真正详细的阅读考虑。你是否需要返回所有列或者是否需要为每个用户返回特定列?

文件

话虽如此,有先天的卡桑德拉和ScyllaDB一些进一步的考虑,可能指向你走向一个统一的EAV模型对一些你在你的问题描述设计的。 Cassandra和ScyllaDB都将密钥和数据库作为文件放在磁盘上。文件的数量基本上是密钥空间数乘以表的数量的乘积。所以更多的密钥空间,表格或者两者的组合,你在磁盘上拥有的文件就越多。这可能是文件描述符和其他os文件杂耍问题的问题。由于您提到的访问过程很长,可能会出现每个文件始终打开的情况。这并不理想,特别是从冷启动开始时。

所有情况相同,一个按键空间/表总是会产生比许多按键空间/表少的文件。这与数据存储量或压缩策略无关。

宽行

但要回的数据模型。 Ashraful的模型有一个主键(userid)和另一个集群键(key-> column1)。由于每个用户文件(500K-2M)中的“条目”数量很多,并且假设每个条目都是由60列组成的行,所以您基本上正在为每个分区键创建500k-2m * 60个平均列行创建非常大的分区。 Cassandra和Scylla通常不喜欢非常大的分区。当然,他们可以处理大型分区吗?实际上,大分区会影响性能,是的。

更新或版本

你提到的更新。基本EAV模型将只代表最近的更新。没有版本控制。您可以做的是将时间添加为集群密钥,以确保您保持列的历史价值。

如果希望所有列备份你可以只序列一切都变成JSON对象,并把它放在一列读取

。但我想象那不是你想要的。在基于键/值的系统(如Cassandra和Scylla)的主键(分区键)模型中,您需要知道键的所有组件,才能恢复数据。如果将column1(唯一行标识符)放入主键中,则需要事先了解它,同样也要将其他列名称也放入主键中。

分区和复合分区键

分区数决定群集的并行性。总分区数或总语料库中分区的基数会影响集群硬件的利用率。更多分区=更好的并行性和更高的资源利用率。

我在这里可能会做的是修改PRIMARY KEY以包含column1。然后,我将使用column作为集群密钥(不仅规定分区内的唯一性,还要排序 - 因此请在列命名约定中考虑这一点)。

在下表定义中,您需要在WHERE条款中提供useridcolumn1作为平等。

CREATE TABLE data (
    userid bigint, 
    column1 text, 
    column text, 
    value text, 
    PRIMARY KEY ((userid, column1), column) 
); 

我也不得不单独的表,也许columns_per_user,即记录每个userid所有列。像

CREATE TABLE columns_per_user (
    userid bigint, 
    max_columns int, 
    column_names text 
    PRIMARY KEY (userid) 
); 

哪里max_columns是列此用户的总数和column_names东西是实际的列名。您可能还会有一列每个用户的总条目数,如user_entries int这基本上是每个用户csv文件中的行数。

+0

这是一个很好的答案。感谢您抽出时间写下它。我想知道为什么(或换句话说 - 目的是什么)保持列数(max_columns)。 – FuzzyAmi

+0

很好的答案。感谢您抽出宝贵的时间。我的另一个考虑是我们有多个系统访问这些数据。在某些情况下,每个键都会查询所有列(即select * from table where key = xxx),因此使用包含所有内容的“blob”列是有意义的,而在其他情况下,只有某些列被检索并“分组”按一定的标准。另外我知道部分数据库将会非常热(取决于user_id),而其他数据量将会减少。 –

+0

@FuzzyAmi只是一种便利功能。否则,你必须将完整的json拉出来并进行计数(循环数组,键入等) – siculars

尝试下面的模式:

CREATE TABLE data (
    userid bigint, 
    key text, 
    column text, 
    value text, 
    PRIMARY KEY (userid, key) 
); 

这里

userid -> userid 
key  -> column1 
column -> column name from column2 
value -> column value 

实施例插入用于下面的数据:

| column1 (unique key per user_id) | column2  | column3  | 
|-----------------------------------|---------------|-----------------| 
| key_1        | value12  | value13  | 
| key_2        | value22  | value23  | 

插入语句:

INSERT INTO data (userid , key , column , value) VALUES (1, 'key_1', 'column2', 'value12'); 
INSERT INTO data (userid , key , column , value) VALUES (1, 'key_1', 'column3', 'value13'); 
INSERT INTO data (userid , key , column , value) VALUES (1, 'key_2', 'column2', 'value22'); 
INSERT INTO data (userid , key , column , value) VALUES (1, 'key_2', 'column3', 'value23');