27

単一のmysqlサーバーを使用するFlask、SQLAlchemyWebアプリがあります。データベースのセットアップを拡張して、読み取り専用のスレーブサーバーを作成し、マスターデータベースサーバーへの書き込みを続行しながら、マスターとスレーブの両方に読み取りを分散できるようにします。

私はいくつかのオプションを見てきましたが、プレーンなSQLAlchemyではこれを行うことができないと思います。代わりに、Webアプリケーションに2つのデータベースハンドルを作成することを計画しています。1つはマスターデータベースサーバーとスレーブデータベースサーバー用です。次に、単純なランダム値を使用して、「SELECT」操作にマスター/スレーブデータベースハンドルを使用します。

ただし、これがSQLAlchemyを使用する正しい方法であるかどうかはわかりません。これをやってのける方法についての提案/ヒントはありますか?

4

2 に答える 2

41

これを行う方法の例は、ブログhttp://techspot.zzzeek.org/2012/01/11/django-style-database-routers-in-sqlalchemy/にあります。基本的に、セッションを拡張して、クエリごとにマスターまたはスレーブから選択することができます。このアプローチの潜在的な問題の1つは、6つのクエリを呼び出すトランザクションが1つある場合、1つのリクエストで両方のスレーブを使用することになる可能性があることです。しかし、Djangoの機能を模倣しようとしているだけです:)

私が使用した使用範囲をより明確に確立する、少し魔法の少ないアプローチは、次のように、ビューの呼び出し可能オブジェクト(Flaskで呼び出されるものは何でも)のデコレータです。

@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に何らかの「リクエスト開始」イベントがあれば、その時点で設定できます。

于 2012-01-24T01:36:21.727 に答える
2

または、別の方法を試すこともできます。たとえば、すべてのインスタンス属性が同じであるが、__bind__クラス属性が異なる2つの異なるクラスを宣言できます。したがって、rwクラスを使用して読み取り/書き込みを実行し、 rクラスを使用して読み取り専用を実行できます。:)

この方法の方が簡単で信頼性が高いと思います。:)

同じ名前の2つの異なるデータベースにテーブルを作成できるため、2つのデータベースモデルを宣言します。このようにして、同じ__tablename__を持つ2つのモデルの場合に「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'    
于 2016-05-12T03:49:22.063 に答える