python - postgres - sqlalchemy password
¿Cómo se ordena a SQLAlchemy ORM ejecutar múltiples consultas en paralelo cuando se cargan relaciones? (2)
Lo primero que debe hacer es verificar qué consultas se están ejecutando realmente en el db. No asumiría que SQLAlchemy está haciendo lo que espera a menos que esté muy familiarizado con él. Puede usar echo=True
en la configuración de su motor o ver algunos registros de db (no estoy seguro de cómo hacerlo con mysql).
Usted mencionó que está utilizando diferentes estrategias de carga, así que supongo que ha leído los documentos que contiene ( http://docs.sqlalchemy.org/en/latest/orm/loading_relationships.html ). Por lo que estás haciendo, probablemente recomendaría la carga de subconsulta, pero depende totalmente de la cantidad de filas / columnas con las que estés tratando. En mi experiencia, es un buen punto de partida general.
Una cosa a tener en cuenta es que puede necesitar algo como:
db.query(Thing).options(subqueryload(''A'').subqueryload(''B'')).filter(Thing.id==x).first()
Con filter.first
rather that get
, ya que este último caso no volverá a ejecutar consultas de acuerdo con su estrategia de carga si el objeto primario ya se encuentra en el mapa de identidad.
Finalmente, no conozco sus datos, pero esos números parecen bastante abismales para algo que no sea un gran conjunto de datos. Verifique que tenga los índices correctos especificados en todas sus tablas.
Es posible que ya haya pasado por todo esto, pero según la información que ha proporcionado, parece que necesita trabajar más para reducir el problema. ¿Es el esquema db o las consultas que está ejecutando SQLA?
De cualquier manera, diría "no" a la ejecución de múltiples consultas en diferentes conexiones. Cualquier intento de hacer eso podría ocasionar que la aplicación de datos vuelva a mostrar datos incoherentes, y si cree que ahora tiene problemas ... :-)
Estoy usando el ORM de SQLAlchemy. Tengo un modelo que tiene múltiples relaciones de muchos a muchos:
User
User <--MxN--> Organization
User <--MxN--> School
User <--MxN--> Credentials
Estoy implementando estas utilizando tablas de asociación , por lo que también hay tablas User_to_Organization, User_to_School y User_to_Credentials que no uso directamente.
Ahora, cuando intento cargar un único Usuario (usando su identificador PK) y sus relaciones (y modelos relacionados) usando la carga ansiosa unida, obtengo un rendimiento horrible (más de 15 segundos). Supongo que esto se debe a este problema :
Cuando se utilizan niveles múltiples de profundidad con la carga unida o subconsulta, la carga de colecciones dentro de las colecciones multiplicará la cantidad total de filas obtenidas de forma cartesiana. Ambas formas de carga ansiosa siempre se unen desde la clase principal original.
Si presento otro nivel o dos a la jerarquía:
Organization <--1xN--> Project
School <--1xN--> Course
Project <--MxN--> Credentials
Course <--MxN--> Credentials
La consulta tarda más de 50 segundos en completarse, aunque la cantidad total de registros en cada tabla es bastante pequeña.
Con la carga diferida, debo cargar manualmente cada relación, y hay múltiples viajes redondos al servidor.
por ejemplo, Operaciones, ejecutadas en serie como consultas:
- Obtener usuario
- Obtener organizaciones de usuario
- Obtener las escuelas de usuario
- Obtener las credenciales del usuario
- Para cada organización, obtenga sus Proyectos
- Para cada escuela, obtenga sus cursos
- Para cada proyecto, obtenga sus credenciales
- Para cada curso, obtenga sus credenciales
Aún así, todo termina en menos de 200ms.
Me preguntaba si de todos modos hay que usar carga diferida, pero realizar las consultas de carga de relación en paralelo. Por ejemplo, usando el módulo concurrent
, asyncio
o usando gevent
.
por ejemplo, paso 1 (en paralelo):
- Obtener usuario
- Obtener organizaciones de usuario
- Obtener las escuelas de usuario
- Obtener las credenciales del usuario
Paso 2 (en paralelo):
- Para cada organización, obtenga sus Proyectos
- Para cada escuela, obtenga sus cursos
Paso 3 (en paralelo):
- Para cada proyecto, obtenga sus credenciales
- Para cada curso, obtenga sus credenciales
De hecho, en este punto, hacer una carga tipo subconsulta también puede funcionar, es decir, devolver Organization and OrganizationID / Project / Credentials en dos consultas separadas:
por ejemplo, paso 1 (en paralelo):
- Obtener usuario
- Obtener organizaciones de usuario
- Obtener las escuelas de usuario
- Obtener las credenciales del usuario
Paso 2 (en paralelo):
- Obtener organizaciones
- Obtener escuelas
- Obtenga los proyectos de las organizaciones, únase a las credenciales
- Obtenga los cursos de las escuelas, únase a Credenciales
MySQL no tiene paralelismo en una sola conexión. Para el ORM hacer eso requeriría conexiones múltiples a MySQL. En general, la sobrecarga de intentar hacer tal "no vale la pena".
Para obtener un user
, sus Organizations
, Schools
, etc., pueden hacerse (en mysql) a través de una única consulta:
SELECT user, organization, ...
FROM Users
JOIN Organizations ON ...
etc.
Esto es significativamente más eficiente que
SELECT user FROM ...;
SELECT organization ... WHERE user = ...;
etc.
(Esto no es "paralelismo").
¿O tal vez sus "pasos" no son del todo "correctos"? ...
SELECT user, organization, project
FROM Users
JOIN Organizations ...
JOIN Projects ...
Eso consigue, en un solo paso, todos los usuarios, junto con todas sus organizaciones y proyectos.
¿Pero está un "usuario" asociado a un "proyecto"? Si no, entonces este es el enfoque equivocado.
Si el ORM no proporciona un mecanismo para generar consultas como esas, entonces está "interponiéndose".