1

我正在 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",但我确实得到了同样的错误,并且没有添加新的索引。
  • 我试图在新创建的命名空间中执行查询:没问题。
  • 开发服务器中不会​​出现该错误。

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

非常感谢!

4

1 回答 1

0

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

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

因此,查看一组具有 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 对它们进行排序. 在这里,一切都是预先计算的,而且速度要快得多。这允许更多的读取吞吐量。对于您的应用程序来说,这可能有点过头了,但想法是您的应用程序可以通过这种方式扩展到巨大的尺寸。

希望这是有道理的。

于 2013-09-14T04:59:39.080 回答