使用 Aerospike 处理由键组合定义唯一性的数据

Using Aerospike to process data where uniqueness is defined by combination of keys

我正在尝试了解如何最好地构建我的 Aerospike 架构。当我在玩它时,我意识到部分问题是我没有完全理解 Aerospike 如何处理数据,这似乎与 RDBMS 和 Cassandra 不同。

我的数据集是记录的集合,因此唯一的 "primary key" 由多个字段的组合定义(如果我在 Aerospike 意义上误用了术语 primary key,我深表歉意 - 最初我是计划用分隔符简单地连接这些字段)。我需要能够通过指定所有这些字段来检索单个记录并检索指定子集的批次。例如,假设我正在存储人口统计数据,其中我的 "primary key" 是我从中获取数据的列 yearlocationsource 的组合。通过指定所有 3 个,我将获得一个准确的记录,如果我指定 2 个或仅指定 1 个,我将获得一个记录集合。

在 RDBMS 中,我会使用索引来实现这一点。在 Cassandra 中,最好的方法是将所有 3 个添加到主键,如果我不能保证在执行搜索时总是有分区键可用,则重新排列它们在物化视图中的顺序。

随着我越来越多地使用 Aerospike,我意识到这里的 PK 不像上面两种情况中的任何一种那样被对待。此外,我开始认为 Aerospike PK 可能根本不应该成为用户数据的一部分,因为默认情况下它们不会返回(除非 sendKeys 在写入之前设置,在这种情况下它们'重新简单地复制到垃圾箱中)。

从阅读文档来看,我真正想要的可能是 secondary indexes(因为它们允许更灵活地查询数据)?索引在这里是正确的方法还是像在 Cassandra 中那样不鼓励使用它们?尝试将 Aerospike 概念与其他 DB 进行比较时,我可能会感到困惑。

好问题 - 需要一个详细的答案,但让我以牺牲整体准确性为代价来保持概念简短。

1 - Aersopike 中的主键是 string/int/bytes 无论您选择什么 -> 由您的应用程序绑定到的客户端库散列为 20 个字节。这个20字节的hash就是发送给服务器的'key',服务器用它来处理你的记录数据。因此,您可以创建一个字符串键:“2020:san_jose:web”,与该键关联的任何数据都将作为记录存储在 Aerospike 中。您可以执行 sendKey 或什至将您的密钥存储为记录中的另一个字符串 bin。但是 Aerospike 用来跟踪您的记录的是“2020:san_jose:web”的 20 字节散列。这种复合键并未隐式绑定到数据箱 - 而是您在应用程序中显式创建。如果您可以 "generate" 这个字符串(在您的应用程序中)用于您感兴趣的一组记录,则可以使用此技术来读取一批记录,然后使用批读取 API。但是你不能使用 bins 中的数据并告诉 Aerospike 为你 "generate" 这个密钥,找到匹配的记录并 return 它们。

2 - 可以使用二级索引吗?在 Aerospike 中,您最多可以构建 256 个 SI,但在给定的查询调用中只能使用一个。 (出于 RAM 和其他操作考虑,我不建议构建多个。)bin 数据的基数越高,您需要的 RAM 就越多。索引建立在进程 RAM 中 - 有其自身的操作含义 - 并将查询与散列键相关联,因此与候选记录相关联。因此,假设您在 city=="san_jose" 上使用 SI - 这将产生一个记录子集。 (选择一个将数据剔除到总数 15% 的 SI,理想情况下 - 建议。)现在,这将检索 city bin 匹配 san_jose 的所有记录。这一切都在 RAM 中——所以速度很快。之后它将从磁盘读取所有这些记录并开始发送回客户端。

3 - 此时,您还有机会编写非常复杂的谓词过滤器。因此,您可以说,在这个检索到的集合中,将记录发送给我,其中 year=2020 和 source=web ...您需要的任何逻辑 AND OR NOT 条件,正则表达式等(我牺牲了准确性以推动更大的观点。您也可以运行 在从磁盘获取记录之前发生在 RAM 级别的记录元数据的 predex 过滤器。)

4 - 最后,为什么在分布式数据库中不鼓励 SI?如果集群稳定,它们会很好地工作。如果节点进出,数据将被迁移以创建副本 - SI 查询 运行s 与迁移数据并行 - 您可能会丢失或获得重复项。将 SI 查询视为相对 'long' 运行ning 操作。在 Aerospike 中,如果您确保在启动 SI 查询之前数据没有迁移,则可以设置一个可选标志 - failOnClusterChange - 如果节点在活动期间退出或加入,您的查询将失败(客户端得到通知) SI查询。根据您的数据模型,您可能关心也可能不关心 SI 查询的 100% 准确性。

此代码示例可能有助于理解。即使我没有在 "country" 上使用二级索引,这也会起作用,它只是效率较低 - 即它会提取 namespace/set 中的每条记录并应用 PredEx。

public void read () {
                Record record = null;
                Statement stmt = new Statement();
                stmt.setSetName("testset");
                stmt.setNamespace("test");
                stmt.setIndexName("country_idx");
                stmt.setFilter(Filter.equal("country","USA")); 
                stmt.setPredExp(
                        PredExp.stringBin("city"),
                        PredExp.stringValue(".*Diego"),  //prefix-suffix match: prefix.*suffix, ignore case or newline
                        PredExp.stringRegex(RegexFlag.ICASE | RegexFlag.NEWLINE)
                );

                RecordSet recordSet = this.client.query(queryPolicy, stmt);
            while (recordSet.next()) {
                record = recordSet.getRecord();
                System.out.println(record.toString());
            }

         }

顺便说一句,客户端库是开源的,并且有所有 API 的示例。对于 java,请参阅:https://github.com/aerospike/aerospike-client-java/tree/master/examples