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

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

问题

想在普通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

谢谢各位:)

共收到 25 条回复

第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, 不需要和数据库通信生成速度自然快很多

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

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