【发布时间】: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