Pycon에서 한종원님이 Aurora read replica 활용 방안에 대해 발표하신 내용을 정리했다.
(참고)
강의 시점(2019.9)으로 2주 전에 aws에서 한 cluster에 writer를 2개 이상 붙일 수 있도록 확장된 기능을 발표함(multi-writer)
클러스터 구성에 따라 리더 endpoint와 라이터 endpoint의 ip 주소가 바뀌는데, 클러스터 구성을 바꾸고 ping을 하면 해당 ip 주소가 제대로 나오지 않는 경우가 있다.
이는 OS 레벨에서 DNS 캐시를 하기 때문이다.
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": os.getenv("DB_NAME"),
"USER": os.getenv("DB_USER"),
"PASSWORD": os.getenv("DB_PASSWORD"),
"HOST": os.getenv("DB_HOST"),
"PORT": os.getenv("DB_PORT"),
},
"replica": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": os.getenv("DB_NAME"),
"USER": os.getenv("DB_USER"),
"PASSWORD": os.getenv("DB_PASSWORD"),
"HOST": os.getenv("DB_HOST"),
"PORT": os.getenv("DB_PORT"),
},
}
DATABSE_ROUTERS = ['my_app.router.Router'
DATABASES
에 복수개의 연결을 추가할 수 있다.DATABSE_ROUTERS
에는 리스트 형태로 내가 작성한 라우터들을 추가할 수 있다.아래의 두 함수는 그대로 두고, db_for_read
와 db_for_write
만 수정하면 되는데 주로 db_for_read
가 수정 대상이다.
import random
class Router:
@staticmethod
def db_for_read(model, **hints):
databases = ['default', 'replica']
return random.choice(databases)
@staticmethod
def db_for_write(model, **hints):
return 'default'
@staticmethod
def allow_relation(obj1, obj2, **hints):
return True
@staticmethod
def allow_migrate(db, app_label, model_name=None, **hints):
return True
db_for_read
함수에서 ‘default’와 ‘replica’에 반반씩 보낼 수 있도록 설정 되어 있다.import random
from django.db import transaction
class Router:
@staticmethod
def db_for_read(model, **hints):
conn = transaction.get_connection('default')
if conn.is_atomic_block:
return 'default'
databases = ['default', 'replica']
return random.choice(databases)
@staticmethod
def db_for_write(model, **hints):
return 'default'
@staticmethod
def allow_relation(obj1, obj2, **hints):
return True
@staticmethod
def allow_migrate(db, app_label, model_name=None, **hints):
return True
import random
from django.db import transaction
class Router:
@staticmethod
def db_for_read(model, **hints):
conn = transaction.get_connection('default')
if conn.is_atomic_block:
return 'default'
# replica가 추가되었을 때 replica를 databases에 추가한다.
databases = ['default', 'replica', 'replica']
return random.choice(databases)
@staticmethod
def db_for_write(model, **hints):
return 'default'
@staticmethod
def allow_relation(obj1, obj2, **hints):
return True
@staticmethod
def allow_migrate(db, app_label, model_name=None, **hints):
return True
2018년 11월에 추가된 기능이다.
custom endpoint는 내가 추가하고 거기에 물려 있는 인스턴스들을 마음대로 조정할 수 있다.
다양한 구성으로 원하는 대로 endpoint를 조절할 수 있음
그러면 아래와 같이 Router 세팅이 간단해진다.
import random
from django.db import transaction
class Router:
@staticmethod
def db_for_read(model, **hints):
conn = transaction.get_connection('default')
if conn.is_atomic_block: # 트랜잭션 내에서 발생하는 리드 쿼리는 writer가 처리하도록 한다.
return 'default'
return 'custom' # 미리 DATABSES에 정의해 놓은 'custom'만 리턴하면 된다.
@staticmethod
def db_for_write(model, **hints):
return 'default'
@staticmethod
def allow_relation(obj1, obj2, **hints):
return True
@staticmethod
def allow_migrate(db, app_label, model_name=None, **hints):
return True
import random
from django.db import transaction
class Router:
@staticmethod
def db_for_read(model, **hints):
# 3rd party library에 따라 DB 분산
if model.__meta.app_label == 'oauth2_provider':
return 'default'
if model.__meta.app_label == 'django_cache':
return 'default'
conn = transaction.get_connection('default')
if conn.is_atomic_block:
return 'default'
return 'custom'
참고영상
Django DB Router로 Database Read Replicas 100% 활용기 및 Troubleshooting 경험 공유 - 한종원 - PyCon.KR 2019