2012-01-20 29 views
19

私は単一のmysqlサーバを使用するFlask SQLAlchemy webappを持っています。データベースのセットアップを拡張して、読み取り専用のスレーブサーバーを用意して、マスタとスレーブの間で読み取りを分散し、マスタdbサーバーへの書き込みを継続できるようにします。読み込みスレーブ読み書きマスター設定

私はオプションのいくつかを見ていると私は、私は、プレーンSQLAlchemyのでこれを行うことができないと信じています。代わりに私のwebappに2つのデータベースハンドルを作成する予定です。マスターとスレーブのdbサーバーのそれぞれに1つずつです。次に、単純なランダム値を使用して、「SELECT」操作用のマスター/スレーブdbハンドルを使用します。

しかしイムこれはSQLAlchemyのを使用して行くための正しい方法であるかどうかわかりません。これを取り除く方法に関する提案/ヒント?前もって感謝します。

答えて

26

私はhttp://techspot.zzzeek.org/2012/01/11/django-style-database-routers-in-sqlalchemy/で私のブログ上でこれを行う方法の例を持っています。基本的には、Sessionを拡張して、クエリごとにマスタまたはスレーブから選択することができます。このアプローチの潜在的な不具合の1つは、6つのクエリを呼び出す1つのトランザクションがある場合、1つのリクエストで両方のスレーブを使用することになるかもしれないということです。また、私が使用した、より明示的に利用範囲を確立し、わずかに少ない魔法のアプローチは、このようなビューを呼び出し可能(それらがフラスコ内で呼び出されているものは何でも)、上のデコレータです:

@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はセッションをアサートしますここに作ることは間違いなく新しいことです。

後続のすべての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 

または変異体は、あなただけでは、コールのための「スレーブ」を使用する場合、これは、それが戻ってセッションへの既存のバインドを復元していることで、「より安全」でありますあなたは物事を逆転させることができます。セッションは、デコレータが書き込み操作のために「マスター」に置く「スレーブ」にバインドされます。その場合にランダムスレーブが必要だった場合、Flaskに何らかの "要求開始"イベントがあった場合は、その時点で設定することができます。

+2

のthnxが、これは多くのことができますzzzeek。 sqlalchemyのすばらしい仕事の功績。 –

+0

Radのコメント、素晴らしいコード例もあります! sqlalchemyがクエリ分析とルートを自動的に行う方法があればいいですが、クエリがtmpテーブルやその他の書き込み操作の原因となるかもしれない世界では、おそらく通常は読み取り専用のものが要求されますクエリを送信する前のバックエンドからのクエリプランは、ほとんどの場合価値があるよりも面倒です。 –

+1

私たちは "クエリ分析"オプションを持っていますが、あなた自身で分析を書く必要があります。水平シャーディングシステムは、この種の技術の例を示しています.http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/horizo​​ntal_shard.htmlを参照してください。 – zzzeek

0

また、別の方法で試すこともできます。我々はインスタンスの属性はすべて同じだが、__bind__というクラス属性を持つ2つの異なるクラスを宣言することができる。したがって、私たちは/書き込みと読み取り専用に行うためのRクラスを読んでやってRWクラスを使用することができます。 :)

私はこの方法がより簡単で信頼できると思います。 :)

我々は同じ名前を持つ2つの異なるDBにテーブルを持つことができるので、我々は2デシベルモデルを宣言します。このようにして、2つのモデルが同じ__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

あなたは、異なる読み取りと書き込みのアクセス可能性を持つ2つのデータベースの作成、定義、使用の例を提供することで、 –

関連する問題