2012-01-20 2 views
19

나는 하나의 mysql 서버를 사용하는 Flask, SQLAlchemy webapp를 가지고있다. 데이터베이스 설정을 확장하여 읽기 전용 슬레이브 서버를 갖춰 마스터 db 서버에 쓰기를 계속하면서 마스터와 슬레이브간에 읽기를 분산시킬 수 있습니다.읽기 슬레이브, 읽기 - 쓰기 마스터 설정

나는 몇 가지 옵션을 살펴 봤지만 일반 SQLAlchemy로는이 작업을 수행 할 수 없다고 생각합니다. 대신 내 웹 응용 프로그램에서 마스터 데이터베이스와 슬레이브 db 서버 각각에 대해 2 개의 데이터베이스 핸들을 만들 계획입니다. 그런 다음 간단한 무작위 값을 사용하여 "SELECT"조작에 대한 마스터/슬레이브 db 핸들을 사용하십시오.

그러나 이것이 SQLAlchemy를 사용하는 올바른 방법인지 확실하지 않습니다. 이걸 풀어내는 방법에 대한 제안이나 조언? 미리 감사드립니다.

답변

26

나는 내 블로그 http://techspot.zzzeek.org/2012/01/11/django-style-database-routers-in-sqlalchemy/에서이를 수행하는 방법에 대한 예제를 가지고 있습니다. 기본적으로 세션을 향상시켜 쿼리 단위로 마스터 또는 슬레이브에서 선택하도록 할 수 있습니다. 이 접근법의 잠재적 인 결함 중 하나는 6 개의 쿼리를 호출하는 하나의 트랜잭션이있는 경우 한 요청에서 두 슬레이브 모두를 사용하여 종료 될 수 있다는 것입니다. 그러나 여기에는 장고의 기능을 모방하려고합니다.

A 약간 또한 더 명시 적으로 내가 사용했습니다 사용의 범위를 설정 덜 마법의 접근 방식은 (그들이 플라스크에서 호출하든)보기 callables에 장식은 다음과 같이이다 :

@with_slave 
def my_view(...): 
    # ... 

with_slave이 같은 것을 할 것입니다, 세션이 있고 일부 엔진이 설정되었다고 가정 할 경우 :

master = create_engine("some DB") 
slave = create_engine("some other DB") 
Session = scoped_session(sessionmaker(bind=master)) 

def with_slave(fn): 
    def go(*arg, **kw): 
     s = Session(bind=slave) 
     return fn(*arg, **kw) 
    return go 

아이디어는 Session(bind=slave)을 호출하면 현재 스레드에 대한 실제 Session 객체를 얻기 위해 레지스트리를 호출하고 존재하지 않는 경우 레지스트리를 생성한다는 것입니다. 그러나 인수를 전달하기 때문에 scoped_session은 우리가 가진 Session 여기 만드는 것은 확실히 새로운 것입니다.

모든 후속 SQL에 대해 "슬레이브"를 가리 키십시오. 그런 다음 요청이 끝나면 Flask 앱이 Session.remove()을 호출하여 해당 스레드의 레지스트리를 지우도록해야합니다. 레지스트리가 다음에 동일한 스레드에서 사용될 때 "마스터"에 바인드 된 새 세션이됩니다. 이러한 장식의 각각에 대해

def with_slave(fn): 
    def go(*arg, **kw): 
     s = Session() 
     oldbind = s.bind 
     s.bind = slave 
     try: 
      return fn(*arg, **kw) 
     finally: 
      s.bind = oldbind 
    return go 

:

또는 변형, 당신은 그냥 전화의 "노예"를 사용하려면,이는 다시 세션에 대한 기존의 바인딩을 복원하도록에서 "안전"입니다 당신은 일을 뒤집을 수 있고, Session이 쓰기 작업을 위해 "master"에 놓는 "slave"에 바인드되게 할 수 있습니다. 이 경우 랜덤 슬레이브를 원한다면, Flask에 어떤 종류의 "요청 시작"이벤트가 있다면 그 시점에서 설정할 수 있습니다.

+2

가 Thnx zzzeek이 많은 도움이 : 여기

은 예입니다. sqlalchemy에 대한 모든 멋진 작업에 대한 명성. –

+0

Rad 주석, 훌륭한 코드 예제! sqlalchemy가 쿼리 분석과 자동 라우팅을 할 수있는 방법이 있다면 좋겠지 만 쿼리가 tmp 테이블이나 다른 쓰기 작업을 일으킬 수있는 세계에서 아마도 일반적으로 읽기만 가능한 것의 결과로 요청하는 것과 같은 것이 필요할 것입니다 쿼리를 제출하기 전에 백엔드에서 쿼리 계획을 수립하면 대부분의 경우 가치가있는 것보다 더 번거롭지 않게됩니다. –

+1

"쿼리 분석"옵션이 있지만 분석을 직접 작성해야합니다. 수평 샤딩 시스템은 이러한 종류의 기술에 대한 예를 보여줍니다. http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/horizontal_shard.html을 참조하십시오. – zzzeek

0

또는 다른 방법을 시도해 볼 수 있습니다. 우리가 같은 모든 인스턴스 특성을 가진 두 개의 서로 다른 클래스를 선언 할 수 있지만 같은 __bind__ 클래스 특성이 다릅니다. 따라서 rw 클래스는 읽기/쓰기가 가능하고 r 클래스는 읽기 전용으로 사용할 수 있습니다. :)

나는이 방법이 더 쉽고 신뢰성 있다고 생각한다. :)

두 개의 다른 db에 같은 이름의 테이블을 가질 수 있기 때문에 두 개의 db 모델을 선언합니다. 이 방법을 사용하면 동일한 모델의 __tablename__ 두 개의 모델을 사용할 때 'extend_existing'오류도 무시할 수 있습니다.

app = Flask(__name__) 
app.config['SQLALCHEMY_BINDS'] = {'rw': 'rw', 'r': 'r'} 
db = SQLAlchemy(app) 
db.Model_RW = db.make_declarative_base() 

class A(db.Model): 
    __tablename__ = 'common' 
    __bind_key__ = 'r' 

class A(db.Model_RW): 
    __tablename__ = 'common' 
    __bind_key__ = 'rw'  
+0

다른 읽기 및 쓰기 액세스 가능성을 가진 두 데이터베이스의 생성, 정의 및 사용 예제를 제공하여 대답을 향상시킬 수 있습니까? –