在 Riak 中对主从关系建模的一种常见方法是让主记录包含一个详细记录 ID 列表,可能还包括一些关于详细记录的信息,这些信息在决定要检索哪些详细记录时可能很有用。
在您的示例中,您可以有两个称为“书籍”和“页面”的存储桶。“图书”存储桶中的主记录将包含有关图书整体的元数据和信息,以及图书中包含的页面列表。每个页面将包含保存页面数据的“页面”记录的 ID 以及相应的页码。例如,如果您希望能够按章节查询,您还可以添加有关某个页面所属章节的信息。
“页面”存储桶将包含页面的文本,并可能包含指向该页面中包含的图像和其他媒体数据的链接。该数据可以存储在另一个存储桶中。
为了获取特定页面或一系列页面,首先从“书籍”存储桶中检索主记录,然后根据记录的内容获取适当的页面。尽管这需要多次 GET 操作,但它们都是基于键的直接查找,这是从 Riak 检索数据的最有效和可扩展的方式,因此它的性能和扩展性都很好。
这种方法还可以轻松更改页面和/或章节的顺序,因为只需要更新主记录。然而,添加、删除或修改页面将需要更新、添加或删除主记录以及一个或多个详细记录。
您当然也可以通过向对象添加二级索引并基于此进行查询来解决此问题。然而,Riak 中的二级索引查询确实必须包括对分区覆盖集(通常是环大小/n_val)的处理才能满足请求,因此会给系统带来更多的负载,并且通常会导致比检索更高的延迟通过直接键查找包含键的单个对象(只需要涉及实际存储对象的分区)。
尽管在插入或删除页面/条目时维护一个包含索引的单独对象会增加一些额外的工作,但这种方法通常会导致更有效的读取,因为只需要直接键查找。如果您的应用程序的读取量很大,那么使用这种方法可能是有意义的,而二级索引对于写入量大的应用程序可能更有效,因为插入和修改成本更低,但读取成本更高。但是,您始终可以添加二级索引以防万一,以保持您的选择开放。
在这种情况下,我通常会建议执行一些基准测试来测试解决方案并检查哪种解决方案最符合您的特定性能和扩展要求。