6

我正在尝试使用 Google App Engine Java,但是缺少唯一约束使事情变得困难。我已经阅读了这篇文章,并且这篇博客提出了一种实现类似方法的方法。我的背景是 MySQL。在没有唯一约束的情况下迁移到数据存储区让我感到不安,因为我以前从来不用担心重复值,并且在插入新值之前检查每个值仍然有出错的余地。

“不,您仍然不能在模式创建期间指定唯一性。”

-- David Underhill谈论 GAE 和唯一约束(发布链接

你们用什么来实现类似于唯一键或主键的东西?

我听说过一个使用低级 API 创建的抽象数据存储层,它的工作原理类似于常规 RDB,但它不是免费的(但我不记得软件的名称)

我的问题的示意图

sNo = biggest serial_number in the db
sNo++
Insert new entry with sNo as serial_number value //checkpoint
User adds data pertaining to current serial_number 
Update entry with data where serial_number is sNo 

但是在第 3 行(检查点),我觉得两个用户可能会添加相同的 sNo。这就是阻止我使用 appengine 的原因。

4

4 回答 4

12

在谈论从传统 RDB 转换到 App Engine 之类的 BigTable 类数据存储时,经常会出现这个问题和其他类似问题。

讨论为什么数据存储不支持唯一键通常很有用,因为它告诉您在考虑数据存储方案时应该采用的心态。唯一约束不可用的原因是它极大地限制了可伸缩性。正如您所说,强制执行约束意味着检查该属性的所有其他实体。无论您是在代码中手动执行此操作,还是数据存储在后台自动执行此操作,它仍然需要发生,这意味着性能较低。可以进行一些优化,但仍需要以一种或另一种方式进行。

你的问题的答案是,真正想想为什么你需要那个独特的约束。

其次,请记住键确实存在于数据存储中,并且是实施简单唯一约束的好方法。

my_user = MyUser(key_name=users.get_current_user().email())
my_user.put()

这将保证不会MyUser再使用该电子邮件创建任何内容,并且您还可以MyUser使用该电子邮件快速检索:

my_user = MyUser.get(users.get_current_user().email())

在 python 运行时,您还可以执行以下操作:

my_user = MyUser.get_or_create(key_name=users.get_current_user().email())

这将使用该电子邮件插入或检索用户。

但是,任何比这更复杂的东西都无法扩展。因此,请认真考虑您是否需要该属性是全局唯一的,或者是否有方法可以消除对该唯一约束的需求。通常,您会发现一些小的变通方法,您根本不需要该属性是唯一的。

于 2010-10-04T13:39:56.263 回答
4

您可以为您的产品生成唯一序列号,而无需强制执行唯一 ID 或查询整个实体集以找出当前最大的序列号。您可以使用事务和单例实体来生成“下一个”序列号。由于该操作发生在交易中,因此您可以确定不会有两种产品获得相同的序列号。

但是,这种方法将成为潜在的性能瓶颈并限制应用程序的可扩展性。如果新序列号的创建不经常发生而导致争用,那么它可能对您有用。

编辑:为了澄清,持有要分配的当前或下一个序列号的单例完全独立于任何实际分配有序列号的实体。它们不必都是实体组的一部分。您可以拥有来自多个模型的实体,使用相同的机制来获取一个新的、唯一的序列号。

我对 Java 的记忆不够好,无法提供示例代码,我的 Python 示例可能对您毫无意义,但这里有一个伪代码来说明这个想法:

  1. 接收创建新库存项目的请求。
  2. 进入交易。
  3. 检索 SerialNumber 模型的单个实体的当前值。
  4. 增加值并将其写入数据库
  5. 退出交易时返回值。

现在,执行实际创建库存项目并将其与新序列号一起存储的所有工作的代码不需要在事务中运行。

警告:如上所述,这可能是一个主要的性能瓶颈,因为任何时候只能创建一个序列号。但是,它确实为您提供了您刚刚生成的序列号是唯一的且未在使用中的确定性。

于 2010-10-04T14:55:45.133 回答
3

我在用户需要保留时间段的应用程序中遇到了同样的问题。我需要准确地“插入”一个唯一的时隙实体,同时期望用户同时请求同一个时隙。

我已经隔离了一个如何在应用引擎上执行此操作的示例,并且我在博客中对此进行了介绍。博客帖子包含使用 Datastore 和 Objectify 的规范代码示例。(顺便说一句,我建议避免使用 JDO。)

我还部署了一个现场演示,您可以在其中推动两个用户保留相同的资源。在此演示中,您可以体验应用引擎数据存储区单击的确切行为。

如果您正在寻找唯一约束的行为,这些应该证明是有用的。

-布洛克

于 2011-06-19T21:48:24.507 回答
0

我首先想到了 broc 博客中事务技术的替代方法,可能是创建一个包含同步方法(比如 addUserName(String name))的单例类,仅当它是唯一的或抛出异常时才负责添加新条目。然后创建一个上下文监听器,它实例化这个单例的单个实例,将它作为一个属性添加到 servletContext。然后,Servlet 可以在它们通过 getServletContext 获得的单例实例上调用 addUserName() 方法。

然而,这不是一个好主意,因为 GAE 可能会将应用程序拆分到多个 JVM 上,因此仍然可能出现多个单例类实例,每个 JVM 中都有一个。看到这个线程

一个更像 GAE 的替代方案是编写一个 GAE 模块,负责检查唯一性并添加新条目;然后使用手动或基本缩放...

<max-instances>1</max-instances>

然后,您将在 GAE 上运行一个实例,该实例充当单一权限点,一次将一个用户添加到数据存储区。如果您担心这个实例是一个瓶颈,您可以改进模块,添加队列或内部主/从架构。

这种基于模块的解决方案将允许在短时间内将许多唯一用户名添加到数据存储中,而不会出现实体组争用问题。

于 2016-08-23T16:38:32.493 回答