新手问题 [已解决] 怎么生成 UUID?用 Gem?用 Rails 自带的?用 Ruby 自带的?

chairy11 · 2015年08月26日 · 最后由 luikore 回复于 2015年08月27日 · 5528 次阅读

问题

想在普通 id 之外,再加一个 uuid 的字段。 刚 google 了一下,好像有三种方法,

  1. 用 gem:uuidtoolsuuid
  2. Ruby原生的方法,有帖子说这样写 ruby require 'securerandom' SecureRandom.uuid 或者再检测一下是否重复,
@seller.user_info_token = loop do
    token = SecureRandom.urlsafe_base64
    break token unless User.exists?(user_info_token: token)
  end
  @seller.user_info_token = SecureRandom.uuid

3.Rails 自带,API 文档,有 uuid_from_hash, uuid_v3, uuid_v4, uuid_v5。 网上关于 RAILS 自带的教程,说的都是用 postgres, 而且好像都是用 uuid 做主键,但我用 mysql 了,还是用普通 id 做主键,也一样?

恩,还没明白我这种场景应该是用什么样的方案? 你们都怎么做的?

解决方案

还是选择了用 gem:uuidtools

具体实现,在 model 里

before_create do
    self.some_other_id = UUIDTools::UUID.timestamp_create().to_s
  end

谢谢各位:)

第 1 种方法挺好的,我们项目就在用

大多时候用 2,你可以尝试 uuid v5

搭车问下 https://github.com/dylang/shortid 这工具有没有 Ruby 版本的?

用数据库生成

当你非常强调 unique 但是又没有很好的 Unique IDentifier 的时候, 那么 uuid gem 是个不错的选择。 如果我没记错它会用到你的 MAC 地址 和 其它内容 一起作为 Unique IDentifier。所以你基本可以认为在不同的机器任何时刻一定 会生成不同的 uuid!

#5 楼 @poshboytl 额,我发现一个叫:uuidtools的 gem,是 uuid的下载量的 5 倍……我研究下……

SecureRandom 足够用 没必要非得用个下载量大的 gem 吧 原生足矣

#6 楼 @chairy11 关键就考察两点, 一个是它怎么实现的 Unique IDentifier, 一个是生成的性能。 偶在开会, 所以你就自己研究下咯!

#7 楼 @kewin #8 楼 @poshboytl

下载量难道不是检验一个 gem 好不好的最直接标准么?

一般如果有 gem 我就会选择用 gem,因为我总觉得我水平很菜,图样图森破,也许很多坑我并没有注意到,高手写得肯定比我好……

比如我刚才看到另一个代码,它会检测一下是否有重复的,我就想,哎呀,我可没有想到还有重复的可能,真够谨慎的……

@seller.user_info_token = loop do
    token = SecureRandom.urlsafe_base64
    break token unless User.exists?(user_info_token: token)
  end
  @seller.user_info_token = SecureRandom.uuid

#9 楼 @chairy11 其实这个 check 看起来很好, 但是也是不严谨的。(但 99.9% 都够用了) 虽然可以能性很小, 但是你 “查询”,“生成并写入 “ 并不是原子操作,也就是说,即是你查了发现数据库没有, 然后你做生成,在写入的时候,也许数据库在那个时间段被插入了同样的值。

即是要这个策略, 也应该做数据库级别的唯一性约束, 然后抛出异常,在异常里做重试的逻辑。

但是你有可能用的是某个 nosql,并不支持数据库级别的唯一性约束, 这个做法也很难。

所以保证生成的时候就是唯一的, 当然是更好的选择。无需做额外的 check.

#9 楼 @chairy11 粉丝多能不能说明一个歌手唱歌唱得好?

Yes or No. 😄

#4 楼 @nouse @chairy11 我也推荐用数据库生成,唯一性和自增特性,加上一些额外的规则可以保证。其他方式,并发的情况下都比较难保证。

@seller.user_info_token = loop do
    token = SecureRandom.urlsafe_base64
    break token unless User.exists?(user_info_token: token)
  end
  @seller.user_info_token = SecureRandom.uuid

并发情况下,两个 thread 同时生产相同的 user_info_token,是都会保存成功的。

@nouse @rubyu2
楼主说的是它用的 mysql.

还有 @chairy11 我再给你解释一下 uuid 的应用场景吧, 这里只举一个栗子 只针区别你说的那个 普通 id 的情况

假设你有 Document, Photo, Text 三个 model, 他们分别有 id 和 uuid

你对外想做一个抽象, 当用户请求

“objects/识别码” 返回相应对象(可能是 Document, Photo 或 Text)。

你用 id 时没办法做的, 因为他有可能重。

而这种抽象用 uuid 做将是极好的。

我只是举了很多例子中的一种应用场景。 只是想说明 uuid 和你数据库的 自增 id 并不完全是一样的,但有些场景还是可以混用。

为什么要再整个 UUID 呢?是要做全局唯一 ID 吗?如果是这样,可以考虑用这个搞:

Flickr 是如何生成唯一 ID 的 (译)

#13 楼 @poshboytl 哦,谢谢,好酷的场景:)

解决了,谢谢各位:)

恩,还没明白我这种场景应该是用什么样的方案?

比如 客户端 可以方便的 cache 服务器端返回的带有 UUID 的 json - 直接按 uuid 扔到比如 Redis 里面就行了

#17 楼 @knwang 哦,谢谢:)

@poshboytl @chairy11 这种用算法生成唯一序列,在极端情况下也是有碰撞概率的,不过一般情况下都是够用的。我说的就是 @diguage 这种方式,用数据库的自增性和唯一性再加一些自定义规则来保证全局唯一性。

#19 楼 @rubyu2 UUID 要撞到实在太难了。

根据维基百科的资料,每秒产生 10 亿笔 UUID,100 年后只产生一次重复的概率是 50%。

#20 楼 @msg7086 嗯,如果这样应该还是一个很不错的选择。 以前公司用过也是一个第三方的 gem 还有就是同事自己写的一些唯一序列号生成规则,出了好多问题,说出来都是泪。

SecureRandom 生成的是 UUID v4, 不是真正的 unique, 利用随机数来保证碰撞概率很小。正如 wiki 所说,碰撞概率很小,可惜有个前提:

However, these probabilities only hold when the UUIDs are generated using sufficient entropy. Otherwise, the probability of duplicates could be significantly higher, since the statistical dispersion might be lower. Where unique identifiers are required for distributed applications, so that UUIDs do not clash even when data from many devices is merged, the randomness of the seeds and generators used on every device must be reliable for the life of the application. Where this is not feasible, RFC4122 recommends using a namespace variant instead.

uuid gem 产生的 uuid 是用 mac+timestamp+local sequence, 代码简单明了。twitter 当年的 snowflake 是同样的思路 https://blog.twitter.com/2010/announcing-snowflake

分布式场景下 uuid 是最方便的选择, sequence 很难,有兴趣可以 google。

数据库 pg/mysql/mongodb 都可自己生成 , pg/mongodb 有专门的 uuid 类型, 占用存储空间少一半

但数据库其实往往用的 uuid-ossp, ruby 也有这个 gem, 不需要和数据库通信生成速度自然快很多

如果要保证全局唯一性, 得自己来制定生成规则

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册