【问题标题】:Can a client ever reliably generate a PK for an object being written to a DB?客户端能否可靠地为写入数据库的对象生成 PK?
【发布时间】:2026-01-31 19:45:02
【问题描述】:

我已经为这个困境烦恼了一段时间,我想我会接近 SO。

我的场景的一些背景:

  • 我的播放列表包含 0 个或多个 PlaylistItem 子项。
  • 很少创建播放列表。因此,我可以从服务器请求 GUID,等待响应,成功后刷新 UI 以显示成功添加的播放列表。
  • 经常创建播放列表项对象。因此,在等待服务器响应 UUID 时,我NOT OK收到一条加载消息。

这最后一个事实是一个优化,我知道,但我认为它极大地提高了程序的可用性。

尽管如此,我还是想讨论一下用于唯一标识我的对象客户端的选项。我将首先强调我尝试过但失败的两个选项,然后是我正在考虑的第三个选项。我希望深入了解其他可能的解决方案。


生成一个 PK UUID 客户端,该客户端将被持久化到服务器。

这是我的第一选择。这是一个显而易见的决定,但也有一些明显的缺点。这里的第一个问题是客户端 UUID 不能也不应该被信任用于这种目的。恶意用户可以轻松地强制进行 PK 冲突。此外,我的理解是,如果我选择在客户端生成 UUID,我应该期待更大的碰撞机会。抓挠。

根据播放列表 GUID 和播放列表中的位置生成复合 PK

我认为这是一个棘手但很好的解决方案。 PlaylistItem 的位置对于给定的播放列表集合是唯一的并且它可以在客户端和服务器端派生。这似乎是一个很好的解决方案。不幸的是,让我的职位成为 PK 的一部分会破坏我的 PK 的不变性。每当一个 PlaylistItem 被重新排序或删除时——需要更新大量的 PlaylistItem 键。抓挠。

根据播放列表 GUID 和自增播放列表项 ID 生成复合 PK

此解决方案与上述解决方案类似,但通过将复合键与位置分开来确保 PK 是不可变的。这是我正在玩弄的当前解决方案。我唯一担心的是,恶意用户可以通过在发送之前修改客户端的自动递增 id 来强制冲突。我认为这种恶意行为不会对系统造成任何损害,但需要考虑。

好的!你有它。我做这一切是不是很愚蠢?我只是把它吸起来并强制我的服务器为我的 PlaylistItem 对象生成 GUID 吗?或者,是否可以编写适当的实现?

更新:

我希望在服务器成功保存到数据库之前直观地表示用户的操作,并在保存失败时实施所需的恢复技术。我不确定这是否愚蠢,但我将通过一个用例场景来解释我的推理:

客户端想要添加一个新的 PlaylistItem。为此,系统会向 YouTube 的 API 发出请求以获取创建 PlaylistItem 所需的所有信息。在 YouTube 的 API 响应后,客户端拥有创建 PlaylistItem 所需的所有必要信息,但唯一标识它的能力除外。

此时,用户已经为 YouTube 的 API 等待了 X 个时间段。现在,我想在客户端上直观地显示 PlaylistItem。如果我选择等待服务器,我现在正在等待 X + Y 时间范围,然后才能看到成功的视觉指示。在测试中,这种延迟感觉很尴尬。

我的服务器只是亚马逊 EC2 上的一个微型实例。我可以通过升级硬件来缩短 Y 时间范围,但我可以通过巧妙的编程完全消除 Y。这就是我面临的困境。

【问题讨论】:

  • 目前还不清楚客户端/服务器通信到底是什么。从服务器取回 UUID(在插入时生成)真的比使用客户端提供的 GUID 进行插入需要更长的时间吗?或者您是否试图在创建 PlaylistItem 时避免任何服务器交互,将插入推迟到以后?
  • @JonSkeet 我已经根据您的问题更新了我的原始帖子,并提供了更多信息。这有助于澄清我的问题吗?
  • 好的,谢谢。现在我只需要考虑一个解决方案。如果您愿意bit 信任客户端,您可以使用高/低算法 - 有效地“保留”一堆 ID。然后,您可以跟踪客户实际提供的内容,并在他们顽皮时提供错误...
  • 这是一个非常天才的方法。随意考虑一下,我不急……不过会考虑一下这个建议。我从来没有考虑过这种可能性!

标签: optimization client-server primary-key guid


【解决方案1】:

好的,当我在评论中提出建议时,您似乎喜欢它:)

您可以使用高/低方法,这基本上允许客户端一次保留一堆密钥。最简单的方法可能是使它成为一个复合主键,由两个整数组成。客户会接到“给我一把大钥匙”这样的电话。您将自动递增“下一个主键”序列,并记录哪个客户端“拥有”该主键。然后,该客户端可以在该主键旁边使用任何次键,并且知道它们将与任何其他客户端隔离。

当客户端执行插入时,您可以检查客户端是否使用了正确的主键,即分配给他们的主键。

当然,另一种方法是只制作主键 { client ID, UUID } 并让客户端指定任何 UUID...

【讨论】: