在 GAE 中实施独特的约束

发布于 2024-09-25 20:35:10 字数 1118 浏览 3 评论 0原文

我正在尝试 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 的原因。

I am trying out Google App Engine Java, however the absence of a unique constraint is making things difficult.
I have been through this post and this blog suggests a method to implement something similar. My background is in MySQL.Moving to datastore without a unique constraint makes me jittery because I never had to worry about duplicate values before and checking each value before inserting a new value still has room for error.

"No, you still cannot specify unique
during schema creation."

-- David Underhill talks about GAE and the unique constraint (post link)

What are you guys using to implement something similar to a unique or primary key?

I heard about a abstract datastore layer created using the low level api which worked like a regular RDB, which however was not free(however I do not remember the name of the software)

Schematic view of my problem

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 

However at line number 3(checkpoint), I feel two users might add the same sNo. And that is what is preventing me from working with appengine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

要走就滚别墨迹 2024-10-02 20:35:10

在谈论从传统 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())

这将插入或检索具有该电子邮件的用户。

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

This and other similar questions come up often when talking about transitioning from a traditional RDB to a BigTable-like datastore like App Engine's.

It's often useful to discuss why the datastore doesn't support unique keys, since it informs the mindset you should be in when thinking about your data storage schemes. The reason unique constraints are not available is because it greatly limits scalability. Like you've said, enforcing the constraint means checking all other entities for that property. Whether you do it manually in your code or the datastore does it automatically behind the scenes, it still needs to happen, and that means lower performance. Some optimizations can be made, but it still needs to happen in one way or another.

The answer to your question is, really think about why you need that unique constraint.

Secondly, remember that keys do exist in the datastore, and are a great way of enforcing a simple unique constraint.

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

This will guarantee that no MyUser will ever be created with that email ever again, and you can also quickly retrieve the MyUser with that email:

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

In the python runtime you can also do:

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

Which will insert or retrieve the user with that email.

Anything more complex than that will not be scalable though. So really think about whether you need that property to be globally unique, or if there are ways you can remove the need for that unique constraint. Often times you'll find with some small workarounds you didn't need that property to be unique after all.

胡大本事 2024-10-02 20:35:10

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

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

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

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

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

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

警告:正如我上面所说,这可能是一个主要的性能瓶颈,因为一次只能创建一个序列号。但是,它确实可以确保您刚刚生成的序列号是唯一的且未被使用。

You can generate unique serial numbers for your products without needing to enforce unique IDs or querying the entire set of entities to find out what the largest serial number currently is. You can use transactions and a singleton entity to generate the 'next' serial number. Because the operation occurs inside a transaction, you can be sure that no two products will ever get the same serial number.

This approach will, however, be a potential performance chokepoint and limit your application's scalability. If it is the case that the creation of new serial numbers does not happen so often that you get contention, it may work for you.

EDIT:
To clarify, the singleton that holds the current -- or next -- serial number that is to be assigned is completely independent of any entities that actually have serial numbers assigned to them. They do not need to be all be a part of an entity group. You could have entities from multiple models using the same mechanism to get a new, unique serial number.

I don't remember Java well enough to provide sample code, and my Python example might be meaningless to you, but here's pseudo-code to illustrate the idea:

  1. Receive request to create a new inventory item.
  2. Enter transaction.
  3. Retrieve current value of the single entity of the SerialNumber model.
  4. Increment value and write it to the database
  5. Return value as you exit transaction.

Now, the code that does all the work of actually creating the inventory item and storing it along with its new serial number DOES NOT need to run in a transaction.

Caveat: as I stated above, this could be a major performance bottleneck, as only one serial number can be created at any one time. However, it does provide you with the certainty that the serial number that you just generated is unique and not in-use.

陪你到最终 2024-10-02 20:35:10

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

我已经隔离了一个如何在应用程序引擎上执行此操作的示例,并且我 关于它的博客。该博客文章包含使用 Datastore 和 Objectify 的规范代码示例。 (顺便说一句,我建议避免使用 JDO。)

我还部署了一个 现场演示,您可以在其中提升两个用户以保留相同的资源。在此演示中,您可以逐次体验 App Engine 数据存储区的确切行为。

如果您正在寻找唯一约束的行为,那么这些应该会很有用。

-布罗克

I encountered this same issue in an application where users needed to reserve a timeslot. I needed to "insert" exactly one unique timeslot entity while expecting users to simultaneously request the same timeslot.

I have isolated an example of how to do this on app engine, and I blogged about it. The blog posting has canonical code examples using Datastore, and also Objectify. (BTW, I would advise to avoid JDO.)

I have also deployed a live demonstration where you can advance two users toward reserving the same resource. In this demo you can experience the exact behavior of app engine datastore click by click.

If you are looking for the behavior of a unique constraint, these should prove useful.

-broc

还如梦归 2024-10-02 20:35:10

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

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

一个更像 GAE 的替代方案是编写一个负责检查的 GAE 模块独特性和添加新条目;然后使用手动或基本扩展...

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

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

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

I first thought an alternative to the transaction technique in broc's blog, could be to make a singleton class which contains a synchronized method (say addUserName(String name)) responsible adding a new entry only if it is unique or throwing an exception. Then make a contextlistener which instantiates a single instance of this singleton, adding it as an attribute to the servletContext. Servlets then can call the addUserName() method on the singleton instance which they obtain through getServletContext.

However this is NOT a good idea because GAE is likely to split the app across multiple JVMs so multiple singleton class instances could still occur, one in each JVM. see this thread

A more GAE like alternative would be to write a GAE module responsible for checking uniqueness and adding new enteries; then use manual or basic scaling with...

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

Then you have a single instance running on GAE which acts as a single point of authority, adding users one at a time to the datastore. If you are concerned about this instance being a bottleneck you could improve the module, adding queuing or an internal master/slave architecture.

This module based solution would allow many unique usernames to be added to the datastore in a short space of time, without risking entitygroup contention issues.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文