1

我想将对象的 url 替换为/object/123to ,因此我在创建对象时手动生成一个列/object/f8a3b2,而不是使用该列。iduid

我可以使用可用的 uuid 库来生成 uid,但是如何确保生成的值始终是唯一的?

我在 uid 列上设置了唯一索引,所以如果它不是唯一的,它应该会抛出一个错误 - 我可以在模型级别捕获它,创建另一个 uid,然后再试一次吗?

4

4 回答 4

4

可能有更好的方法来做到这一点,但我想到的第一件事就是validates_uniqueness_of在您的活动记录模型上使用该方法,正如您所建议的那样。

在您的模型中:

class MyUniqueModel < ActiveRecord::Base
    validates_uniqueness_of :uuid_column
end

在您的控制器中

@uniqueObject = MyUniqueModel.new(modelParams)
@uniqueObject.uuid_column = generate_uuid() # whatever method you use for generating your uuid
while !uniqueObject.save # if at first it didn't save...
    uniqueObject.uuid_column = generate_uuid() # try, try again
end # should break out of the loop if the save succeeded.

如果您对模型进行了其他验证,那么您将在我的建议中遇到的问题是确定保存失败是由您的明显非唯一 uuid 还是其他一些验证错误引起的。这意味着您必须通过失败的列过滤循环体,以确定是否应该费心重新生成另一个 UUID。

于 2012-09-24T15:23:40.980 回答
1

关于 uuid gem

如果您使用的是uuid gem,则不必担心唯一性,因为这已经为您处理好了。但是,uuid 将生成长的 128 位唯一 ID。所以,如果你想要更短的东西,你可能不想使用它。

一种更简单的方法,如果您的需求很简单

例如,如果你想要随机的 8 位唯一代码,你可以在我的 cortex 项目中的new_unique_codemethod下看到这样一个系统的实现。

它的作用是生成一个 8 位字母数字代码来表示应用程序中的资源。生成给定代码后,我尝试执行 a .find_by_code,如果返回,nil我知道它尚未使用。

在我的特定情况下,8 位字母数字代码为我提供了一个包含数百万个可能代码的键空间(我忘记了有多少)。根据我的应用程序的用例,代码冲突的可能性(已使用代码与可用代码的百分比)足够小,可能需要为给定资源多次生成新代码。

这样做的结果是,需要为给定资源多次生成新代码的情况极为罕见,因此几乎不会占用额外的时间。

应用程序中的多个应用程序服务器/线程怎么样?

我的解决方案的一个限制是,如果您有多个应用程序服务器,但只有一个数据库服务器,则可能有两个独立的应用程序服务器在它们中的任何一个在集中式数据库中创建新资源记录之前生成相同的代码。这会产生一种可能发生代码冲突的情况,并且除了保存到数据库的第一条记录之外的所有记录都将被标记为无效(由于代码列的唯一性约束)。此特定应用程序仅在单个应用程序服务器实例上运行(并且可能只会运行)。我知道这是一个已知的权衡,所以我可以冒险。

但是,如果我想解决这个问题,我会限制每个应用服务器的随机代码范围,这样它们就不会意外重叠。例如,假设我正在生成随机代码,这些代码以 00000000-99999999 之间的 8 位数字代码形式出现。例如,如果我有三个运行此应用程序的应用程序服务器,我会简单地将服务器#1 限制为在 00000000-33333333 之间生成代码,服务器#2 到 33333334-66666666,服务器#3 到 66666667-99999999 之间。这样可以保证它们永远不会重叠代码,并且您仍然可以整天生成唯一的随机密钥。

如何将给定服务器限制在给定范围内有点离题,但一些可用选项是:

  1. 在每个应用服务器中手动设置配置设置。这是手动的(因此很脆弱),但如果您有数量稳定且不会更改的应用服务器,这可能没问题。
  2. 让应用服务器相互通信并协商它们的随机数范围应该是什么。
  3. 放弃吧,意识到这是一个非常聪明的人已经解决的复杂问题,您不需要重新创建轮子,只需使用 UUID。
  4. 考虑使用作为网络服务运行的集中式唯一 ID 生成器,例如Twitter Snowflake
于 2012-09-24T15:29:12.497 回答
0

使用Rails Validations验证唯一性:

class MyModel < ActiveRecord::Base
  validates :id, uniqueness: true
end
于 2012-09-24T15:23:53.337 回答
0

你应该使用

validates :uid, :uniqueness => true

我必须在此建议中添加一个警告,因为仍然存在一种情况,您会从数据库中收到重复 uid 列值的错误。您可以在Michael Hartl 的 RoR 教程中找到更多相关信息

为了在重复的 uid 上捕获并重试保存,您可以执行以下操作:

model = Model.create(:uid => generate_uid)
if model.errors[:uid].any?
  model.create(:uid => generate_uid)
end
于 2012-09-24T15:23:59.503 回答