Django で複数 DB を実装します。
前回は、アプリケーション 毎に複数 DB を使用する方法を実装しました。
今回は、マスタースレーブ構成の実装をしたいと思います。
マスターは読み書き(Select, Insert, Update, Delete)が出来て、スレーブは読み取り(Select)のみできるという実装です。
Contents
完成イメージ
バージョン
- Python 3.6.4
- Django 2.0.3
- MySQL 5.7.21
- mysqlclient 1.3.12
インストール
Python, Django
Python と Django のインストールは、Python Web フレームワーク Django の環境を構築するの記事を参考にしてください。
MySQL, mysqlclient
MySQL と mysqlclient のインストールは、Djangoでマイグレーションを行う + MySQLの記事を参考にしてください。
実装
settings.py
ALLOWED_HOSTS = ['*'] INSTALLED_APPS = [ ... 'app1.app.App1Config' ] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'master', 'USER': 'root', 'PASSWORD': 'Eba7|B33veK+', 'OPTIONS': { 'charset': 'utf8mb4', } }, 'slave': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'slave', 'USER': 'root', 'PASSWORD': 'Eba7|B33veK+', 'OPTIONS': { 'charset': 'utf8mb4', } }, } DATABASE_ROUTERS = ['multidb_app.db_router.DbRouter']
settings.py に DB を2つ定義します。’default’ を master とし、master は、Read と Write 両方を受け付けます。もう1つは、slave とし、こちらは、Read のみを受け付けます。
それらの制御は後述する db_router.py で実装します。
db_router.py
import random class DbRouter: def db_for_read(self, model, **hints): return random.choice(['default', 'slave']) def db_for_write(self, model, **hints): return 'default' def allow_relation(self, obj1, obj2, **hints): return True def allow_migrate(self, db, app_label, model=None, **hints): return True
DB ルータです。
db_for_read 関数で、Read (Select) の動作を定義します。default (master) と slave をランダムで接続しています。
db_for_write 関数で、Write (Insert, Update, Delete) の動作を定義しますので、default (master) のみに接続するようにしています。
models.py
from django.db import models class Table1(models.Model): column1 = models.CharField(max_length=10) def __str__(self): return self.column1
動作確認用に、カラム1つの簡単なテーブルを models.py に定義します。
urls.py
from django.urls import path from app1.views import app1 urlpatters = [ path('app1/', app1), ]
こちらも動作確認用に簡単な urls.py を定義します。
views.py
from django.db import router from django.http import HttpResponse from app1.models import Table1 def app1(request): read_db = router.db_for_read(Table1) write_db = router.db_for_write(Table1) response = '''read_db: {read_db} write_db: {write_db}'''.format(read_db=read_db, write_db=write_db) return HttpResponse(response)
views.py では、Django の router モジュールを使用して、現在接続している DB を取得し、ブラウザに表示するようにしています。
これで準備は整いました。
DB 作成
本来であれば、DB を2つ用意して、レプリケーション設定をすべきですが、ここではその設定を省略し、同一サーバーにデータベースを二つ作成し、なんちゃってレプリケーション構成にしています。
MySQL のレプリケーションについては、こちらの Qiita 記事などが参考になると思います。
create database
$ mysql -u root -p -e 'create database master default charset utf8mb4' $ mysql -u root -p -e 'create database slave default charset utf8mb4'
同一サーバーにデータベースを二つ作成し、なんちゃってレプリケーションです。
makemigrations
$ python3.6 manage.py makemigrations
データベースマイグレーション用のファイルを生成します。
migrate
$ python3.6 manage.py migrate $ python3.6 manage.py --database=slave
ちゃんとレプリケーションして入れば、2つ目のコマンドは不要ですが、今回はなんちゃってなので、スレーブにもデータベースマイグレーションを実行します。
show tables
mysql> show tables from master; +----------------------------+ | Tables_in_master | +----------------------------+ | app1_table1 | | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+ 11 rows in set (0.00 sec) mysql> show tables from slave; +----------------------------+ | Tables_in_slave | +----------------------------+ | app1_table1 | | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+ 11 rows in set (0.00 sec) mysql>
テーブルはこんな感じになりました。master も slave も全く同じ構成です。
tree
$ tree . ├── app1 │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── manage.py └── multidb_app ├── __init__.py ├── db_router.py ├── settings.py ├── urls.py └── wsgi.py 3 directories, 14 files
動作確認
runserver
$ python3.6 manage.py rumserver 0.0.0.0:8000
まずは、runserver を起動します。
ブラウザで http://192.168.33.10:8000/app1/
- read_db: default write_db: default
- read_db: slave write_db: default
私の環境のIPは、192.168.33.10 なので、ブラウザで http://192.168.33.10:8000/app1/ にアクセスし、何回か更新すると、上記のように、read_db が default になったり、slave になったりするのが確認できると思います。
まとめ
Django で複数データベースを実装しました。
前回は、アプリケーション毎にデータベースを使用する実装をしましたが、今回は、1つのアプリケーションに対して、2つのデータベース(マスターとスレーブ)の実装をしました。
スレーブを増やしても、settings と db_router を少し修正するだけで対応できるので、とても便利ですね。