À partir de Rails 6.0, plusieurs bases de données sont disponibles en tant que fonctionnalités standard L'utilisation de plusieurs bases de données présente les avantages de faciliter la mise à l'échelle lorsque l'échelle du projet augmente et d'augmenter le nombre de connexions.
Dans cet article, j'essaierai d'utiliser deux bases de données et une réplique de la base de données dans une application Rails.
Le code source créé est publié sur GitHub https://github.com/youichiro/rails-multiple-db-sandbox
--Utilisation de plusieurs bases de données --Créer une base de données commune et une base de données scolaire --Créer un modèle pour chaque base de données --Utiliser primaire / réplique --Préparer une réplique de la base de données commune et une réplique de la base de données de l'école
Plusieurs bases de données sont un mécanisme de lecture et d'écriture de données en se connectant à plusieurs bases de données à partir d'une seule application. Lorsqu'il y a deux bases de données, la base de données A et la base de données B, Rails peut ** changer la base de données connectée en fonction du modèle à appeler **
Il s'agit d'un mécanisme permettant de préparer un réplica en lecture seule pour une base de données et de basculer entre le primaire et le réplica en fonction de la demande. Lorsque le nombre d'accès à la base de données augmente, la charge d'accès peut être répartie en séparant le DB pour l'écriture et le DB pour la lecture. Rails bascule automatiquement les requêtes ** POST, PUT, PATCH et DELETE vers les requêtes primaires ** et ** GET et HEAD pour accéder au réplica ** s'il n'y a pas d'écriture récente.
Le config / database.yml
lors de la création d'une base de données comme celle-ci ressemble à ceci:
config/database.yml
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password:
host: localhost
port: 3306
development:
common:
<<: *default
database: rails_app_common_development
migrations_paths: db/common_migrate
common_replica:
<<: *default
database: rails_app_common_development
replica: true
school:
<<: *default
database: rails_app_school_development
migrations_paths: db/school_migrate
school_replica:
<<: *default
database: rails_app_school_development
replica: true
Pour la base de données principale, migrations_paths
spécifie l'emplacement de stockage du fichier de migration.
Spécifiez replica: true
pour la réplique
Créer une base de données avec ce paramètre
$ bin/rails db:create
Changer la base de données connectée en fonction du modèle à appeler Créez une classe abstraite qui constitue la base du modèle qui se connecte à la base de données commune et décrivez les paramètres de connexion à la base de données.
app/models/common_base.rb
class CommonBase < ApplicationRecord
self.abstract_class = true
connects_to database: { writing: :common, reading: :common_replica }
end
Créez une classe CommonBase
qui hérite de ApplicationRecord et spécifiez la base de données au moment de l'écriture et la base de données au moment de la lecture avec connections_to
.
Créez une classe abstraite pour le modèle qui se connecte également à la base de données de l'école
app/models/school_base.rb
class SchoolBase < ApplicationRecord
self.abstract_class = true
connects_to database: { writing: :school, reading: :school_replica }
end
Assurez-vous d'hériter de CommonBase
ou SchoolBase
lors de la création d'un nouveau modèle
Cela vous permet de changer la destination de connexion à la base de données en fonction du modèle.
C'est la procédure de création d'un «modèle utilisateur» dans la base de données commune. Commencez par créer un fichier de modèle et un fichier de migration avec la commande generate model.
$ bin/rails g model user name:string school:references --database common
Running via Spring preloader in process 54763
invoke active_record
create db/common_migrate/20201030135726_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
Fichier de migration créé dans le répertoire db / common_migrate
En spécifiant la base de données à connecter à --database
, elle sera créée dans migrations_paths
défini dans database.yml.
Si vous souhaitez créer un modèle dans la base de données de l'école, spécifiez --database school
Puis faites la migration
#Lors de l'application de tous les fichiers de migration
$ bin/rails db:migrate
#Lors de l'application uniquement du fichier de migration de la base de données commune
$ bin/rails db:migrate:common
Enfin, changez le fichier modèle
Au moment de la génération, il hérite de ApplicationRecord, mais comme le modèle User souhaite utiliser la base de données commune, modifiez-le pour hériter de CommonBase
.
- class User < ApplicationRecord
+ class User < CommonBase
end
Vous pouvez maintenant lire et écrire à partir de la base de données commune pour le modèle User.
En préparant une réplique, les requêtes POST, PUT, DELETE, PATCH seront écrites dans le primaire et les requêtes GET, HEAD seront lues à partir du réplica. Pour vérifier cela, utilisez arproxy pour afficher l'état de la connexion à la base de données dans le journal des requêtes.
gem arproxy
à Gemfile
et bundle install
config / initializers / arproxy.rb
config/initializers/arproxy.rb
if Rails.env.development? || Rails.env.test?
require 'multiple_database_connection_logger'
Arproxy.configure do |config|
config.adapter = 'mysql2'
config.use MultipleDatabaseConnectionLogger
end
Arproxy.enable!
end
lib / multiple_database_connection_logger.rb
lib/multiple_database_connection_logger.rb
class MultipleDatabaseConnectionLogger < Arproxy::Base
def execute(sql, name = nil)
role = ActiveRecord::Base.current_role
name = "#{name} [#{role}]"
super(sql, name)
end
end
Si vous envoyez une demande de curl et regardez le journal, vous pouvez voir s'il a été écrit ou lu Essayez avec le [users_controller] pré-créé (https://github.com/youichiro/rails-multiple-db-sandbox/blob/master/app/controllers/users_controller.rb)
index
$ curl localhost:3000/users
show
$ curl localhost:3000/users/1
create
$ curl -X POST -H 'Content-Type: application/json' -d '{"name": "saito", "school_id": 1}' localhost:3000/users
update
$ curl -X PUT -H 'Content-Type: application/json' -d '{"name": "saito(updated)"}' localhost:3000/users/5
destroy
$ curl -X DELETE http://localhost:3000/users/5
Dans le cas de l'index, show action, il lit, dans le cas de l'action create, update, destroy, il écrit et vous pouvez voir que le primaire / réplica est commuté.
Si vous JOIGNEZ la table des étudiants à la table des notes, vous pouvez vous JOINDRE car il s'agit de la même base de données
Grade.joins(:students).where(name: 'grade1')
SQL émis
SELECT `grades`.*
FROM `grades`
INNER JOIN `students` ON `students`.`grade_id` = `grades`.`id`
WHERE `grades`.`name` = 'grade1
Si vous essayez de JOIN de la table des étudiants à la table des utilisateurs, vous ne pouvez pas REJOINDRE car il s'agit d'une base de données différente
User.joins(:students).where(name: 'ogawa')
Erreur qui se produit
ActiveRecord::StatementInvalid (Mysql2::Error: Table 'rails_app_common_development.students' doesn't exist)
Dans l'attente des fonctionnalités de partitionnement qui seront prises en charge à partir de Rails 6.1
Recommended Posts