DynamoDB 中本地索引和全局索引的区别

Posted

技术标签:

【中文标题】DynamoDB 中本地索引和全局索引的区别【英文标题】:Difference between local and global indexes in DynamoDB 【发布时间】:2014-02-18 08:15:02 【问题描述】:

我很好奇这两个二级索引以及它们之间的区别。很难想象这是什么样子。而且我认为,这将帮助更多的人,而不仅仅是我。

【问题讨论】:

在DynamoDB FAQ 中回答。搜索“全局二级索引与本地二级索引有何不同?” 现在从常见问题解答中不太容易找到。也许它被重组了 AWS 官方文档及对比表:docs.aws.amazon.com/en_pv/amazondynamodb/latest/developerguide/…docs.aws.amazon.com/en_pv/amazondynamodb/latest/developerguide/… 很遗憾,DynamoDB 常见问题解答中没有回答。 【参考方案1】:

GSI 不能用于一致性读取。

LSI 可用于一致性读取,但它们会将主分区大小限制为 10GB。此外,LSI 只能在创建表时创建。

【讨论】:

【参考方案2】:

这是文档中的正式定义:

全局二级索引 — 具有哈希和范围键的索引,可以 与桌子上的不同。全局二级索引是 被认为是“全局的”,因为对索引的查询可以跨越所有 跨所有分区的表中的数据。

本地二级索引 — 具有相同哈希键的索引 表,但不同的范围键。本地二级索引是“本地” 从某种意义上说,本地二级索引的每个分区都有范围 到具有相同哈希键的表分区。

但是,就关键定义而言,差异远远超出了可能性。找出以下将直接影响维护索引的成本和工作量的一些重要因素:

吞吐量:

本地二级索引消耗表的吞吐量。当您通过本地索引查询记录时,该操作会消耗表中的读取容量单位。当您在具有本地索引的表中执行写操作(创建、更新、删除)时,将有两个写操作,一个针对表,另一个针对索引。这两个操作都将消耗表中的写入容量单位。

全局二级索引有自己的预置吞吐量,当您查询索引时,该操作将消耗索引的读取容量,当您在具有全局索引的表中执行写入操作(创建、更新、删除)时,有将是两个写操作,一个用于表,另一个用于索引*。

*在为全局二级索引定义预置吞吐量时,请务必特别注意以下要求:

为了使表写入成功,提供的吞吐量 表及其所有全局二级索引的设置必须 有足够的写入容量来容纳写入;否则, 写入表将受到限制。

管理:

本地二级索引只能在创建表时创建,无法将本地二级索引添加到现有表中,创建索引后也无法删除。

全局二级索引可以在创建表时创建并添加到现有表中,也可以删除现有的全局二级索引。

读取一致性:

本地二级索引支持最终一致性或强一致性,而全局二级索引仅支持最终一致性。

投影:

本地二级索引允许检索未投影到索引的属性(尽管有额外的成本:性能和消耗的容量单位)。使用全局二级索引,您只能检索投影到索引的属性。

关于定义到二级索引的键的唯一性的特殊考虑:

在本地二级索引中,对于给定的哈希键值,范围键值不需要是唯一的,同样适用于全局二级索引,键值(哈希和范围)不需要是唯一的。

来源:http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html

【讨论】:

"全局二级索引有自己的预置吞吐量,当您查询索引时,该操作将消耗表中的读取容量" - 错误。对全局二级索引的查询或扫描会消耗索引中的容量单位,而不是基表中的容量单位。 @bsd 添加一条关于使用 LSI 的主要限制的注释是有意义的:“对于具有本地二级索引的表,每个分区键值有 10 GB 的大小限制。A具有本地二级索引的表可以存储任意数量的项目,只要任何一个分区键值的总大小不超过 10 GB。” (docs.aws.amazon.com/amazondynamodb/latest/developerguide/…)【参考方案3】:

一种说法是这样的:

LSI - 允许您对单个 Hash-Key 执行查询,同时使用多个不同的属性来“过滤”或限制查询。

GSI - 允许您对表中的多个 Hash-Key 执行查询,但因此会增加吞吐量。

下面对表格类型及其工作方式进行了更广泛的细分:

仅哈希

你可能已经知道了; Hash-Key 本身必须是唯一的,因为写入已经存在的 Hash-Key 会覆盖现有数据。

哈希+范围

Hash-Key + Range-Key 允许您拥有多个相同的 Hash Key,只要它们具有不同的范围键即可。在这种情况下,如果您写入已存在的 Hash-Key,但使用该 Hash-Key 尚未使用的 Range-Key,则会创建一个新项目,而如果项目具有相同的 Hash+Range 组合已经存在,它会覆盖匹配项。

另一种思考方式是像具有格式的文件。您可以在同一个文件夹(表)中拥有与另一个文件同名(哈希)的文件,只要它们的格式(范围)不同。同样,您可以拥有多个相同格式的文件,只要它们的名称不同即可。

大规模集成电路

LSI 与 Hash-Key + Range-Key 基本相同,并且在创建项目时遵循与它相同的规则,不同之处在于您还必须为 LSI 提供值;它们不能为空/null。

说 LSI 是“Range-Key 2”并不完全正确,因为您不能拥有(使用我之前的文件和格式类比)名为:file.format.lsifile.format.lsi2 的文件。但是,您可以使用 file.format.lsifile.format2.lsifile.format.lsifile2.format.lsi

