【问题标题】:Reverse Indexing and Data modeling in Key-Value storeKey-Value 存储中的反向索引和数据建模
【发布时间】:2020-06-24 12:33:21
【问题描述】:

我是key-value 商店的新手。我的目标是使用嵌入式键值存储来保持持久数据模型。如果使用传统的 RDBMS 设计,数据模型包含很少的相关表。我正在检查medium article 为键值存储建模表。虽然本文使用 Java 的 Level DB,但我计划在工作中使用 RocksDB 或 FASTER 和 C++。

它使用一种方案,每行的每个属性都使用一个键,如下例所示。

$table_name:$primary_key_value:$attribute_name = $value

当用户代码知道确切要获取哪个键时,以上内容适用于点查找。但也有一些场景,例如搜索具有相同电子邮件地址的用户,或搜索特定年龄以上的用户或搜索特定性别的用户。在搜索场景中,文章对所有键执行线性扫描。在每次迭代中,它都会检查键的模式,并在找到具有匹配模式的键后应用业务逻辑(检查值是否匹配)。

看起来,这种类型的搜索效率低下,最坏的情况是它需要遍历整个商店。为了解决这个问题,需要反向查找表。我的问题是

如何建模反向查找表?这是对轮子的某种改造吗?有没有其他办法?

一个容易想到的解决方案是为每个可索引的属性存储一个separate ?,如下所示。

$table_name:$attribute_name:$value_1 = $primary_key_value 

使用这种方法,直接的问题是

如何处理反向查找表中的冲突?因为多个$primary_keys 可能与同一个值相关联。

作为一种直接的解决方案,可以存储多个主键的array,而不是存储单个值,如下所示。

$table_name:$attribute_name:$value_1 = [$primary_key_value_1, ... , $primary_key_value_N]

但这种类型的建模需要用户代码从字符串解析数组,并在多次操作后再次将其序列化为字符串(假设底层键值存储不知道数组值)。

将多个键存储为数组值是否有效?还是存在一些供应商提供的有效方式?

假设像设计这样的字符串化数组有效,每个可索引属性都必须有这样的索引。因此,这对要索引的内容和不索引的内容提供了细粒度的控制。想到的下一个设计决策是这些索引将存储在哪里?

是否应该将索引存储在单独的存储/文件中?还是在实际数据所属的同一个商店/文件中?每个物业应该有不同的商店吗?

对于这个问题,我不知道,因为这两种方法都需要或多或少相同数量的 I/O。然而,拥有大数据文件将在磁盘上有更多的东西,在内存上会有更少的东西(所以更多的 I/O),而对于多个文件,内存上会有更多的东西,所以页面错误更少。根据特定键值存储的体系结构,这种假设可能完全错误。同时拥有太多文件会导致管理复杂文件结构的问题。此外,维护索引需要用于插入、更新和删除操作的事务。拥有多个文件会导致在多个树中进行一次更新,而拥有单个文件会导致在单个树中进行多次更新。

是否支持更具体的涉及多个存储/文件的事务?

不仅是索引,还有一些表的元信息也需要与表数据一起保存。要生成一个新的主键(自动递增),需要先了解最后一个行号或生成的最后一个主键,因为像COUNT(*) 这样的东西不起作用。此外,由于未对所有键编制索引,meta 信息可能包括已编制索引的属性和未编制索引的属性。

如何存储每个表的元信息?

同样的一组问题也出现在元表中。例如元数据应该是一个单独的存储/文件吗?此外,由于我们注意到并非所有属性都被索引,我们甚至可能决定将每一行作为 JSON 编码值存储在数据存储中,并将其与索引存储一起保存。底层键值存储供应商会将该 JSON 视为字符串值,如下所示。

$table_name:data:$primary_key_value = {$attr_1_name: $attr_1_value, ..., $attr_N_name: $attr_N_value}
...
$table_name:index:$attribute_name = [$primary1, ..., $primaryN]

但是,通过指向主键的索引仍然可以进行反向查找。

使用 JSON 编码值而不是将所有属性存储为单独的键有什么缺点吗?

到目前为止,除了强制用户使用 JSON 编码和一些用于 JSON 编码/解码的堆分配之外,我找不到使用此方法的任何缺点。

上述问题并非特定于任何特定应用程序。这些问题足够普遍,可以与使用key-value 商店的所有开发相关联。所以有必要知道是否有轮子的再发明。

问题中提到的所有问题是否有任何事实上的标准解决方案?解决方案是否与问题中所述的不同?

【问题讨论】:

    标签: database data-modeling key-value-store leveldb rocksdb


    【解决方案1】:

    如何建模反向查找表?这是对轮子的某种改造吗?有没有其他办法?

    • 您描述的所有方式都是创建索引的有效方式。
    • 它不会在 RocksDB 中重新发明轮子,因为 RocksDB 不支持索引。
    • 这真的取决于数据,一般你需要将索引值和主键复制到另一个空间来创建索引。

    如何处理反向查找表中的冲突?因为多个 $primary_keys 可能与同一个值相关联。

    您可以使用 JSON(或其他方式)序列化 pks。这种方法的问题是当 pk 变得非常大时(这可能是也可能不是)。

    将多个键存储为数组值是否有效?还是存在一些供应商提供的有效方式?

    有了 RocksDB,没有什么能让它“更轻松”了。

    您没有提到以下方法:

    $table_name:$attribute_name:$value_1:$primary_key_value_1 = ""
    $table_name:$attribute_name:$value_1:$primary_key_value_2 = ""
    ...
    
    $table_name:$attribute_name:$value_1:$primary_key_value_n = ""
    

    值为空的地方。而索引的pk 是键的一部分。

    是否应该将索引存储在单独的存储/文件中?还是在实际数据所属的同一个商店/文件中?每个物业应该有不同的商店吗?

    这取决于键值存储。使用rocksdb,如果你需要事务,你必须坚持一个db文件。

    是否支持更具体的涉及多个存储/文件的事务?

    只有 Oracle Berkeley DB 和 WiredTiger 支持该功能。

    如何存储每个表的元信息?

    元数据可以在数据库或代码中。

    使用 JSON 编码值而不是将所有属性存储为单独的键有什么缺点吗?

    是的,就像我上面说的,如果你将所有的 pk 编码为一个值,当 pk 的数量很大时,可能会导致下游出现问题。例如,您需要阅读整个列表才能进行分页。

    问题中提到的所有问题是否有任何事实上的标准解决方案?解决方案是否与问题中所述的不同?

    总结一下:

    • 使用 RocksDB,使用单个数据库文件
    • 在索引中,将主键编码在键内,并将值留空,以便能够分页。

    【讨论】:

    • 如何有效地搜索所有匹配 $table_name:$attribute_name:$value_1::* 的 pk ?我可以在没有迭代方法的情况下获得与前缀匹配的一系列键吗?它看起来像范围查找,但不知道确切的下限和上限
    • 查看 fdb strinc 函数,它将返回不是给定字节前缀的下一个字节,这将允许执行类似 range(prefix, strinc(prefix)) 的操作。
    • 抱歉,我对 C/C++ RocksDB API 不是很熟悉。它有cursor、next 和prev 的概念吗?如果是这种情况,上述range 技巧只是一种避免反序列化关键字节以了解您是否处于良好范围内的性能。为简单起见,您可以执行您描述的操作并反序列化密钥,检查它是否在范围内。
    猜你喜欢
    • 1970-01-01
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多