【发布时间】: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