【问题标题】:Why do my AppEngine queries throw DatastoreNeedIndexExceptions although the required indices are 'serving'?为什么我的 AppEngine 查询会抛出 DatastoreNeedIndexExceptions,尽管所需的索引正在“服务”?
【发布时间】:2024-05-04 08:30:06
【问题描述】:

我正在 Google AppEngine 上运行一个多租户 Java 高复制 Web 应用程序。应用程序成功使用了多属性索引(在 datastore-indexes.xml 文件中配置)。好吧,至少到现在为止……

从今天开始,至少有一个命名空间在执行查询时会引发 DatastoreNeedIndexExceptions。奇怪的是,相同的查询在其他命名空间中也有效。

这是 datastore-indexes.xml 中的索引配置和管理面板中的索引状态:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="false">
    <datastore-index kind="Index_Asc_Asc_Asc_Asc" ancestor="false" source="manual">
        <property name="components" direction="asc"/>
        <property name="component_0" direction="asc"/>
        <property name="component_1" direction="asc"/>
        <property name="component_2" direction="asc"/>
        <property name="component_3" direction="asc"/>
    </datastore-index>
</datastore-indexes>

对应的查询如下所示:

SELECT __key__ FROM Index_Asc_Asc_Asc_Asc WHERE components = '12340987hja' AND component_0 = 'asdfeawsefad' AND component_1 = '4FnlnSYiJuo25mNU' AND component_3 = 'cvxyvsdfsa' AND component_2 >= 0

当我在我的应用程序或管理面板数据存储视图中执行此查询时,App Engine 会抛出 DatastoreNeedIndexException 并提供以下建议。同样,相同的查询也适用于其他命名空间:

The suggested index for this query is:
<datastore-index kind="Index_Asc_Asc_Asc_Asc" ancestor="false">
  <property name="component_0" direction="asc" />
  <property name="component_1" direction="asc" />
  <property name="component_3" direction="asc" />
  <property name="components" direction="asc" />
  <property name="component_2" direction="asc" />
</datastore-index>

调查:

  • 我尝试设置 autoGenerate="true",但确实遇到了同样的错误,并且没有添加新的索引。
  • 我已尝试在新创建的命名空间中执行查询:没问题。
  • 开发服务器中没有出现该错误。

我有什么遗漏吗?有没有其他人遇到过同样的问题?为什么相同的查询在其他命名空间中有效,但在该命名空间中无效?

谢谢!

【问题讨论】:

  • 错误中建议的索引与您所说的定义的不同。您是否手动添加了建议的索引定义?
  • 您好,蒂姆,感谢您的评论!它在哪些方面有所不同?财产顺序重要吗?有一些文件吗?谢谢!
  • 逐行查看索引规格。它们必须完全匹配。秩序很重要。只需剪切并粘贴索引规范并进行部署。全部在文档中。
  • 蒂姆,你是对的。秩序很重要。我还没有在文档中找到相应的信息,但我只是添加了推荐的索引以进行测试。有用。不幸的是,在我的设置中,不可能盲目地添加该索引。不过,感谢您的帮助!
  • 实际上文档确实提到了它。索引是查询和排序顺序的组合。如果您阅读了索引是如何工作的,那么很明显索引的每个成员和顺序都必须与您正在使用的查询相匹配。我很好奇为什么您不能添加与您希望执行的查询匹配的索引规范。正如您所发现的,如果不添加索引,查询将不起作用。

标签: java google-app-engine


【解决方案1】:

蒂姆是对的。为了帮助阐明这一点,您需要了解数据存储的工作原理。

基本上,所有数据存储读取都需要在您正在查看的索引中按顺序进行。换句话说,它们需要在相邻的行中。这就是数据存储如何提高速度以及如何跨多台机器进行分片的方式。 (平等匹配有一些例外情况,但请接受聪明人暂时为我们解决这个问题)。

因此查看一组具有 num 列、alpha 列和 id 列的数据,如下所示:

    id   Num    Alpha
---------------------
     1     1        A
     2     1        Z
     3     4        A
   ...   ...      ...  <-- lots of data
100004     2        Z
100005     1        C

因此,当数据存储区通过像您这样的查询时,它会查看预先计算的索引并找到匹配的起点。然后它将读取,直到行不再与查询匹配。它永远不会像您在 SQL 中习惯的那样进行连接。关闭的东西是仅适用于相等运算符的拉链合并。行在索引中必须相邻!

所以 index num asc, alpha asc 看起来像:

    id   Num    Alpha
---------------------
   ...   ...      ...  <- negative numbers
     1     1        A
100005     1        C
     2     1        Z
100004     2        Z
     3     4        A
   ...   ...      ...  <-- lots of data (assume all other num values were above 5)

和索引 alpha asc,num asc 看起来像:

    id   Num    Alpha
---------------------
     1     A        1
     3     A        4
   ...   ...      ...  <-- lots of data
100005     C        1
   ...   ...      ...  <-- lots of data
     2     Z        1
100004     Z        2
   ...   ...      ...  <-- lots of data 

这允许数据存储区快速压缩您的数据以非常快速地获得答案。然后它可以使用 id 来查找该行的其余数据。

例如,如果您尝试查看所有 num=1 并希望所有 alpha 顺序排序,则必须将所有 num=1 行读入内存(可能是数百万行)然后对它们进行排序基于 A。这里都是预先计算的,而且速度更快。这允许更多的读取吞吐量。这对您的应用程序来说可能有点矫枉过正,但想法是您的应用程序可以通过这种方式扩展到巨大的尺寸。

希望这是有道理的。

【讨论】:

    最近更新 更多