2009-12-01 10 views
49

Est-ce possible? Dans une seule application, cela gère de nombreux projets avec SQLite. Ce que je veux, c'est avoir une base de données différente pour chaque projet que mon application gère .. donc plusieurs copies d'une base de données structurée de manière identique, mais avec des données différentes. Je vais choisir quelle copie utiliser base sur params sur l'URI. Ceci est fait pour 1. la sécurité .. Je suis un novice dans ce genre de programmation et je ne veux pas qu'il arrive que pour une raison quelconque en travaillant sur un projet, un autre soit corrompu. 2. facile Sauvegarde et archivage de vieux projetsPlusieurs bases de données dans Rails

+0

http://imnithin.github.io/multiple-database.html – Nithin

Répondre

38

Rails par défaut n'est pas conçu pour une architecture multi-base de données et, dans la plupart des cas, il n'a aucun sens du tout. Mais oui, vous pouvez utiliser différentes bases de données et connexions.

Voici quelques références:

+0

N'aurait pas pu le dire mieux. Les environnements permettent également d'avoir un système db différent entre la production et le développement par exemple. Je ne recommanderais pas de faire cette pensée – marcgg

+0

Je prévois de faire un choix similaire parce que je m'attends à ce que certaines tables de base de données aient des centaines de millions d'enregistrements.La division de la base de données en instances distinctes par client me permettrait d'avoir un seul serveur d'applications avec plusieurs serveurs de base de données. – Mika

+0

Vos deux derniers liens ne sont plus pertinents. Et le premier pourrait devenir obsolète avec une nouvelle version de rails. Veuillez inscrire les parties de code correspondantes dans la réponse. – ndn

4

Vous devriez également consulter ce projet appelé DB Charmer: http://kovyrin.net/2009/11/03/db-charmer-activerecord-connection-magic-plugin/

DbCharmer est un plugin simple mais puissant pour ActiveRecord qui fait quelques choses:

  1. vous permet de gérer facilement des connexions (switch_connection_to méthode)
  2. Permet de changer les modèles AR de modèles AR connexions par défaut à un serveurs/bases de données distinctes
  3. vous permet de choisir facilement où votre requête devrait aller (on_* méthodes famille)
  4. Permet d'envoyer automatiquement des requêtes à lire vos esclaves en maîtres gérerait toutes les mises à jour.
  5. ajoute plusieurs migrations de bases de données à ActiveRecord
+1

Le plugin a été tué par l'auteur, à 02.01.2015, personne n'est intervenu pour le maintenir. – Smar

26

Si vous êtes en mesure de contrôler et de configurer chaque Rails par exemple, et vous pouvez vous permettre de gaspiller des ressources à cause d'eux étant en attente, vous épargner quelques problèmes et juste changer la database.yml pour modifier la connexion à la base de données utilisée sur chaque instance. Si vous êtes préoccupé par la performance, cette approche ne le coupera pas.

Pour les modèles liés à une seule table unique sur une seule base de données que vous pouvez appeler establish_connection à l'intérieur du modèle:

establish_connection "database_name_#{RAILS_ENV}" 

Comme décrit ici: http://apidock.com/rails/ActiveRecord/Base/establish_connection/class

Vous aurez des modèles utilisant des tables d'une base de données et d'autres modèles différents utilisant des tables d'autres bases de données.

Si vous avez des tables identiques, communes sur différentes bases de données et partagées par un seul modèle, ActiveRecord ne vous aidera pas. En 2009, j'avais besoin de cela sur un projet sur lequel je travaillais, en utilisant Rails 2.3.8. J'avais une base de données pour chaque client, et j'ai nommé les bases de données avec leurs identifiants.J'ai donc créé une méthode pour changer la connexion à l'intérieur ApplicationController:

def change_database database_id = params[:company_id] 
    return if database_id.blank? 

    configuration = ActiveRecord::Base.connection.instance_eval { @config }.clone 
    configuration[:database] = "database_name_#{database_id}_#{RAILS_ENV}" 

    MultipleDatabaseModel.establish_connection configuration 
end 

