【问题标题】:Datastore queries take too long when results are lower than query limit当结果低于查询限制时,数据存储查询花费的时间太长
【发布时间】:2017-03-06 16:48:34
【问题描述】:

我有一个可与待办事项应用相媲美的应用。在任何给定时间,都有几个待定任务分配给单个用户。一些用户有将近 2500 个任务待处理,而一些用户只有 2 个。

当匹配查询的结果低于应用于查询的限制时,数据存储查询似乎花费了太长时间。示例:

场景 1:

用户 A:有 2500 个待处理任务。查询限制为 500,第一次请求获取的结果显然是 500。所用时间:5767 毫秒(5.7 秒)。

用户 B:有 2 个待处理任务。查询限制为 500,第一个请求获取的结果显然是 2。所用时间:7124 毫秒(7.1 秒)。

场景 2:

用户 A:有 2500 个待处理任务。查询限制为 10,第一个请求获取的结果显然为 10。所用时间:~400 毫秒(1/2 秒)。

用户 B:有 2 个待处理任务。查询限制为 10,第一次请求获取的结果显然为 2。所用时间:5-6 秒。

场景 3:

用户 A:有 2500 个待处理任务。查询限制为 500,第一次请求获取的结果显然为 500。所用时间:6244 毫秒(6 秒)。

用户 C:有 551 个待处理任务。查询限制为 500,第一次请求获取的结果显然为 500。所用时间:13579 毫秒(13 秒)。

我的代码:

public static Map <String , Object> getEntitiesUsingQueryCursor( String kind , int limit , int chunkSize , String currentCursor, String account, String user, Boolean status, String dept ) throws Exception
        {

            String nextCursor = null;

            Entity entity = null;

            List <Entity> listOfEntity = new ArrayList <Entity>();

            Map <String , Object> result = new HashMap <String , Object>();


            DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
            com.google.appengine.api.datastore.Query q = new com.google.appengine.api.datastore.Query( kind );

List <Filter> listOfFilter = new ArrayList <Filter>();
Filter filter1 = new FilterPredicate( "account" , FilterOperator.EQUAL ,  account);
Filter filter2 = new FilterPredicate( "user" , FilterOperator.EQUAL ,  user);
Filter filter3 = new FilterPredicate( "dept" , FilterOperator.EQUAL ,  dept);
Filter filter4 = new FilterPredicate( "status" , FilterOperator.EQUAL ,  status); //Boolean
listOfFilter.add( filter1 );
listOfFilter.add( filter2 );
listOfFilter.add( filter3 );
listOfFilter.add( filter4 );
Filter filterParams1 = filterParams = CompositeFilterOperator.and( listOfFilter );
q.setFilter( filter );

            PreparedQuery pq = datastore.prepare( q );
            FetchOptions fetchOptions = FetchOptions.Builder.withLimit(limit).prefetchSize( chunkSize ).chunkSize( chunkSize );

            if ( !StringUtil.isBlank( currentCursor ) )
                fetchOptions.startCursor( Cursor.fromWebSafeString( currentCursor ) );

            QueryResultIterable <Entity> results = pq.asQueryResultIterable( fetchOptions );
            QueryResultIterator <Entity> iterator = results.iterator();

            while ( iterator.hasNext() )
                {
                    entity = iterator.next();
                    listOfEntity.add( entity );
                }

            if(listOfEntity.size() == limit)
                nextCursor = iterator.getCursor().toWebSafeString();

            result.put( "cursor" , nextCursor );
            result.put( "entity" , listOfEntity );

            return result;
        }

这就是数据存储查询的工作方式吗?有人可以提出更好的查询实体的方法吗?如果我将查询的平均限制设置为 50,则待处理任务少于 50 个的用户必须等待至少 7 秒才能在页面上获取任务。即使我将限制设置为 10 并且用户只有 2 个待处理任务,也适用 7 秒时间。

【问题讨论】:

  • 猜测一下,您的查询必须为每个过滤器扫描一个索引。查看cloud.google.com/appengine/articles/indexselection#Performance 了解索引如何影响查询性能。
  • @snakecharmerb 当查询限制为 10 并且匹配过滤器的结果超过 2500 时,延迟为 400 毫秒。如果我应用相同的查询并且与过滤器匹配的结果为 2(小于限制 10),则所用时间为 5-6 秒。真的是关于索引吗?
  • 有可能。考虑:在数据存储中限制为 10 和 2500+ 个匹配项,查询引擎可以在找到 10 个匹配项后停止查询。由于数据存储中有 10 个和 2 个匹配项,查询引擎将在返回之前读取每条可能匹配的记录。因此,限制大于匹配数的查询保证具有最坏情况的性能。那么问题就变成了,如何减轻这种情况?一个可能的答案是制作索引以在最坏的情况下最大化性能。另一个答案是取消限制,但我认为这不是一个选项。
  • @snakecharmerb 谢谢,让我看看文档并回复你
  • @snakecharmerb 如果这听起来很愚蠢,请原谅我,但我的查询没有排序顺序。我只有 4 个“等于”过滤器,这根本不需要任何索引,为其他查询添加的现有索引会影响我的结果吗?

标签: google-app-engine google-cloud-datastore


【解决方案1】:

如果您在account,user,dept,status 上定义composite index,则回答上述查询将只需要对单个索引进行线性扫描,这将大大提高查询速度(无论限制如何)。

为了说明,假设你有 [row] [account, user, dept, status] [entity] 1] A B C D e1 2] A B E F e2 3] A B E F e3 4] A F A A e4 5] B A Z E e5 查询'A B E F' 将找到行[2],然后线性扫描到[3],返回[e1, e2]。它会停在[4](不匹配的第一行)做的很少的工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-08
    • 2019-11-14
    • 2012-09-03
    • 2013-07-11
    • 2017-09-24
    相关资源
    最近更新 更多