【问题标题】:GAE datastore inequality filter two properties adviceGAE 数据存储不等式过滤器两个属性建议
【发布时间】:2016-05-26 05:00:18
【问题描述】:

我有一个场景,我需要在数据存储中查询一些在过去 X 分钟内一直处于活动状态的随机用户。

我的每个用户实体都有一个名为“随机”的属性。当我想找到一些随机用户时,我会生成一个随机的最小值和最大值,并使用它们来查询数据存储区中的用户随机属性。

这是我目前得到的:

public static List<Entity> getRandomUsers(Key filterKey, String gender, String language, int maxResults) {
    ArrayList<Entity> nonDuplicateEntities = new ArrayList<>();

    HashSet<Entity> hashSet = new HashSet<>();
    int attempts = 0;
    while (nonDuplicateEntities.size() < maxResults) {
        attempts++;
        if (attempts >= 10) {
            return nonDuplicateEntities;
        }

        double ran1 = Math.random();
        double ran2 = Math.random();

        Filter randomMinFilter = new Query.FilterPredicate(Constants.KEY_RANDOM, Query.FilterOperator.GREATER_THAN_OR_EQUAL, Math.min(ran1, ran2));
        Filter randomMaxFilter = new Query.FilterPredicate(Constants.KEY_RANDOM, Query.FilterOperator.LESS_THAN_OR_EQUAL, Math.max(ran1, ran2));
        Filter languageFilter = new Query.FilterPredicate(Constants.KEY_LANGUAGE, Query.FilterOperator.EQUAL, language);

        Filter randomRangeFilter;
        if (gender == null || gender.equals(Constants.GENDER_ANY)) {
            randomRangeFilter = Query.CompositeFilterOperator.and(randomMinFilter, randomMaxFilter, languageFilter);
        } else {
            Filter genderFilter = new Query.FilterPredicate(Constants.KEY_GENDER, Query.FilterOperator.EQUAL, gender);
            randomRangeFilter = Query.CompositeFilterOperator.and(randomMinFilter, randomMaxFilter, genderFilter, languageFilter);
        }

        Query q = new Query(Constants.KEY_USER_CLASS).setFilter(randomRangeFilter);

        PreparedQuery pq = DatastoreServiceFactory.getDatastoreService().prepare(q);

        List<Entity> entities = pq.asList(FetchOptions.Builder.withLimit(maxResults - nonDuplicateEntities.size()));
        for (Entity entity : entities) {
            if (filterKey.equals(entity.getKey())) {
                continue;
            }
            if (hashSet.add(entity)) {
                nonDuplicateEntities.add(entity);
            }
            if (nonDuplicateEntities.size() == maxResults) {
                return nonDuplicateEntities;
            }
        }
    }

    return nonDuplicateEntities;
}

我现在只需要最近活跃的用户。

每个用户实体还有一个“上次活动”属性,我想将其包含在查询中,例如最后一次活动 > 30 分钟前。

这意味着对两个属性使用不等式过滤器,我不能这样做。

最有效的方法是什么?

我可以在最后 X 分钟内激活所有用户实体,然后随机选择一些。我可以保留我的代码,并在将它们添加到非重复实体列表之前检查最后一个活动,但这可能涉及对数据存储的大量调用。

是否有其他方法可以仅使用查询来做到这一点?

【问题讨论】:

  • X 在你的随机分钟内有多固定,你感兴趣的时间是几分钟,10 分钟 50 分钟?
  • @TimHoffman 分钟值不会是随机的,它将是一个介于 10 到 30 之间的常数值
  • 一旦你有一个最近活跃的 n 个用户列表,你可以从列表中随机挑选一些显示在内存中。在数据库中存储随机值......我们只是说它可能不是所有想法中最好的。随机值的索引和按这些排序只是浪费钱恕我直言。
  • 虽然一个键只查询最近有活动的用户,比如最近 50 个,然后随机选择一个键然后得到那个单一的实体不会太贵。此外,您可以将密钥缓存在 memcache 中一小段时间,然后重复使用以随机选择,直到缓存过期。
  • @Simon 请将链接添加到您所指的答案中,以便我可以责骂某人 ;-)。蒂姆的最后评论可能是最好的解决方案。另请注意,数据库中的随机值不会给您预期的随机结果。用户总是以相同的顺序排序,所以如果有一个用户的随机值非常低,他总是会主导你最近的活动(或其他)列表。蒂姆:我可以建议你根据你的评论来回答吗?

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


【解决方案1】:

鉴于此处要求的上述 cmets 是一种方法。

假设您有一个存储日期时间戳的“最后一个活动”属性,那么您可以执行一个键查询,其中最后一个活动的 datetime_stamp >“感兴趣的日期时间戳”。

在检索密钥时,对结果集执行随机选择,然后使用 get 操作显式获取密钥。这会将成本限制在小型操作和获取上。

然后我会考虑将这组密钥缓存在 memcache 中,并具有定义的到期期限,因此如果您在下一个指定期限内需要另一个随机选择而不是重新查询,则可以重复使用这组密钥,而不是重新查询,2 秒之后。考虑到随机选择,准确性似乎并不重要。

如果确实采用缓存策略,则必须处理缓存到期和刷新缓存。

这里的一个潜在问题是遇到狗堆效应,即多个请求都无法同时检索缓存,并且每个处理程序都开始构建缓存。在负载较轻的系统中,这可能不是问题,在具有大量活动的负载较重的系统中,您可能希望通过任务保持缓存处于活动状态。 - 只是想一想。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-30
    • 2014-08-06
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多