0

我正在开发一个 AppEngine 项目,并且我在 AppEngine 数据存储之上使用 JDO 来实现持久性。我有一个实体,它使用编码字符串作为键,还使用应用程序生成的键名(也是字符串)。我这样做是因为我的应用程序会经常从野外收集数据(可能会收集相同的东西)并尝试持久化它们。为了避免持久化几个本质上包含相同数据的实体,我决定对这些数据的一些属性进行哈希处理,以获得一致的键名(由于实体关系而不直接操作键)。现在的问题是,每当我计算我的哈希(键名)并尝试存储实体时,如果它已经存在于数据存储中,数据存储(或 JDO 或任何罪魁祸首)默默地覆盖数据存储中实体的属性,而不会引发任何异常。这会对应用程序产生严重影响,因为它会覆盖实体(我们用于排序)的时间戳(一个字段)。我怎样才能最好地解决这个问题?

4

3 回答 3

2

你需要做get-before-set(检查和设置或CAS)。

CAS 是并发的基本租户,它是并行计算的必要弊端。

无论如何,获取比套装便宜得多,因此它实际上可以为您省钱。

与其盲目写入数据存储区,不如先检索;如果实体不存在,则捕获异常并放置实体。如果确实存在,请在保存之前进行深入比较。如果没有任何改变,请不要坚持(并节省成本)。如果它发生了变化,请随意选择合并策略。维护过时修订的一种(略显丑陋)方法是将先前的实体作为字段存储在更新的实体中(可能不适用于许多修订)。

但是,在这种情况下,您必须在设置之前获得。如果您不希望有很多重复项并且想要真正的 chintzy,您可以先执行 exists 查询...这是对您要使用的键执行仅键计数查询(成本比完整获取低 7 倍)。if (count() == 0) then put() else getAndMaybePut() fi

计数查询语法可能看起来很慢,但从我的基准测试来看,它是判断实体是否存在的最快(也是最便宜)的方法:

public boolean exists(Key key){
    Query q;
    if (key.getParent() == null)
      q = new Query(key.getKind());
    else
      q = new Query(key.getKind(), key.getParent());
    q.setKeysOnly();
    q.setFilter(new FilterPredicate(
      Entity.KEY_RESERVED_PROPERTY, FilterOperator.EQUAL, key));
    return 1 == DatastoreServiceFactory.getDatastoreService().prepare(q)
      .countEntities(FetchOptions.Builder.withLimit(1));
}
于 2012-12-30T12:07:05.200 回答
1

在 put() 新实体之前,您必须执行 get() 以查看是否存在具有相同键的实体。没有办法做到这一点。

您可以使用 memcache 和本地“内存中”缓存来加快 get() 操作。如果您可能多次阅读相同的信息,这可能会有所帮助。如果没有,memcache 查询实际上可能会减慢您的进程。

为确保两个请求不会相互覆盖,您应该使用事务(不可能使用 Ajax 建议的查询,除非您将所有项目放在一个实体组中,这可能会将您的更新限制为每秒 1 次)

在伪代码中:

  1. 从散列数据创建密钥
  2. 检查内存缓存中的键(使用 ConcurrentHashSet 键),如果找到则返回
  3. 检查 MemcacheService 的 key,如果找到则返回
  4. 开始交易
  5. 从数据存储中获取实体,如果找到则返回
  6. 在数据存储中创建实体
  7. 提交事务,如果并发更新失败则返回
  8. 将密钥放入缓存(内存和内存缓存)

如果另一个请求(线程)已经同时写入了相同的密钥,则第 7 步将失败。

于 2013-01-03T09:42:30.520 回答
0

我建议您不要将 ID 保存为字符串,而是为您的实体使用 Long ID,或者您可以使用由 appengine 自动生成的 Key 数据类型。

   @PersistenceCapable
   public class Test{
     @PrimaryKey
     @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
     private Long ID;

     // getter and setter 
   }

这将每次都为您返回一个独特的价值。

于 2012-12-30T18:27:47.757 回答