0

SQLAlchemy を使用してゲームサーバーを作成しています。

ゲームサーバーは非常に高速である必要があるため、ユーザー ID (整数) に応じてデータベースを分離することにしました。

たとえば、次のように成功しました。

from threading import Thread
from sqlalchemy import Column, Integer, String, DateTime, create_engine
from sqlalchemy.ext.declarative import declarative_base, DeferredReflection
from sqlalchemy.orm import sessionmaker

DeferredBase = declarative_base(cls=DeferredReflection)
class BuddyModel(DeferredBase):
    __tablename__ = 'test_x'

    id = Column(Integer(), primary_key=True, autoincrement=True)
    value = Column(String(50), nullable=False)

次のコードは複数のデータベースを作成します。

test1 ~ test10 のデータベースがあります。

for i in range(10):
    url = 'mysql://user@localhost/'
    engine = create_engine(url, encoding='UTF-8', pool_recycle=300)
    con = engine.connect()
    con.execute('create database test%d' % i)

次のコードは、10 個の個別のエンジンを作成します。

get_engine() 関数は、ユーザー ID に応じてエンジンを提供します。

(ユーザーIDは整数)

engines = []
for i in range(10):
    url = 'mysql://user@localhost/test%d'% i

    engine = create_engine(url, encoding='UTF-8', pool_recycle=300)

    DeferredBase.metadata.bind = engine
    DeferredBase.metadata.create_all()
    engines.append(engine)

def get_engine(user_id):
    index = user_id%10
    return engines[index]

prepare 関数を実行すると、BuddyModel クラスが準備され、エンジンにマッピングされます。

def prepare(user_id):
    engine = get_engine(user_id)
    DeferredBase.prepare(engine)

** 次のコードは、私がやりたいことを正確に実行します **

for user_id in range(100):
    prepare(user_id)

    engine = get_engine(user_id)
    session = sessionmaker(engine)()
    buddy = BuddyModel()
    buddy.value = 'user_id: %d' % user_id
    session.add(buddy)
    session.commit()

しかし、問題は、複数のスレッドで実行するとエラーが発生することです

class MetalMultidatabaseThread(Thread):

    def run(self):
        for user_id in range(100):
            prepare(user_id)

            engine = get_engine(user_id)
            session = sessionmaker(engine)()
            buddy = BuddyModel()
            buddy.value = 'user_id: %d' % user_id
            session.add(buddy)
            session.commit()
threads = []
for i in range(100):
    t = MetalMultidatabaseThread()
    t.setDaemon(True)
    t.start()
    threads.append(t)

for t in threads:
    t.join()

エラーメッセージは...

ArgumentError: Class '<class '__main__.BuddyModel'>' already has a primary mapper defined. Use non_primary=True to create a non primary Mapper.  clear_mappers() will remove *all* current mappers from all classes.

だから..私の質問は、SQLAlchemyを使用して上記のアーキテクチャのように複数のデータベースを実行するにはどうすればよいですか?

4

2 に答える 2

1

これは水平シャーディングと呼ばれ、少しトリッキーな使用例です。あなたが持っているバージョンは、最初にエンジンを取得することに基づいてセッションを作成すると、正常に動作します。あなたが好きかもしれないこれの2つの変種があります。

1 つは、水平シャーディング拡張機能を使用することです。この拡張機能を使用すると、正しいノードを自動的に選択するセッションを作成できます。

もう1つは多かれ少なかれあなたが持っているものですが、冗長ではありません。ルーティング機能を持つ Session クラスを構築すると、少なくとも単一のセッションを共有しsession.using_bind('engine1')て、まったく新しいセッションを作成する代わりにクエリを実行できます。

于 2015-01-10T22:52:41.557 に答える
0

私は自分の質問に対する答えを見つけました。

ユーザーID(整数)に応じて複数のデータベースを構築するには、セッションを使用するだけです。

これを説明する前に、データベース アーキテクチャについて詳しく説明したいと思います。

たとえば、ユーザー ID 114 がサーバーに接続する場合、サーバーは次のようなものを使用してユーザーの情報を取得する場所を決定します。

user_id%10 # <-- 4th database 

建築

DATABASES 
  - DB0 <-- save all user data whose ID ends with 0 
  - DB1 <-- save all user data whose ID ends with 1
  .
  .
  .
  - DB8 <-- save all user data whose ID ends with 9

これが答えです

最初に bind パラメータを使用しないでください。単純に空にします。

Base = declarative_base()

モデルを宣言..

class BuddyModel(Base):
    __tablename__ = 'test_x'

    id = Column(Integer(), primary_key=True, autoincrement=True)
    value = Column(String(50), nullable=False)

CRUDをしたい時はセッションを作る

engine = get_engine_by_user_id(user_id)
session = sessionmaker(bind=engine)()

buddy = BuddyModel()
buddy.value = 'This is Sparta!! %d' % user_id
session.add(buddy)
session.commit()

エンジンは、ユーザー ID と一致するものである必要があります。

于 2015-01-08T00:56:08.407 に答える