tutorial query espaƱol python performance orm sqlalchemy

python - query - SQLAlchemy: Escanee tablas enormes usando ORM?



sqlalchemy query (3)

Actualmente estoy jugando con SQLAlchemy un poco, que es realmente bastante limpio.

Para las pruebas creé una gran tabla que contiene el archivo de mis imágenes, indexado por hash SHA1 (para eliminar duplicados :-)). Que fue impresionantemente rápido ...

Por diversión hice el equivalente de un select * sobre la base de datos SQLite resultante:

session = Session() for p in session.query(Picture): print(p)

Esperaba ver hashes desplazándose, pero en su lugar simplemente siguió escaneando el disco. Al mismo tiempo, el uso de memoria se disparaba, alcanzando 1GB después de unos segundos. Esto parece provenir de la función de mapa de identidad de SQLAlchemy, que pensé que solo conservaba referencias débiles.

¿Puede alguien explicarme esto? ¡Pensé que cada foto p se recolectaría después de que se haya escrito el hash !?


De acuerdo, acabo de encontrar la manera de hacerlo yo mismo. Cambiar el código a

session = Session() for p in session.query(Picture).yield_per(5): print(p)

carga solo 5 imágenes a la vez. Parece que la consulta cargará todas las filas a la vez de forma predeterminada. Sin embargo, todavía no entiendo la exención de responsabilidad sobre ese método. Cita de los documentos de SQLAlchemy

ADVERTENCIA: use este método con precaución; si la misma instancia está presente en más de un lote de filas, los cambios de los usuarios finales en los atributos se sobrescribirán. En particular, generalmente es imposible usar esta configuración con colecciones cargadas ansiosamente (es decir, cualquier lazy = False) ya que esas colecciones se borrarán para una carga nueva cuando se encuentren en un lote de resultados posterior.

Entonces, si el uso de yield_per es realmente la forma correcta (tm) de escanear cantidades copiosas de datos de SQL mientras se usa el ORM, ¿cuándo es seguro usarlo?


Puede aplazar la imagen para que solo se recupere al acceder. Puede hacerlo en una consulta por consulta. me gusta

session = Session() for p in session.query(Picture).options(sqlalchemy.orm.defer("picture")): print(p)

o puedes hacerlo en el mapeador

mapper(Picture, pictures, properties={ ''picture'': deferred(pictures.c.picture) })

Cómo lo haces está en la documentación here

Si lo hace de cualquier manera, se asegurará de que la imagen solo se cargue cuando acceda al atributo.


esto es lo que suelo hacer en esta situación:

def page_query(q): offset = 0 while True: r = False for elem in q.limit(1000).offset(offset): r = True yield elem offset += 1000 if not r: break for item in page_query(Session.query(Picture)): print item

Esto evita los diversos almacenamientos intermedios que también hacen los DBAPI (como psycopg2 y MySQLdb). Todavía necesita ser usado apropiadamente si su consulta tiene uniones explícitas, aunque se espera que las colecciones cargadas ansiosamente se carguen completamente ya que se aplican a una subconsulta que tiene el LIMIT / OFFSET real provisto.

Me he dado cuenta de que Postgresql tarda casi tanto en devolver las últimas 100 filas de un gran conjunto de resultados como lo hace para devolver el resultado completo (menos la tara real de recolección de filas), ya que OFFSET solo hace un simple escaneo de todo el asunto.