Utilisez plusieurs bases de données avec Rails 6.0

À 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

Ce que j'ai fait

--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

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 ** スクリーンショット 2020-10-31 4.17.26.png

base de données primaire / réplique

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.

スクリーンショット 2020-10-31 14.17.32.png

Paramètres de la base de données

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

Créer une classe abstraite pour le modèle

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.

Créer un 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.

Vérifiez si le primaire / réplica est commuté sur demande

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.

paramètres arproxy

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


class MultipleDatabaseConnectionLogger < Arproxy::Base
 def execute(sql, name = nil)
  role = ActiveRecord::Base.current_role
  name = "#{name} [#{role}]"
  super(sql, name)
 end
end

Vérifiez l'état de la connexion à la base de données au moment de la demande

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

スクリーンショット 2020-10-31 18.40.58.png

show

$ curl localhost:3000/users/1

スクリーンショット 2020-10-31 18.41.09.png

create

$ curl -X POST -H 'Content-Type: application/json' -d '{"name": "saito", "school_id": 1}' localhost:3000/users

スクリーンショット 2020-10-31 18.47.08.png

update

$ curl -X PUT -H 'Content-Type: application/json' -d '{"name": "saito(updated)"}' localhost:3000/users/5

スクリーンショット 2020-10-31 18.48.18.png

destroy

$ curl -X DELETE http://localhost:3000/users/5

スクリーンショット 2020-10-31 18.48.41.png

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é.

Vérifiez le comportement de JOIN

Vous pouvez JOIN entre les tables de la même base de données

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

JOIN ne peut pas être effectué entre les tables de différentes bases de données

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)

enfin

Dans l'attente des fonctionnalités de partitionnement qui seront prises en charge à partir de Rails 6.1

référence

Recommended Posts

Utilisez plusieurs bases de données avec Rails 6.0
Utilisez plusieurs cases à cocher dans Rails6!
[Docker] À utiliser à tout moment avec Docker + Rails
[Rails] Utiliser jQuery
Téléchargez facilement plusieurs images avec rails rails + carrierwave + cloudinary
[Rails] Procédure de liaison de bases de données avec Ruby On Rails
Créer un graphique simple avec Rails Utiliser gem-chartkick / groupdate
[Ruby on Rails] Téléversement de plusieurs images avec refile
Utiliser ProGuard avec Gradle
[Rails 6] Erreur d'exécution avec $ rails s
Utiliser Puphpeteer avec Docker
Utilisez XVim2 avec Xcode 12.0.1
Utilisation de CentOS avec LXD
Manipuler le dispositif avec des rails
[Rails] Didacticiel Apprendre avec les rails
[Rails] Test avec RSpec
Utiliser Webmock avec Rspec
Utiliser des images avec des rails
[Rails] Développement avec MySQL
Utiliser les WebJars avec Gradle
Prend en charge la multilinguisme avec Rails!
Utilisez jlink avec gradle
Utiliser des couches Lambda avec Java
Utiliser GDAL avec Python avec Docker
Pourquoi puis-je utiliser la commande rails installée avec gem? ??
Utiliser Thymeleaf avec Azure Functions
[Rails] Polymorphique express avec graphql-ruby
[Rails] Télécharger des vidéos avec Rails (ActiveStorage)
[Vue Rails] "Hello Vue!" Affiché avec Vue + Rails
[RSpec] Utilisons FactoryBot [Rails]
[Rails] Comment utiliser enum
Japaneseize en utilisant i18n avec Rails
Utiliser l'API Bulk avec RestHighLevelClient
Préparation au développement avec Rails
Utilisez SDKMAN! Avec Git Bash
Comment utiliser la jonction de rails
Exécuter des rails à chaque fois dans le docker
[Docker] Construction de l'environnement Rails 5.2 avec docker
[Rails] Spécifiez le format avec link_to
[Rails] Comment utiliser la validation
Utiliser Spring JDBC avec Spring Boot
[Rails] Recherche à partir de plusieurs colonnes + conditions avec Gem et ransack
[Rails] Comment utiliser authenticate_user!
Utilisez Ruby avec Google Colab
Utiliser SpatiaLite avec Java / JDBC
Utilisez log4j2 avec YAML + Gradle
Utilisez votre propre classe dans le répertoire lib avec Rails6
[Rails] Comment utiliser Scope
Utiliser PlantUML avec Visual Studio Code
[Rails] Comment utiliser la "devise" des gemmes