这是完全可行的,在许多情况下它是一个不错的设计选择。在分片、多主写入或需要移动到任何多数据库系统时,创建全局唯一 ID 会使事情变得更加容易。当然,缺点是它占用的空间是 bigint 的两倍,而且比自动增量更复杂。
首先,切勿将 UUID 存储为 VARCHAR。好的,现在不碍事了,下面是一些代码:
这个 gem 使用 Mysql 8.0+ 的 UUID_TO_BIN 和 BIN_TO_UUID 来存储二进制和检索 UUID 字符。 https://github.com/nedap/mysql-binuuid-rails
gem 'mysql-binuuid-rails'
在模型中,使用 Rails 5+ 中的 Attributes API。请务必记住设置所有外键来解码 UUID,即attribute :user_id, MySQLBinUUID::Type.new
class Account < ApplicationRecord
attribute :id, MySQLBinUUID::Type.new
before_create { self.id = ApplicationRecord.generate_uuid }
end
本次迁移指定不创建默认主键,然后手动指定主键。触发器很好,因为如果您在没有 ActiveRecord 的情况下运行任何创建记录的查询,它仍然会正确生成和填充主键。
class CreateAccounts < ActiveRecord::Migration[6.0]
def change
create_table :accounts, id: false do |t|
t.binary :id, limit: 16, primary_key: true
t.string :email
t.timestamps
end
reversible do |dir|
dir.up do
execute <<~SQL
CREATE TRIGGER before_insert_set_account_uuid
BEFORE INSERT ON accounts
FOR EACH ROW
BEGIN
IF new.id IS NULL THEN
SET new.id = UUID_TO_BIN(UUID(), 1);
END IF;
END
SQL
execute "UPDATE accounts SET id = UUID_TO_BIN(UUID());"
end
dir.down do
execute <<~SQL
DROP TRIGGER before_insert_set_account_uuid;
SQL
end
end
end
end
这是使用 MySQL 的方法来创建 UUID。请注意,这意味着两次往返数据库,一次获取新的 UUID,一次保存记录。最好利用 UUIDTools 等在本地生成它。
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def self.generate_uuid
ActiveRecord::Base.connection.execute("select UUID();").first[0]
end
end