Rails 在 Rails 中生成全局唯一 ID 的最佳实践是什么?

wfwdex · 2017年03月16日 · 最后由 vincent 回复于 2017年03月16日 · 1171 次阅读

目前在做一个系统,数据库是MySQL,数据是跨机房单向同步的,由节点服务器向主服务器同步数据,需要节点服务器能生成全局唯一ID。

目前在用的是 uuidtools 这个gem生成uuid。

但生成的uuid有点长,而且数据大部分是日志,而且看了这个回复,觉得用uuid不妥,这几天想优化一下,找到另外一个方案:

使用 hashids 这个gem,每个节点服务器不同的salt,然后再根据生成的uuid来生成id。

但hashids依然是生成字符型的,想请教一下有没有生成bigint的方法或者更好的方案?


ps: 由于twitter的snowflake需要另外搭建专用的id生成服务器,而节点服务器之间也是跨机房的,所以这个方案对我不适用。

ps: 关闭讨论按钮总是被误点到。

共收到 11 条回复

postgresql sequence

ruby_sky 回复

谢谢回复,我忘记说我用的是MySQL了。

wfwdex 关闭了讨论 03月16日 12:47
wfwdex 重新开启了讨论 03月16日 12:47
wfwdex 关闭了讨论 03月16日 13:00
wfwdex 重新开启了讨论 03月16日 13:00

http://ruby-doc.org/stdlib-2.1.3/libdoc/securerandom/rdoc/SecureRandom.html#method-c-uuid

标准库里面的 SecureRandom.uuid 不能用么?

如果要搞整形的话,那就得自己维护一个 Sequence 了,参见: mongoid_auto_increment_id

大致是有个 Sequence 表

Field Value
table_name count

你需要在每次创建的时候,用 Atom 的方式修改 Sequence 对应记录,并拿出新的 count

# increment count and return new count
new_id = Sequence.increment(table_name)

这样做会有什么问题?同一种 table_name 在并发插入新数据的时候,会因为都修改了 Sequence 的同一行记录而导致锁表,于是插入会有并发限制。

如何解决,增加一个缓冲器,例如放入内存里面,每次调用 increment 不是 +1,而是加一批,例如 200,并将可用的 200 个数字放内存里面,下次用的时候,如果有可用的,先用内存里面可用的数字,直到用完再弄一批。

huacnlee 回复

谢谢回复,用 SecureRandom.uuid 生成的话,和uuidtools没啥区别吧?如果我为每个节点服务器配置一个数字ID然后加上用SecureRandom.random_number 生成的随机数字,在高并发下,是不是还是会有问题?

引用 @vincent这里的关于mysql使用uuid为主键的回复

没有这样用过,不过从 innodb 存储特性看,这种做法非常不可取,如果数据量很大,可能导致严重的性能问题,主要原因有:

innodb 的非主键索引都将存一个主键,uuid 相比整数 id,索引大小增加很多; uuid 主键比较肯定比 整数慢,另外非主键索引查找最终还要引用一次主键查找; innodb 主键索引和数据存储位置相关(簇类索引),uuid 主键可能会引起数据位置频繁变动,严重影响性能。

使用 snowflake 的方法为什么被排除呢?我觉得用 snowflake 的方法很合适你的情况,不过要做一些调整,每个机房建立自己的 snowflake 服务就可以,不需要跨机房调用。

wfwdex 回复

你在机器上固定数字的办法有天会成为坑,玩意数字搞错了呢?

请参考 mongoid_auto_increment_id 的实现,这个就是用于解决 MongoDB ObjectId 过长的问题,在效率,分布式等各方面都是没有问题的。

huacnlee 回复

好的,多谢,我看看

vincent 回复

谢谢回复,每个机房建一个这样的服务实现起来感觉有些麻烦,成本也不小。

wfwdex 回复

还有种更简单的方法,每个 client 自己生成,其实算法很简单,我的 github 里就有一个

wfwdex 回复

对,就是这个 https://github.com/xiewenwei/smart_shard_key ,我很早之前写的,刚刚在手机上不方便给链接。

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