Rails what's difference in unique index and validates_uniqueness_of

前端 未结 3 2323
难免孤独
难免孤独 2021-02-15 02:43

Firstly, can anyone explain how unique index works in databases?

Suppose I have a User model with a name column and I add a unique index on it

3条回答
  •  失恋的感觉
    2021-02-15 03:19

    Here are the difference between unique index and validates_uniqueness_of

    This is a patch to enable ActiveRecord to identify db-generated errors for unique constraint violations. For example, it makes the following work without declaring a validates_uniqueness_of:

    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"
    

    The benefits are speed, ease of use, and completeness --

    Speed

    With this approach you don't need to do a db lookup to check for uniqueness when saving (which can sometimes be quite slow when the index is missed -- https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate... ). If you really care about validating uniqueness you're going to have to use database constraints anyway so the database will validate uniqueness no matter what and this approach removes an extra query. Checking the index twice isn't a problem for the DB (it's cached the 2nd time around), but saving a DB round-trip from the application is a big win.

    Ease of use

    Given that you have to have db constraints for true uniqueness anyway, this approach will let everything just happen automatically once the db constraints are in place. You can still use validates_uniqueness_of if you want to.

    Completeness

    validates_uniqueness_of has always been a bit of a hack -- it can't handle race conditions properly and results in exceptions that must be handled using somewhat redundant error handling logic. (See "Concurrency and integrity" section in http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMe...)

    validates_uniqueness_of is not sufficient to ensure the uniqueness of a value. The reason for this is that in production, multiple worker processes can cause race conditions:

    1. Two concurrent requests try to create a user with the same name (and we want user names to be unique)

    2. The requests are accepted on the server by two worker processes who will now process them in parallel

    3. Both requests scan the users table and see that the name is available

    4. Both requests pass validation and create a user with the seemingly available name

    For more clear understanding please check this

    If you create a unique index for a column it means you’re guaranteed the table won’t have more than one row with the same value for that column. Using only validates_uniqueness_of validation in your model isn’t enough to enforce uniqueness because there can be concurrent users trying to create the same data.

    Imagine that two users tries to register an account with the same email where you have added validates_uniqueness_of :email in your user model. If they hit the “Sign up” button at the same time, Rails will look in the user table for that email and respond back that everything is fine and that it’s ok to save the record to the table. Rails will then save the two records to the user table with the same email and now you have a really shitty problem to deal with.

    To avoid this you need to create a unique constraint at the database level as well:

    class CreateUsers < ActiveRecord::Migration
      def change
        create_table :users do |t|
          t.string :email
          ...
        end
        
        add_index :users, :email, unique: true
      end
    end
    

    So by creating the index_users_on_email unique index you get two very nice benefits. Data integrity and good performance because unique indexes tends to be very fast.

    If you put unique: true in your posts table for user_id then it will not allow to enter duplicate records with same user_id.

提交回复
热议问题