DjangoでバックエンドDBにMySQLを利用してMaster/Slave構成とした場合の話です。
たぶんトランザクションをサポートしているようなDBであれば同様です。
settings.py
DATABASES = {
'app_master': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'app',
'USER': 'user',
'PASSWORD': 'pass',
'HOST': '192.168.33.50',
'PORT': '3306',
},
'app_slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'app',
'USER': 'user',
'PASSWORD': 'pass',
'HOST': '192.168.33.60',
'PORT': '3306',
'TEST': {
'MIRROR': 'app_master',
},
},
}
database_router.py
class DatabaseRouter(object):
def db_for_read(self, model, **hints):
"""
Read
"""
return "app_slave"
def db_for_write(self, model, **hints):
"""
Write
"""
return "app_master"
...
上記のようなコードを書いて、更新系は【app_master】に、参照系は【app_slave】に流すという設定とします。
アプリケーション内で、「ユニークキーに対応するデータがあればUPDATE、なければINSERT」というようなことをします。
MySQLの【ON DUPLICATE KEY UPDATE】のようなことです。
@staticmethod
def upsert_item_num(user_id, item_id, num):
try:
# ItemModelはuser_id、item_idでユニーク
item = ItemModel.objects.get("user_id"=user_id, "item_id"=item_id)
item.num = num
item.save()
except ItemModel.DoesNotExist as e:
item = ItemModel.object.create(user_id=user_id, item_id=item_id, num=num)
return item
@transaction.atomic(using="app_master")
def update(self, request, pk):
# なんかいろいろやって
upsert_item_num(1, 1, 10)
# さらにいろいろやって追加で
upsert_item_num(1, 1, 20)
最初こういうコード書いていたんだけど、Master/Slaveの設定を反映させたら急にエラーになった。
2回目のupsert_item_numでDuplicate entryというエラー。
Modelのgetは常にSlaveを参照するように設定しているので、コミットされていない状態だと、2回目のgetもDoesNotExistになっていた。
こういう問題はModelのget_or_createを利用するようにしておけば回避できる。
@staticmethod
def upsert_item_num(user_id, item_id, num):
item = ItemModel.object.get_or_create(user_id=user_id, item_id=item_id)
item.num = num
item.save()
return item
get_or_createは更新系に対してクエリを投げるぽいです。