Khi dự án của bạn yêu cầu xây dựng database cho một ứng dụng cho phép chia sẻ một vài dữ liệu chung cho một vài sites khác. Ví dụ như là chúng ta có 2 ứng dụng là notesmemories cần dùng chung database là bảng user để khi mà người dùng có tài khoản trong notes thì đều có thể đăng nhập vào memories thông qua tài khoản đó trong khi đó chúng ta cũng có những database riêng để lưu trữ dữ liệu cho những phần khác của những sites đó. Vậy chúng ta cần phải làm gì?

Đến đây chắc bạn cũng sẽ nghĩ cần phải tách dữ liệu và lưu trữ vào những database riêng biệt, qua đó có thể include vào những sites khác nhau để dùng. Vậy làm sao để có thể sử dụng multiple database trong ứng dụng, dưới đây mình sẽ hướng dẫn config và migration multiple databases trong Rails application. Bài hướng dẫn sử dụng Rails 4.x

Đầu tiên để ứng dụng có thể hoạt động ta cần configure database connection và mọi việc này hoàn toàn có thể nằm trong 1 file duy nhất là database.yml. Có rất nhiều cách khác nhau để config các database, ở đây mình sẽ giới thiệu cách mà mình thấy là rõ ràng và có thể biểu hiện được multi database ở đây. Chúng ta có 2 database là db1db2 sẽ config như sau:

# config/database.yml
defaults: &defaults
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password:
  collation: utf8_unicode_ci
  socket: /var/run/mysqld/mysqld.sock

db1:
  development:
    database: db1_development
    <<: *defaults
  test:
    database: db1_test
    <<: *defaults

  production:
    database: db1_production
    <<: *defaults

db2:
  development:
    database: db2_development
    <<: *defaults

  test:
    database: db2_test
    host: localhost
    <<: *defaults

  production:
    database: db2_production
    host: localhost
    <<: *defaults

development:
  database: db1_development
  <<: *defaults
test:
  database: db1_development
  <<: *defaults

Lưu ý, nhìn bên trên kia bạn vẫn thấy có config development đúng không? Đừng bận tâm nhiều đến nó, vì đó chỉ là một khai báo mặc định app sẽ nhận nó, còn lại chúng ta sẽ sử dụng những config trong db1db2 để tiến hành migration và connect để sử dụng.

Tiếp đó chúng ta sẽ định nghĩa constants cho config của db1db2 để có thể dễ dàng sử dụng ở tất cả mọi nơi cần dùng (migration và model), ta có thể định nghĩa chúng trong application.rb

# config/application.rb
module MultiMysql
  db_conf = YAML.load_file("config/database.yml")
  DB1_CONF = db_conf["db1"][Rails.env]
  DB2_CONF = db_conf["db2"][Rails.env]
  ....
end

Tiếp đến chúng ta sẽ xử lý phần migration. Đầu tiên, bạn tạo các file migration như bình thường, có thể sử dụng lệnh rails g model ... của rails sẽ tạo ra file migration như bình thường, sau đó bạn sẽ thêm vào mỗi file @connection để biết được là mình đang connect đến database nào, muốn cho bảng đó nằm trong database nào thì ta connect đến database đó. Hay để dễ quản lý, mình sẽ chia migration ra làm 2 folder riêng biệt, 1 lưu trữ migration file cho database 1 được đặt tên là db1 và cái còn lại sẽ lưu trữ migration file cho database 2 với tên là db2. Sau đó mình sẽ tạo file base cho mỗi db1db2 trong đó có chứa connection kết nối tới Mysql của db1db2. Ví dụ file migration của db1

# db/migrate/db1/db1_migration_base.rb
class Db1MigrationBase < ActiveRecord::Migration
  protected
  def change
    @connection = ActiveRecord::Base.establish_connection(MultiMysql::DB1_CONF).connection
  end
end
# db/migrate/db1/20160505121212_create_users.rb
require_relative './db1_migration_base'
class CreateUsers < Db1MigrationBase
  def change
    super
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps null: false
    end
  end
end

Giờ, nếu muốn thêm bảng nào vào db1 bạn cũng chỉ cần tạo file migration như bình thường sau đó chuyển file đó vào thư mục db1 và cho nó kế thừa từ Db1MigrationBase sử dụng super function change để có connection đến config db1 và sử dụng như bình thường.

Một điều lưu ý là bạn cần tạo database bằng tay, không như database bình thường là chúng ta chỉ cần dùng lệnh rake db:create vì nó sẽ chỉ tạo database config trong development. Giờ bạn có thể dùng lệnh rake db:migrate như bình thường. Sau khi migration xong ta có thể kiểm tra database trong mysql, thu được kết quả như sau:

Cuối cùng, ta sẽ nói cho model biết connect đến database nào bằng cách sử dụng establish_connection của ActiveRecord.

# app/models/user.rb
class User < ActiveRecord::Base
  establish_connection MultiMysql::DB1_CONF

  has_many :products
end
# app/models/product.rb
class Product < ActiveRecord::Base
  establish_connection MultiMysql::DB2_CONF

  belongs_to :user
end

Chúng ta hoàn toàn có thể sử dụng association bình thường.

User.first.products
    User Load (0.4ms)  SELECT  `users`.* FROM `users`  ORDER BY `users`.`id` ASC LIMIT 1
    Product Load (0.6ms)  SELECT `products`.* FROM `products` WHERE `products`.`user_id` = 1

Vậy là xong, bây giờ bạn có thể config bao nhiêu database tùy ý.

Reference

Ruby on Rails Connect to Multiple Databases and Migrations
Multiple-Databases In Single Rails Application
Connecting Models To Different Databases In Rails

Demo

https://github.com/nguyenngoc2505/multi-database-demo