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]

通过运行准备函数,将准备好 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

这称为水平分片,是一个有点棘手的用例。您拥有的版本,基于首先获取引擎进行会话,将正常工作。您可能会喜欢这两种变体。

一种是使用水平分片扩展。此扩展允许您创建会话以自动选择正确的节点。

另一个或多或少是你所拥有的,但不那么冗长。构建一个具有路由功能的 Session 类,这样您至少可以共享一个会话并说,session.using_bind('engine1')用于查询而不是创建一个全新的会话。

于 2015-01-10T22:52:41.557 回答
0

我找到了我的问题的答案。

要根据 USER ID(整数)构建多个数据库,只需使用 session.

在解释这个之前,我想更多地阐述一下数据库架构。

例如,如果用户 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

这是答案

首先不要使用绑定参数..只需将其设为空即可。

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 回答