et a ajouté que la méthode comme before_filter à tous les contrôleurs:

before_filter :change_database 

Ainsi, pour chaque action de chaque contrôleur, lorsque params [ : company_id] est défini et défini, il va changer la base de données à la bonne.

Pour gérer les migrations j'ai étendu ActiveRecord :: Migration, avec une méthode qui recherche tous les clients et Itère un bloc à chaque ID:

class ActiveRecord::Migration 
    def self.using_databases *args 
     configuration = ActiveRecord::Base.connection.instance_eval { @config } 
     former_database = configuration[:database] 

     companies = args.blank? ? Company.all : Company.find(args) 

     companies.each do |company| 
      configuration[:database] = "database_name_#{company[:id]}_#{RAILS_ENV}" 
      ActiveRecord::Base.establish_connection configuration 

      yield self 
     end 

     configuration[:database] = former_database 
     ActiveRecord::Base.establish_connection configuration 
    end 
end 

Notez que ce faisant, il serait impossible pour vous faire des requêtes dans la même action à partir de deux bases de données différentes. Vous pouvez appeler à nouveau change_database mais il deviendra méchant lorsque vous essayez d'utiliser des méthodes qui exécutent des requêtes, à partir des objets qui ne sont plus liés à la base de données correcte. En outre, il est évident que vous ne pourrez pas joindre des tables appartenant à des bases de données différentes.

Pour gérer cela correctement, ActiveRecord devrait être considérablement étendu. Il devrait y avoir un plugin maintenant pour vous aider avec ce problème. Une recherche rapide m'a donné celui-ci:

DB-Charmer: http://kovyrin.github.com/db-charmer/

Je suis prêt à essayer. Faites-moi savoir ce qui fonctionne pour vous.

2

Il convient de noter que dans toutes ces solutions, vous devez vous rappeler de fermer les connexions de base de données personnalisées. Vous sera à court de connexions et de voir des problèmes étranges de demande de délai d'expiration sinon.

Une solution simple consiste à effacer les connexions actives! dans un after_filter dans votre contrôleur.

after_filter :close_custom_db_connection 

def close_custom_db_connection 
    MyModelWithACustomDBConnection.clear_active_connections! 
end 
+4

Rails utilisent pool de connexion, il convient d'éviter d'abuser de la limite de connexion. – Anatoly

12

J'ai dépassé en ajoutant ceci au-dessus de mes modèles en utilisant l'autre base de données

class Customer < ActiveRecord::Base 
    ENV["RAILS_ENV"] == "development" ? host = 'devhost' : host = 'prodhost' 

    self.establish_connection(
     :adapter => "mysql", 
     :host  => "localhost", 
     :username => "myuser", 
     :password => "mypass", 
     :database => "somedatabase" 
    ) 
0

dans votre config/database.yml faire quelque chose comme ça

default: &default 
    adapter: postgresql 
    encoding: unicode 
    pool: 5 

development: 
    <<: *default 
    database: mysite_development 

test: 
    <<: *default 
    database: mysite_test 

production: 
    <<: *default 
    host: 10.0.1.55 
    database: mysite_production 
    username: postgres_user 
    password: <%= ENV['DATABASE_PASSWORD'] %> 

db2_development: 
    <<: *default 
    database: db2_development 

db2_test: 
    <<: *default 
    database: db2_test 

db2_production: 
    <<: *default 
    host: 10.0.1.55 
    database: db2_production 
    username: postgres_user 
    password: <%= ENV['DATABASE_PASSWORD'] %> 

puis dans vos modèles, vous pouvez référencer db2 avec

class Customers < ActiveRecord::Base 
    establish_connection "db2_#{Rails.env}".to_sym 
end 
0

Ce que vous avez décrit dans la question est multitenancy (bases de données structurées de manière identique avec des données différentes dans chaque). Le Apartment gem est génial pour cela.

Pour la question générale de plusieurs bases de données dans Rails: ActiveRecord prend en charge plusieurs bases de données, mais Rails ne fournit pas un moyen de les gérer. J'ai récemment créé la gemme Multiverse pour résoudre ce problème.

Questions connexes