基本上,LSI 只是一个“Filter-key”,而不是实际的 Range-Key;您的基本 Hash 和 Range 值组合必须仍然是唯一的,而 LSI 值根本不必是唯一的。一种更简单的看待它的方法可能是将 LSI 视为文件中的数据。您可以编写代码来查找所有名称为“PROJECT101”的文件,无论它们的fileFormat 是什么,然后读取其中的数据以确定查询中应包含哪些内容以及省略哪些内容。这基本上就是 LSI 的工作原理(只是没有打开文件以读取其内容的额外开销)。

GSI

对于 GSI,您实际上是在为每个 GSI 创建另一个表,但无需维护多个在它们之间镜像数据的单独表;这就是它们花费更多吞吐量的原因。

因此,对于 GSI,您可以指定 fileName 作为您的基本 Hash-Key,并指定 fileFormat 作为您的基本 Range-Key。然后,您可以指定具有 fileName2 的哈希键和 fileFormat2 的范围键的 GSI。然后,您可以根据需要查询fileNamefileName2,这与LSI 不同,您只能查询fileName

主要优点是您只需要维护一个表,而不是 2 个,并且每当您写入主哈希/范围或 GSI 哈希/范围时,其他(多个)将自动更新同样,因此您不能像使用多表设置一样“忘记”更新其他表。此外,在更新一个之后再更新另一个之前,也不会丢失连接,就像多表设置一样。

此外,GSI 可以“重叠”基本 Hash/Range 组合。因此,如果您想创建一个表,其中 fileNamefileFormat 作为您的基本哈希/范围,filePriorityfileName 作为您的 GSI,您可以。

最后,GSI Hash+Range 组合不必是唯一的,而基本 Hash+Range 组合必须是唯一的。这在双/多表设置中是不可能的,但在 GSI 中是不可能的。因此,您必须在更新时同时提供基础和 GSI 哈希+范围的值;这些值都不能为空/null。

【讨论】:

所以 LSI 就像是文件(项目)的“标签”?【参考方案4】:

这个文档给出了很好的解释:

https://aws.amazon.com/blogs/aws/now-available-global-secondary-indexes-for-amazon-dynamodb/

我无法评论这个问题,但在读写性能方面哪个更好:

(表读写吞吐量为100的本地索引)或(读/写吞吐量为50的全局索引以及表的读/写吞吐量为50的全局索引?)

我的用例不需要单独的分区键,因此本地索引应该足以满足所需的功能。

【讨论】:

【参考方案5】:

本地二级索引仍然依赖于原始哈希键。 当您提供带有 hash+range 的表时,请将 LSI 视为 hash+range1、hash+range2.. hash+range6。 您还有 5 个范围属性可供查询。 此外,只有一个预置吞吐量。

全局二级索引定义了一个新的范例 - 每个索引都有不同的哈希/范围键。 这打破了每个表一个哈希键的原始用法。 这也是为什么在定义 GSI 时需要为每个索引添加预置吞吐量并为此付费的原因。

有关差异的更多详细信息,请参阅GSI announcement

【讨论】:

可能要添加1)二级索引,无论是LSI还是GSI,与唯一性无关 你最多可以有 5 个本地二级索引,这就是为什么陈哈雷尔说 “你还有 5 个范围属性要查询”【参考方案6】:

这些是可能的索引搜索:

通过哈希 按哈希 + 范围 按哈希 + 本地索引 按全球索引 按全局索引 + 范围索引

表的哈希和范围索引: 这些是以前版本的 Amazon AWS 开发工具包的常用索引。

全局和本地索引: 除了表的现有哈希和范围索引之外,这些是在表上创建的“附加”索引。 全局索引类似于哈希。 范围索引的行为类似于与表的哈希一起使用的范围索引。 在代码中的实体模型中,getter 必须以这种方式注释:

对于全局索引:

@DynamoDBIndexHashKey(globalSecondaryIndexName = INDEX_GLOBAL_RANGE_US_TS)
@DynamoDBAttribute(attributeName = PROPERTY_USER)
public String getUser() 
    return user;

对于与全局索引关联的范围索引:

@DynamoDBIndexRangeKey(globalSecondaryIndexName = INDEX_GLOBAL_RANGE_US_TS)
@DynamoDBAttribute(attributeName = PROPERTY_TIMESTAMP)
public String getTimestamp() 
    return timestamp;

此外,如果通过全局索引读取表,则必须是最终读取(不是一致读取):

queryExpression.setConsistentRead(false);

【讨论】:

【参考方案7】:

另一种解释方式:LSI 帮助您对具有相同 Hash Key 的项目进行额外查询。 GSI 帮助您对“整个表格”的项目进行类似的查询。非常有用。

如果您有用户个人资料表: 唯一 ID、姓名、电子邮件。在这里,如果您需要使表格可根据名称、电子邮件进行查询 - 那么唯一的方法就是使它们成为 GSI(LSI 不会有帮助)

【讨论】:

你能详细说明为什么你只能用 GSI 做到这一点吗?

以上是关于DynamoDB 中本地索引和全局索引的区别的主要内容,如果未能解决你的问题,请参考以下文章

并行执行DynamoDB查询(全局二级索引的BatchGetItems)

全局索引和本地索引的区别

跨主索引和全局二级索引的 DynamoDB 键唯一性

dynamoDB 中全局二级索引范围键的最大值

DynamoDB:查询中的全局二级索引利用率

使用全局二级索引的 DynamoDB 表查询项