ruby-on-rails - 使用 Rails 5,为每条新记录生成和使用 Basecamp 风格的“hash_id”(而不是顺序行 id)的最有效方法是什么?
问题描述
我想要的是 URL 与Basecamp的非常相似:
https://3.basecamp.com/4000818/buckets/7452203/message_boards/1039416768
我已经按照本指南实现了这个功能,但是我对需要运行可能数百万个 .exists 的过程不满意?查找开放号码并担心这会很快影响我的应用程序的性能。
def set_hash_id
hash_id = nil
loop do
hash_id = SecureRandom.urlsafe_base64(9).gsub(/-|_/,('a'..'z').to_a[rand(26)])
break unless self.class.name.constantize.where(:hash_id => hash_id).exists?
end
self.hash_id = hash_id
end
我发现很难相信 Basecamp 在每次记录保存时都依赖于如此低效的东西,我正在寻找他们是如何做到的,或者找到一个看起来相同但没有链接教程开销的设置。
对于生成非顺序记录 ID 的方法,我将不胜感激。我对 UUID 不感兴趣,因为我无法忍受它们生成的不讨人喜欢的 URL。此外,它们必须是整数。基本上,与 Basecamp URL 完全一样,但没有存在的开销?检查。他们是否有可能将数字与编码时间戳或其他东西组合在一起以确保没有冲突?我已经探索了hashids.org方法,但这不会生成仅整数哈希。
我使用 Postgres 作为我的数据库,以防万一这有帮助。
解决方案
效率方面我认为你应该没问题。GitLab 也使用类似的东西来生成独特的令牌。
还有另一个值得考虑的问题:
您的方法不能保证生成唯一密钥,因为该操作不是原子的(GitLab 也不是)。在检查唯一性和将记录写入数据库之间,可能已经生成了相同的密钥。
您至少有 2 个选项来处理此问题。两种解决方案也应该更有效(这是您主要关心的问题)。
在保存时捕获数据库的唯一键约束违规
def save
begin
self.hash_id = generate_hash_id
super
rescue ActiveRecord::RecordNotUnique => e
# 1. you may need to check the message for the specific constraint
# 2. you may need to implement an abort condition to prevent infinite retries
retry
end
end
您也可以在 ActiveRecord 回调中执行此操作。
让数据库生成密钥
另一种解决方案是让您的数据库在创建时生成唯一键。像这篇博文A Better ID Generator For PostgreSQL中描述的函数可能会更好地满足您的目的。
此解决方案的优点是您的应用程序代码无需担心生成或捕获冲突。缺点是这个解决方案是特定于数据库的。
推荐阅读
- java - 拍照并获取位图和 Uri - android
- c# - 首次请求时自动登录
- java - ElasticSearch Java API 多重过滤器无法正常工作
- sql - Sqlite 从复合主键中选择上一行/下一行
- java - Spring Boot JPA:@Modifying @Query 没有效果
- php - PHP array_search通过变量在多维数组中
- mysql - SpringBoot 使用 Hibernate 与 Mysql 集成
- android - Smack 中的聊天标记 (XEP-0333)
- django - Django:如何测试无效的对象创建
- javascript - 检查嵌套括号是否平衡的 JavaScript 正则表达式