【发布时间】:2013-12-12 03:04:38
【问题描述】:
我一直在尝试准确了解各种类型的索引如何影响每个实体所需的写入操作数。我以为我已经弄清楚了,但后来我发现了一个让我陷入困境的案例。
我有一个根用户实体,其中包含多个 GameSummary 子实体,这些子实体具有时间戳和一堆其他统计字段(想想玩家姓名、得分等)。
我的目标是能够查询所有用户或特定用户的 GameSummaries 列表。结果应该全部按时间戳降序排序,我可能想稍后添加一个搜索功能,在其他一些 GameSummary 字段上添加相等过滤器并以相同的排序顺序返回结果。这一切都相当简单,但我显然希望用尽可能少的索引来做到这一点。
在时间戳属性上使用默认索引会创建两个索引(升序和降序),但我只需要其中一个索引,因此另一个写入只是开销。我的第一个想法是将属性设置为“noindex”(在 python 中 Indexed=False),然后在 index.yaml 中添加我需要的降序索引。这最终无法正常工作,大概是因为查询计划器尝试使用默认索引并且不返回任何结果。
然后我想到,如果我使用时间戳作为实体键,我可以按 __key__ (没有默认索引)排序并只添加我实际需要的索引。我正在使用 --require_indexes 运行开发服务器,这导致我的查询失败(如预期的那样),直到我添加了这两个索引:
- kind: GameSummary
ancestor: no
properties:
- name: __key__
direction: desc
- kind: GameSummary
ancestor: yes
properties:
- name: __key__
direction: desc
此时,我查看了管理控制台中的 Datastore Viewer,发现我的 GameSummaries 的 Write Ops 列只有 2(实体和 EntitiesByKind)。我期待在这里看到 5 个计数(第一个索引多 1 个,祖先索引多 2 个)。不过,根据开发服务器的说法,我似乎可以免费获得降序的 __key__ 索引写入?
管理控制台是在骗我吗?还是我错过了 __key__ 属性上的一些奇怪的索引?
更奇怪的是,添加具有附加属性的索引,这些属性在 __key__ 上排序,如下所示:
- kind: GameSummary
ancestor: no
properties:
- name: Player
- name: Enemy
- name: __key__
direction: desc
- kind: GameSummary
ancestor: yes
properties:
- name: Player
- name: Enemy
- name: __key__
direction: desc
也不会增加 GameSummary 实体的 Write Ops。我可能会看到这个简单案例发生了一些神奇的事情,但这让我觉得它越来越像开发服务器中的一个错误。
【问题讨论】:
-
我首先要问你为什么使用时间戳作为键?如果两个实体使用完全相同的时间戳写入(当然不太可能),您可能会从 App Engine 收到错误消息。此外,
__key__实际上确实会自动索引,但只能按升序排列(否则,我猜按 ID 获取会很慢)。 -
由于 GameSummary 对象有一个 User 父对象,如果多个实体具有相同的时间戳,只要它们不是来自同一个 User 就可以了。在实践中,一个用户不应该能够同时玩两个游戏来复制时间戳,所以我不担心这种情况下的冲突。