如何确保唯一ID在Rails中确实是唯一的?

时间:2012-09-24 15:10:33

标签: ruby-on-rails model uuid

我想将对象的网址从/object/123替换为/object/f8a3b2,因此我不会使用id列来生成uid创建对象时手动创建列。

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

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

4 个答案:

答案 0 :(得分: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。

答案 1 :(得分:1)

关于uuid gem

如果您正在使用uuid gem,则无需担心其独特性,因为这已经为您解决了。但是,uuid将生成长128位的唯一ID。所以,如果你想要更短的东西,你可能不想使用它。

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

如果您想要,也就是说,8位唯一代码也是随机的,您可以在我的皮质项目under the new_unique_code method中看到这样一个系统的实现。

这样做会生成一个8位字母数字代码来表示应用程序中的资源。生成给定代码后,我尝试执行.find_by_code,如果返回nil,我知道它已经在使用 NOT

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

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

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

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

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

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

  1. 手动设置每个应用服务器中的配置设置。这是手动的(因此很脆弱),但是如果你有一定数量的app服务器不会改变,这可能就好了。
  2. 让应用服务器互相交谈并协商他们的随机数范围。
  3. 放弃,意识到这是a complex problem that has been solved by very smart people,你不需要重新创建轮子,只需使用UUID。
  4. 考虑使用作为网络服务运行的集中式唯一ID生成器,例如Twitter Snowflake

答案 2 :(得分:0)

使用Rails Validations验证唯一性:

class MyModel < ActiveRecord::Base
  validates :id, uniqueness: true
end

答案 3 :(得分:0)

你应该使用

validates :uid, :uniqueness => true

我必须在此建议中添加警告,因为仍有一种情况会导致数据库因重复的uid列值而出错。您可以在Michael Hartl's RoR tutorial

中找到有关它的更多信息

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

model = Model.create(:uid => generate_uid)
if model.errors[:uid].any?
  model.create(:uid => generate_uid)
end