Rails6.0 여러 데이터베이스 사용

17354 단어 RailsRubyDBtech
Rails6.0부터 여러 데이터베이스를 표준 기능으로 사용 가능
여러 개의 데이터베이스, 예를 들어 프로젝트 규모가 커질 때 축소하기 쉽고 연결 수를 늘릴 수 있다는 장점
본고는 Rails 응용 프로그램에서 두 개의 데이터베이스와 데이터베이스 Replica를 사용하는 방법을 시도할 것이다
생성된 소스 코드가 GiitHub에 공개됨
https://github.com/youichiro/rails-multiple-db-sandbox

다중 데이터베이스


여러 개의 데이터베이스는 한 응용 프로그램에서 여러 개의 데이터베이스로 연결되어 데이터 읽기와 쓰기를 하는 구조이다
데이터베이스 A와 데이터베이스 B 두 개의 DB가 있을 때, Rails는 호출된 모델에 따라 연결된 데이터베이스를 전환할 수 있다
スクリーンショット 2020-10-31 4.17.26.png

primary/replica 데이터베이스


데이터베이스에 읽기 전용 리플리카(복제)를 미리 준비하고 요구에 따라primary와 리플리카의 구조를 전환합니다
데이터베이스에 대한 액세스가 많아지면 쓰기 및 읽기 DB로 데이터를 분할하여 액세스 로드를 분산할 수 있습니다.
Rails에서 POST, PUT, PATCH, DELETE 요청은primary로 자동으로 전환되며, 최근 쓰기가 없으면 GET, HEAD 요청은 자동으로 Replica로 전환됩니다
スクリーンショット 2020-10-31 14.17.32.png

데이터베이스 설정


다음 데이터베이스를 만들 때config/database.yml:
  • common 데이터베이스
  • common 데이터베이스 리플리카
  • school 데이터베이스
  • school 데이터베이스의replica
  • 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
    
    primary 데이터베이스migrations_paths에서 이전 파일의 저장 위치를 지정했습니다
    Replica에서 지정replica: true이 설정을 사용하여 데이터베이스 만들기
    $ bin/rails db:create
    

    모델 추상 클래스 생성하기


    호출된 모델을 통해 연결된 데이터베이스 전환
    common 데이터베이스에 연결된 모델 기초 추상 클래스를 만들고 데이터베이스 연결 목적지의 설정을 설명합니다
    app/models/common_base.rb
    class CommonBase < ApplicationRecord
      self.abstract_class = true
    
      connects_to database: { writing: :common, reading: :common_replica }
    end
    
    Application RecordCommonBase를 계승하여 클래스를 만들었습니다. connects_to 쓸 때의 DB와 읽을 때의 DB를 지정했습니다.
    school 데이터베이스에 연결된 모델의 추상 클래스도 마찬가지로 생성
    app/models/school_base.rb
    class SchoolBase < ApplicationRecord
      self.abstract_class = true
    
      connects_to database: { writing: :school, reading: :school_replica }
    end
    
    새 모델 생성 시 CommonBase 또는 SchoolBase 상속
    이렇게 하면 모델을 통해 데이터베이스의 연결 위치를 전환할 수 있다

    모델 생성하기


    common 데이터베이스에 Userモデル를 만들 때
    우선generate 모델 명령으로 모델 파일과 이전 파일을 만듭니다
    $ 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
    
    마이그레이션 파일은 db/common_migrate 디렉토리에 생성--database에 연결된 데이터베이스, 데이터베이스를 지정합니다.yml 설정migrations_paths에서 제작
    school 데이터베이스에서 모델을 만들고 싶을 때 지정--database school이제 자기 조절을 해볼게요.
    # 全てのマイグレーションファイルを適用する場合
    $ bin/rails db:migrate
    
    # commonデータベースのマイグレーションファイルのみを適用する場合
    $ bin/rails db:migrate:common
    
    모델 파일 최종 수정
    생성 시 계승ApplicationRecord하였으나 User모델은common 데이터베이스를 사용하고자 계승CommonBase으로 변경하였습니다.
    - class User < ApplicationRecord
    + class User < CommonBase
      end
    
    이렇게 하면common 데이터베이스에서 User 모델을 읽고 쓸 수 있습니다

    요구에 따라primary/replica가 전환되었는지 확인하십시오


    리플리카 준비를 통해 POST, PUT,DELETE,PATCH의 요청은primary에 기록되고GET,HEAD 요청은 리플리카에서 읽습니다
    이를 확인하려면 arproxy를 사용하여 조회 로그에 데이터베이스 연결 상황을 표시하십시오

    arproxy 설정

  • Gemfilegem arproxybundle 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
    

    요청 시 데이터베이스 연결 확인


    curl에서 요청을 보내고 로그를 보면 writing인지 reading인지 확인할 수 있습니다
    먼저 해본다users_controller
    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
    index, show 동작의 경우reading,create,update,destroy 동작의 경우writing,primary/replica가 전환 중임을 알 수 있습니다

    JOIN의 행동을 확인합니다.


    같은 데이터베이스 테이블 간에 JOIN


    students 테이블을 grade 테이블에서 JOIN일 때 같은 데이터베이스이기 때문에 JOIN이
    Grade.joins(:students).where(name: 'grade1')
    
    SQL 릴리즈
    SELECT `grades`.*
    FROM `grades`
    INNER JOIN `students` ON `students`.`grade_id` = `grades`.`id`
    WHERE `grades`.`name` = 'grade1
    

    서로 다른 데이터베이스의 표 사이에는 JOIN이 있을 수 없습니다.


    사용자 테이블에 학생표 JOIN을 넣으려면 데이터베이스가 다르기 때문에 JOIN을 사용할 수 없습니다
    User.joins(:students).where(name: 'ogawa')
    
    오류 발생
    ActiveRecord::StatementInvalid (Mysql2::Error: Table 'rails_app_common_development.students' doesn't exist)
    

    끝말


    Rails6.1부터 지원되는 미리 지정된 자르기 기능 기대

    참고 자료

  • Rails 가이드 | Active Record에서 여러 데이터베이스 사용
  • Rails6.0의 멀티플 데이터베이스를 사용해 보았습니다
  • 좋은 웹페이지 즐겨찾기