Підключення Rails 3.1 до декількох баз даних


77

У ShowNearby ми робили дуже великий перехід на RoR 3.1 з PHP, і ми стикаємося з кількома проблемами, які, можливо, деякі з вас вирішували раніше.

Ми маємо великий обсяг даних, і ми вирішили розділити нашу БД на кілька БД, які ми можемо обробляти окремо. Наприклад, наші облікові записи, місця, журнали та інші розділені на кілька баз даних

Нам потрібно отримати міграції, світильники, моделі, щоб грати гарно, і поки що це було досить безладно. Деякі наші вимоги до прийнятного рішення:

  • одна модель повинна стосуватися однієї таблиці в одній з баз даних.
  • rake db: drop - повинен скинути все середовище бази даних, яке ми вказали в database.yml
  • rake db: create - повинен створити всю базу даних env, яку ми вказали в database.yml
  • rake db: migrate - повинен запускати міграції до різних баз даних
  • rake db: test - слід захоплювати світильники та опускати їх до різних баз даних та тестового блоку / функції / тощо

Ми розглядаємо можливість встановлення окремих проектів для кожної бази даних та підключення їх до ActiveResource, але ми вважаємо, що це не дуже ефективно. Хтось із вас раніше стикався з подібною проблемою?


Ми також розглядаємо можливість оновлення з PHP-додатки на Rails; вам пощастило з цим?
Tommyixi

Привіт @Tommyixi: це було дуже давно, і з того часу багато що змінилося. Ретроспективно, я думаю, що зараз є кращим рішенням об’єднати їх в одну базу даних, ніж розділити їх на кілька баз даних
Фер Мартін,

Відповіді:


142

На відповідь Wukerplank, ви також можете помістити деталі з'єднання в database.yml, як зазвичай, з таким іменем:

log_database_production:
  adapter: mysql
  host: other_host
  username: logmein
  password: supersecret
  database: logs

Тоді у вашій спеціальній моделі:

class AccessLog < ActiveRecord::Base
  establish_connection "log_database_#{Rails.env}".to_sym
end

Щоб ці докучливі дані не були в коді вашої програми.

Редагувати: якщо ви хочете повторно використовувати це з’єднання в декількох моделях, вам слід створити новий абстрактний клас і успадкувати його, оскільки зв’язки тісно пов’язані з класами (як пояснено тут , тут і тут ), кожен клас.

Якщо це так, налаштуйте все так:

class LogDatabase < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "log_database_#{Rails.env}".to_sym
end

class AccessLog < LogDatabase
end

class CheckoutLog < LogDatabase
end

Як врахувати зміни навколишнього середовища? Так, наприклад, у розробці я хочу establish_connectionз log_devбазою даних, але у виробництві я хочу establish_connectionз logбазою даних. Чи можу я просто зателефонувати Rails.env?
Роберт Ауді

@AzizLightestablish_connection "log_database_#{Rails.env}"
Unixmonkey

12
Будьте попереджені. Здається, що використання цього методу залишає з'єднання в додаткових базах даних відкритими, не використовуючи їх повторно. Це зупинить вашу програму під великим навантаженням.
Altonymous

10
@Altonymous Хороший момент. Я думаю, ви маєте на увазі таку поведінку: github.com/rails/rails/issues/7019 З’єднання стає пов’язаним із класом; тому, якщо вам потрібно повторно використовувати з’єднання, вам слід встановити його на абстрактному класі та успадкувати від нього замість AR :: Base. Я оновив свою відповідь, щоб відобразити це.
Unixmonkey

Так. Ідеально Я збирався відповісти цією відповіддю, якщо ви не скоро. : P
Altonymous

18

Підключитися до різних баз даних досить просто:

# model in the "default" database from database.yml
class Person < ActiveRecord::Base

  # ... your stuff here

end

# model in a different database
class Place < ActiveRecord::Base

  establish_connection (
    :adapter  => "mysql",
    :host     => "other_host",
    :username => "username",
    :password => "password",
    :database => "other_db"
  )

end

Я був би обережний із налаштуванням декількох проектів Rails, оскільки ви додасте багато накладних витрат на пошук даних для своїх контролерів, що може сповільнити роботу.

Що стосується ваших питань щодо міграцій, пристосувань, моделей тощо: Я не думаю, що це буде простий спосіб, тому, будь ласка, розміщуйте окремі запитання та будьте максимально конкретні.

Об’єднання БД в одне не є варіантом? Це значно полегшило б вам життя!


1
Проблема полягає в тому, що пул з'єднань не буде використовуватися належним чином із наведеним вище зразком
Сем Саффрон

11

Знайшов чудовий пост, який вкаже іншим на правильний спосіб зробити це http://blog.bitmelt.com/2008/10/connecting-to-multiple-database-in-ruby.html

Налаштуйте це приблизно так:

database.yml (файл конфігурації DB)

support_development:
    adapter: blah
    database: blah
    username: blah
    password: blah

support_base.rb (файл моделі)

class SupportBase < ActiveRecord::Base
    self.abstract_class = true #important!
    establish_connection("support_development")
end

tst_test.rb (файл моделі)

class TstTest < SupportBase 
    #SupportBase not ActiveRecord is important!

    self.table_name = 'tst_test'

    def self.get_test_name(id)
        if id = nil
            return ''
        else
            query = "select tst_name from tst_test where tst_id = \'#{id}\'"
            tst = connection.select_all(query) #select_all is important!
            return tst[0].fetch('tst_name')
        end
    end
end

PS, це насправді не охоплює міграції, я не думаю, що ви можете виконувати міграції на більш ніж одній БД за допомогою rake (хоча я не впевнений, що цього важко "не можна зробити", можливо це можливо). Це був просто чудовий спосіб підключення та запитів інших БД, якими ви не керуєте.


5

Можливо, ви також захочете додати середовище Rails, тому ваші бази розробки та тестування не однакові.

establish_connection "legacy_#{Rails.env}"

3

У наступній статті пропонується визначити нові завдання Rake для переходу до кількох баз даних. Кожне завдання встановлює власне з'єднання, а потім виконує міграцію за допомогою цього з'єднання та конкретної папки бази даних.

Він також визначає знайоме, db:migrateщо викликає дві інші задачі.

У тому числі сюди, якщо посилання стане недоступним:

desc "Migrate the database through scripts in db/migrate directory."

namespace :db do
  task :migrate do
    Rake::Task["db:migrate_db1"].invoke
    Rake::Task["db:migrate_db2"].invoke
  end

  task :migrate_db1 do
    ActiveRecord::Base.establish_connection DB1_CONF
    ActiveRecord::Migrator.migrate("db/migrate/db1/")
  end

  task :migrate_db2 do
    ActiveRecord::Base.establish_connection DB2_CONF
    ActiveRecord::Migrator.migrate("db/migrate/db2/")
  end
end

Джерело: Ruby on Rails підключається до декількох баз даних та міграцій


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.