在GAE中实施唯一约束

时间:2010-10-04 13:09:00

标签: google-app-engine unique datastore

我正在尝试使用Google App Engine Java,但是缺少一个独特的约束会让事情变得困难。 我一直through this postthis blog建议实现类似的方法。我的背景是在MySQL.Moving到没有唯一约束的数据存储区让我感到紧张,因为我从来不必担心重复值,并在插入新值之前检查每个值仍有错误的余地。

  

“不,你仍然不能指定唯一的   在架构创建期间。“

     

- David Underhill讨论了GAE和唯一约束(post link

你们有什么用来实现类似于独特或主要键的东西?

我听说过使用低级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 个答案:

答案 0 :(得分:12)

在讨论从传统RDB过渡到像App Engine这样的类似BigTable的数据存储时,经常出现这个问题和其他类似的问题。

讨论为什么数据存储区不支持唯一键通常很有用,因为它会在考虑数据存储方案时通知您应该处于的思维模式。独特约束不可用的原因是它极大地限制了可伸缩性。就像你说的那样,强制执行约束意味着检查该属性的所有其他实体。无论您是在代码中手动执行还是数据存储在幕后自动执行,它仍然需要发生,这意味着性能降低。可以进行一些优化,但仍需要以某种方式发生。

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

其次,请记住数据存储区中存在 do 键,这是强制执行简单唯一约束的好方法。

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())

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

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

答案 1 :(得分:4)

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

然而,这种方法将成为潜在的性能瓶颈,并限制应用程序的可伸缩性。如果新序列号的创建不经常发生以至于您得到争用,那么它可能对您有用。

编辑: 为了澄清,保存当前 - 或下一个 - 要分配的序列号的单例完全独立于实际具有分配给它们的序列号的任何实体。它们不需要都是实体组的一部分。您可以使用相同的机制从多个模型中获取实体,以获得新的唯一序列号。

我不太清楚Java是否足以提供示例代码,而我的Python示例对您来说可能毫无意义,但这里有伪代码来说明这个想法:

  1. 接收创建新库存项目的请求。
  2. 输入交易。
  3. 检索SerialNumber模型的单个实体的当前值。
  4. 增值并将其写入数据库
  5. 退出交易时返回值。
  6. 现在,执行所有实际创建库存项目并将其与新序列号一起存储的所有工作的代码都不需要在事务中运行。

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

答案 2 :(得分:3)

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

我已经在app引擎上分离了一个如何执行此操作的示例,我blogged about it。博客文章包含使用数据存储区的规范代码示例,以及Objectify。 (顺便说一句,我建议避免使用JDO。)

我还部署了一个live demonstration,您可以将两个用户提前保留相同的资源。在本演示中,您可以通过单击来体验应用引擎数据存储的确切行为。

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

-broc

答案 3 :(得分:0)

我首先想到broc博客中交易技术的替代方法,可能是创建一个包含同步方法的单例类(比如addUserName(String name)),只有在它是唯一的时才负责添加新条目或抛出异常。然后创建一个contextlistener,它实例化该单例的单个实例,并将其作为属性添加到servletContext中。然后,Servlet可以在通过getServletContext获取的单例实例上调用addUserName()方法。

然而,这不是一个好主意,因为GAE很可能将应用程序拆分为多个JVM,因此仍然可以发生多个单例类实例,每个JVM中有一个。 see this thread

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

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

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

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