Esclavo de lectura, configuración maestra de lectura-escritura

Tengo un Flask, SQLAlchemy webapp que utiliza un único servidor mysql. Quiero expandir la configuración de la base de datos para tener un servidor esclavo de solo lectura, de modo que pueda distribuir las lecturas entre el maestro y el esclavo mientras continúo escribiendo en el servidor maestro de db.

He analizado algunas de las opciones y creo que no puedo hacer esto con SQLAlchemy. En su lugar, estoy planeando crear 2 manejadores de base de datos en mi aplicación web, uno para servidores maestro y esclavo db. Luego, utilizando un valor aleatorio simple, use el controlador de db maestro / esclavo para las operaciones “SELECCIONAR”.

Sin embargo, no estoy seguro de si esta es la forma correcta de usar SQLAlchemy. ¿Alguna sugerencia / consejos sobre cómo llevar esto a cabo? Gracias por adelantado.

Tengo un ejemplo de cómo hacer esto en mi blog en http://techspot.zzzeek.org/2012/01/11/django-style-database-routers-in-sqlalchemy/ . Básicamente, puede mejorar la sesión para que elija entre maestro o esclavo en una base de consulta por consulta. Un problema potencial con este enfoque es que si tienes una transacción que llama a seis consultas, podrías terminar usando ambos esclavos en una sola solicitud … pero simplemente estamos tratando de imitar la característica de Django 🙂

Un enfoque un poco menos mágico que también establece el scope del uso más explícitamente que he usado es un decorador a la vista de callables (como se llame en Flask), como este:

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

with_slave haría algo como esto, asumiendo que tienes una sesión y algunos motores configurados:

 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 

La idea es que llamar a Session(bind=slave) invoca el registro para obtener el objeto Session real para el subproceso actual y lo crea si no existe. Sin embargo, como pasamos un argumento, scoped_session afirmará que la sesión Lo que estamos haciendo aquí es definitivamente nuevo.

Lo apuntas al “esclavo” para todos los SQL subsiguientes. Luego, cuando finalice la solicitud, se asegurará de que su aplicación Flask esté llamando a Session.remove() para borrar el registro de ese hilo. Cuando el registro se utilice a continuación en el mismo hilo, será una nueva sesión vinculada al “maestro”.

O una variante, desea usar el “esclavo” solo para esa llamada, esto es “más seguro” porque restaura cualquier enlace existente a la sesión:

 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 

Para cada uno de estos decoradores puede revertir las cosas, hacer que la Sesión esté vinculada a un “esclavo” donde el decorador la pone en “maestro” para las operaciones de escritura. Si quisiera un esclavo aleatorio en ese caso, si Flask tuviera algún tipo de evento de “solicitud de inicio”, podría configurarlo en ese momento.

O bien, podemos intentarlo de otra manera. Como podemos declarar dos clases diferentes con todos los atributos de instancia iguales pero el __bind__ clase __bind__ es diferente. Por lo tanto, podemos usar la clase rw para hacer lectura / escritura y la clase r para hacer solo lectura. 🙂

Creo que esta manera es más fácil y confiable. 🙂

Declaramos dos modelos de db porque podemos tener tablas en dos db diferentes con los mismos nombres. De esta manera también podemos omitir el error ‘extend_existing’ cuando dos modelos con el mismo __tablename__ .

Aquí hay un ejemplo:

 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'