Ruby on rails Rails what';s唯一索引中的差异并验证的唯一性

Ruby on rails Rails what';s唯一索引中的差异并验证的唯一性,ruby-on-rails,postgresql,indexing,Ruby On Rails,Postgresql,Indexing,首先,有人能解释一下唯一索引在数据库中是如何工作的吗 假设我有一个带有名称列的用户模型,我在其上添加了一个唯一索引,但在模型(User.rb)中,名称字段上有一个存在验证器 所以现在当我试图创建两个同名用户时,我得到了一个错误 重复的键值违反唯一约束“索引用户名称” 因此,在我看来,它就像唯一性索引与唯一性验证器一样工作(?) 如果是,那么外键呢 假设我有Post模型,其中属于:user与用户有多个:Post。 以及posts表中具有唯一索引的外键用户id。那么多篇文章不能有相同的用户id 有人

首先,有人能解释一下唯一索引在数据库中是如何工作的吗

假设我有一个带有
名称列
的用户模型,我在其上添加了一个
唯一索引
,但在模型(User.rb)中,
名称
字段上有一个
存在
验证器

所以现在当我试图创建两个同名用户时,我得到了一个错误

重复的键值违反唯一约束“索引用户名称”

因此,在我看来,它就像
唯一性索引
唯一性验证器一样工作(?)

如果是,那么外键呢

假设我有
Post
模型,其中
属于:user
用户有多个:Post
。 以及
posts表中具有唯一索引的外键
用户id
。那么多篇文章不能有相同的
用户id

有人能解释一下
唯一索引的工作原理吗


我使用Ruby 2.0.0运行Rails 4

以下是唯一索引和验证的唯一性之间的区别

这是一个补丁,使ActiveRecord能够识别数据库生成的唯一违反约束的错误。例如,它在不声明validates_university_的情况下执行以下操作:

create_table "users" do |t|
  t.string   "email",   null: false
end
add_index "users", ["email"], unique: true

class User < ActiveRecord::Base
end

User.create!(email: 'abc@abc.com')
u = User.create(email: 'abc@abc.com')
u.errors[:email]
=> "has already been taken"
create_表“users”do|t|
t、 字符串“email”,null:false
结束
添加索引“用户”[“电子邮件”],唯一:true
类用户“已采取行动”
好处是速度快、易用性和完整性--

速度

使用这种方法,您不需要在保存时执行db查找来检查唯一性(当缺少索引时,这有时会非常缓慢--)。如果您真的关心验证唯一性,那么无论如何都必须使用数据库约束,这样无论发生什么情况,数据库都将验证唯一性,并且这种方法将删除一个额外的查询。检查索引两次对DB来说不是问题(它第二次被缓存),但是从应用程序中节省DB往返是一个巨大的胜利

易用性

考虑到无论如何都必须有db约束才能实现真正的唯一性,这种方法将让一切在db约束就位后自动发生。如果愿意,您仍然可以使用的validates\u university\u

完整性

validates_university_of一直有点麻烦——它无法正确处理竞争条件,并导致必须使用冗余的错误处理逻辑来处理的异常。(请参阅中的“并发性和完整性”一节)

验证唯一性的唯一性不足以确保值的唯一性。其原因是,在生产中,多个工作进程可能会导致竞争条件:

  • 两个并发请求尝试创建具有相同名称的用户(和 我们希望用户名是唯一的)

  • 服务器上的请求由两个工作进程接受,这两个工作进程 现在将并行处理它们

  • 这两个请求都扫描users表,并查看名称是否正确 可用

  • 这两个请求都通过了验证,并创建了一个具有 可用名称

  • 如需更清晰的理解,请勾选此项

    如果为列创建唯一索引,则意味着您可以保证表中不会有多个该列具有相同值的行。在模型中仅使用validates\u university\u验证不足以强制唯一性,因为可能会有并发用户尝试创建相同的数据

    假设两个用户试图使用相同的电子邮件注册一个帐户,您在用户模型中添加了validates\u university\u of:email。如果同时点击“注册”按钮,Rails将在用户表中查找该电子邮件,并回复说一切正常,可以将记录保存到表中。Rails随后将使用同一封电子邮件将这两条记录保存到用户表中,现在您需要处理一个非常糟糕的问题

    要避免这种情况,还需要在数据库级别创建一个唯一的约束:

    class CreateUsers < ActiveRecord::Migration
      def change
        create_table :users do |t|
          t.string :email
          ...
        end
        
        add_index :users, :email, unique: true
      end
    end
    
    class CreateUsers
    因此,通过创建索引用户电子邮件唯一索引,您可以获得两个非常好的好处。数据完整性和良好的性能,因为唯一索引往往非常快


    如果在用户id的posts表中输入unique:true,则不允许输入具有相同用户id的重复记录。

    至于唯一性

    唯一性验证属性的值在之前是唯一的 对象被保存。它不会在中创建唯一性约束 数据库,因此可能会发生两个不同的数据库连接 为要创建的列创建两个具有相同值的记录 要独一无二。为了避免这种情况,您必须在这两者上创建一个唯一的索引 数据库中的列

    此外,如果您只是在模型级别验证了
    的唯一性,那么您将被限制从rails端插入重复记录,而不是在数据库级别。 通过dbconsole的SQL注入查询将毫无问题地插入重复记录

    当您说您在“posts”表中的“user_id”上创建了一个索引为的外键时,默认情况下rails只在其上创建一个
    索引
    ,而不是
    唯一索引
    。如果您有1-M关系,那么在您的案例中,唯一索引中就没有任何意义

    如果“用户id”的posts表中有
    unique:true
    那么,就不可能用相同的“用户id”复制记录