Django 複数 DB を実装する(マスタースレーブ構成)

シェアする

Django で複数 DB を実装します。

前回は、アプリケーション 毎に複数 DB を使用する方法を実装しました。

今回は、マスタースレーブ構成の実装をしたいと思います。

マスターは読み書き(Select, Insert, Update, Delete)が出来て、スレーブは読み取り(Select)のみできるという実装です。

完成イメージ

バージョン

  • 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 を少し修正するだけで対応できるので、とても便利ですね。