mysqldb instalar insertar datos consultar con python mysql mysql-python

instalar - mysql connector python



Cuándo cerrar los cursores usando MySQLdb (5)

Creo que será mejor que intentes utilizar un cursor para todas tus ejecuciones y lo cierres al final de tu código. Es más fácil trabajar con él, y también podría tener beneficios de eficiencia (no me cites en eso).

conn = MySQLdb.connect("host","user","pass","database") cursor = conn.cursor() cursor.execute("somestuff") results = cursor.fetchall() ..do stuff with results cursor.execute("someotherstuff") results2 = cursor.fetchall() ..do stuff with results2 cursor.close()

El punto es que puede almacenar los resultados de la ejecución de un cursor en otra variable, liberando así su cursor para hacer una segunda ejecución. Te encuentras con problemas de esta manera solo si estás usando fetchone (), y necesitas hacer una segunda ejecución del cursor antes de iterar a través de todos los resultados de la primera consulta.

De lo contrario, diría que simplemente cierre sus cursores tan pronto como haya terminado de obtener todos los datos de ellos. De esta forma, no tendrá que preocuparse por atar cabos sueltos más adelante en su código.

Estoy construyendo una aplicación web WSGI y tengo una base de datos MySQL. Estoy usando MySQLdb, que proporciona cursores para ejecutar declaraciones y obtener resultados. ¿Cuál es la práctica estándar para obtener y cerrar cursores? En particular, ¿cuánto tiempo deben durar mis cursores? ¿Debo obtener un nuevo cursor para cada transacción?

Creo que debes cerrar el cursor antes de comprometer la conexión. ¿Hay alguna ventaja significativa al encontrar conjuntos de transacciones que no requieren confirmaciones intermedias para que no tenga que obtener nuevos cursores para cada transacción? ¿Hay muchos gastos generales para obtener nuevos cursores, o simplemente no es un gran problema?


Es mejor reescribirlo usando la palabra clave ''with''. ''With'' se ocupará de cerrar el cursor (es importante porque es un recurso no administrado) automáticamente. El beneficio es que cerrará el cursor en caso de excepción también.

from contextlib import closing import MySQLdb '''''' At the beginning you open a DB connection. Particular moment when you open connection depends from your approach: - it can be inside the same function where you work with cursors - in the class constructor - etc '''''' db = MySQLdb.connect("host", "user", "pass", "database") with closing(db.cursor()) as cur: cur.execute("somestuff") results = cur.fetchall() # do stuff with results cur.execute("insert operation") # call commit if you do INSERT, UPDATE or DELETE operations db.commit() cur.execute("someotherstuff") results2 = cur.fetchone() # do stuff with results2 # at some point when you decided that you do not need # the open connection anymore you close it db.close()


Sugiero hacerlo como php y mysql. Comience i al principio de su código antes de imprimir los primeros datos. Entonces, si obtiene un error de conexión, puede mostrar un mensaje de error de 50x (No recuerdo qué error interno es). Y manténgala abierta durante toda la sesión y ciérrela cuando sepa que ya no la necesitará.


Nota: esta respuesta es para PyMySQL , que es un reemplazo directo para MySQLdb y efectivamente la última versión de MySQLdb desde que MySQLdb dejó de ser mantenido. Creo que todo aquí también es cierto para el legado MySQLdb, pero no lo he comprobado.

Primero que nada, algunos hechos:

  • Python with sintaxis llama al método __enter__ del administrador de __enter__ antes de ejecutar el cuerpo del bloque with , y su método __exit__ luego.
  • Las conexiones tienen un método __enter__ que no hace nada además de crear y devolver un cursor, y un método __exit__ que confirma o retrotrae (dependiendo de si se lanzó una excepción). No cierra la conexión.
  • Los cursores en PyMySQL son puramente una abstracción implementada en Python; no hay un concepto equivalente en MySQL. 1
  • Los cursores tienen un método __enter__ que no hace nada y un método __exit__ que "cierra" el cursor (lo que simplemente significa anular la referencia del cursor a su conexión principal y descartar cualquier información almacenada en el cursor).
  • Los cursores contienen una referencia a la conexión que los engendró, pero las conexiones no contienen una referencia a los cursores que han creado.
  • Las conexiones tienen un método __del__ que las cierra
  • Según https://docs.python.org/3/reference/datamodel.html , CPython (la implementación de Python predeterminada) utiliza el recuento de referencias y borra automáticamente un objeto una vez que el número de referencias llega a cero.

Al unir estas cosas, vemos que un código ingenuo como este es en teoría problemático:

# Problematic code, at least in theory! import pymysql with pymysql.connect() as cursor: cursor.execute(''SELECT 1'') # ... happily carry on and do something unrelated

El problema es que nada ha cerrado la conexión. De hecho, si pega el código de arriba en un shell de Python y luego ejecuta SHOW FULL PROCESSLIST en un shell de MySQL, podrá ver la conexión inactiva que ha creado. Como el número predeterminado de conexiones de MySQL es 151 , que no es enorme , en teoría podría comenzar a tener problemas si tuviera muchos procesos manteniendo estas conexiones abiertas.

Sin embargo, en CPython, hay una tolerancia de ahorro que garantiza que un código como el de mi ejemplo anterior probablemente no te obligue a salir de un montón de conexiones abiertas. Esa gracia salvadora es que tan pronto como el cursor sale del ámbito (por ejemplo, la función en la que se creó finaliza, o el cursor obtiene otro valor asignado), su recuento de referencia llega a cero, lo que hace que se elimine, eliminando la conexión la cuenta de referencia es cero, lo que hace que se __del__ método de conexiones __del__ que fuerza la conexión. Si ya pegó el código anterior en su shell de Python, ahora puede simular esto ejecutando cursor = ''arbitrary value'' ; tan pronto como lo haga, la conexión que abrió se desvanecerá de la salida SHOW PROCESSLIST .

Sin embargo, confiar en esto no es elegante, y teóricamente podría fallar en las implementaciones de Python que no sean CPython. Cleaner, en teoría, sería explícitamente .close() la conexión (para liberar una conexión en la base de datos sin esperar a que Python destruya el objeto). Este código más robusto se ve así:

import contextlib import pymysql with contextlib.closing(pymysql.connect()) as conn: with conn as cursor: cursor.execute(''SELECT 1'')

Esto es feo, pero no depende de que Python destruya sus objetos para liberar su (número de disponibilidad finito de) conexiones de base de datos.

Tenga en cuenta que cerrar el cursor , si ya está cerrando la conexión explícitamente así, no tiene sentido.

Finalmente, para responder a las preguntas secundarias aquí:

¿Hay muchos gastos generales para obtener nuevos cursores, o simplemente no es un gran problema?

No, la creación de instancias de un cursor no afecta a MySQL en absoluto y básicamente no hace nada .

¿Hay alguna ventaja significativa al encontrar conjuntos de transacciones que no requieren confirmaciones intermedias para que no tenga que obtener nuevos cursores para cada transacción?

Esto es situacional y difícil de dar una respuesta general a. Como dice https://dev.mysql.com/doc/refman/en/optimizing-innodb-transaction-management.html , "una aplicación puede enfrentar problemas de rendimiento si se comete miles de veces por segundo y problemas de rendimiento diferentes si se compromete solo cada 2-3 horas " . Usted paga una sobrecarga de rendimiento por cada confirmación, pero al dejar las transacciones abiertas por más tiempo, aumenta las posibilidades de que otras conexiones tengan que esperar bloqueo, aumentar el riesgo de interbloqueos y, posiblemente, aumentar el costo de algunas búsquedas realizadas por otras conexiones. .

1 MySQL tiene una construcción que llama cursor pero solo existe dentro de procedimientos almacenados; son completamente diferentes a los cursores PyMySQL y no son relevantes aquí.


En lugar de preguntar cuál es la práctica estándar, ya que a menudo eso no está claro y es subjetivo, puede intentar consultar el módulo mismo para obtener orientación. En general, usar la palabra clave with como otro usuario sugirió es una gran idea, pero en esta circunstancia específica puede que no le brinde la funcionalidad que espera.

A partir de la versión 1.2.5 del módulo, MySQLdb.Connection implementa el protocolo del gestor de contexto con el siguiente código ( github ):

def __enter__(self): if self.get_autocommit(): self.query("BEGIN") return self.cursor() def __exit__(self, exc, value, tb): if exc: self.rollback() else: self.commit()

Ya hay varias preguntas y respuestas, o puede leer la declaración "with" de Understanding Python , pero esencialmente lo que ocurre es que __enter__ ejecuta al comienzo del bloque with , y __exit__ ejecuta al salir del bloque with . Puede utilizar la sintaxis opcional with EXPR as VAR para vincular el objeto devuelto por __enter__ a un nombre si tiene la intención de hacer referencia a ese objeto más adelante. Entonces, dada la implementación anterior, aquí hay una manera simple de consultar su base de datos:

connection = MySQLdb.connect(...) with connection as cursor: # connection.__enter__ executes at this line cursor.execute(''select 1;'') result = cursor.fetchall() # connection.__exit__ executes after this line print result # prints "((1L,),)"

La pregunta ahora es, ¿cuáles son los estados de la conexión y el cursor después de salir del bloque with ? El método __exit__ que se muestra arriba solo llama a self.rollback() o self.commit() , y ninguno de esos métodos llama al método close() . El cursor en sí no tiene definido el método __exit__ , y no importaría si lo hiciera, porque with solo está administrando la conexión. Por lo tanto, tanto la conexión como el cursor permanecen abiertos después de salir del bloque with . Esto se confirma fácilmente agregando el siguiente código al ejemplo anterior:

try: cursor.execute(''select 1;'') print ''cursor is open;'', except MySQLdb.ProgrammingError: print ''cursor is closed;'', if connection.open: print ''connection is open'' else: print ''connection is closed''

Debería ver que la salida "el cursor está abierto, la conexión está abierta" impresa en stdout.

Creo que debes cerrar el cursor antes de comprometer la conexión.

¿Por qué? La API de MySQL C , que es la base de MySQLdb , no implementa ningún objeto de cursor, como se indica en la documentación del módulo: "MySQL no es compatible con los cursores, sin embargo, los cursores se emulan fácilmente". De hecho, la clase MySQLdb.cursors.BaseCursor hereda directamente del object y no impone tal restricción en los cursores con respecto a commit / rollback. Un desarrollador de Oracle dijo lo siguiente :

cnx.commit () antes de cur.close () suena más lógico para mí. Tal vez pueda seguir la regla: "Cierre el cursor si ya no lo necesita". Por lo tanto, commit () antes de cerrar el cursor. Al final, para Connector / Python, no hace mucha diferencia, pero u otras bases de datos podrían serlo.

Espero que sea lo más cercano a la "práctica estándar" sobre este tema.

¿Hay alguna ventaja significativa al encontrar conjuntos de transacciones que no requieren confirmaciones intermedias para que no tenga que obtener nuevos cursores para cada transacción?

Lo dudo mucho, y al tratar de hacerlo, puede introducir un error humano adicional. Mejor decidir una convención y seguir con ella.

¿Hay muchos gastos generales para obtener nuevos cursores, o simplemente no es un gran problema?

La sobrecarga es insignificante, y no toca el servidor de la base de datos en absoluto; está completamente dentro de la implementación de MySQLdb. Puedes mirar BaseCursor.__init__ en github si realmente sientes curiosidad por saber qué está sucediendo cuando creas un nuevo cursor.

Volviendo a lo anterior cuando estábamos discutiendo, quizás ahora usted pueda entender por qué los MySQLdb.Connection clase __enter__ y __exit__ dan un objeto de cursor completamente nuevo en cada bloque y no se molesta en seguirlo o cerrarlo en el Fin del bloque. Es bastante liviano y existe exclusivamente para su conveniencia.

Si realmente es tan importante para usted microgestionar el objeto del cursor, puede usar contextlib.closing para compensar el hecho de que el objeto del cursor no tiene definido el método __exit__ . Para el caso, también puede usarlo para forzar al objeto de conexión a cerrarse al salir de un bloque with . Esto debería generar "my_curs está cerrado; my_conn está cerrado":

from contextlib import closing import MySQLdb with closing(MySQLdb.connect(...)) as my_conn: with closing(my_conn.cursor()) as my_curs: my_curs.execute(''select 1;'') result = my_curs.fetchall() try: my_curs.execute(''select 1;'') print ''my_curs is open;'', except MySQLdb.ProgrammingError: print ''my_curs is closed;'', if my_conn.open: print ''my_conn is open'' else: print ''my_conn is closed''

Tenga en cuenta que with closing(arg_obj) no se invocarán los métodos __enter__ y __exit__ del objeto del argumento; solo llamará al método de close del objeto del argumento al final del bloque with . (Para ver esto en acción, simplemente defina una clase Foo con __enter__ , __exit__ , y close métodos que contengan instrucciones simples de print , y compare lo que sucede cuando lo hace with Foo(): pass a lo que sucede cuando lo hace with closing(Foo()): pass ). Esto tiene dos implicaciones importantes:

Primero, si el modo de confirmación automática está habilitado, MySQLdb INICIARÁ una transacción explícita en el servidor cuando lo utilice with connection y confirme o retrotraiga la transacción al final del bloque. Estos son los comportamientos predeterminados de MySQLdb, destinados a protegerlo del comportamiento predeterminado de MySQL de comprometer inmediatamente todas y cada una de las sentencias DML. MySQLdb supone que cuando utiliza un administrador de contexto, desea una transacción y utiliza el BEGIN explícito para omitir la configuración de confirmación automática en el servidor. Si está acostumbrado a usar la with connection , puede pensar que el autocommit está desactivado cuando en realidad solo se está pasando por alto. Puede obtener una sorpresa desagradable si agrega el closing a su código y pierde integridad transaccional; no podrá deshacer los cambios, puede comenzar a ver errores de concurrencia y puede no ser inmediatamente obvio por qué.

Segundo, with closing(MySQLdb.connect(user, pass)) as VAR vincula el objeto de conexión a VAR , en contraste with MySQLdb.connect(user, pass) as VAR , que vincula un nuevo objeto de cursor a VAR . ¡En este último caso, no tendría acceso directo al objeto de conexión! En su lugar, tendría que usar el atributo de connection del cursor, que proporciona acceso proxy a la conexión original. Cuando el cursor está cerrado, su atributo de connection se establece en None . Esto da como resultado una conexión abandonada que se mantendrá hasta que ocurra una de las siguientes situaciones:

  • Se eliminan todas las referencias al cursor
  • El cursor sale del alcance
  • La conexión expira
  • La conexión se cierra manualmente a través de las herramientas de administración del servidor

Puede probar esto supervisando las conexiones abiertas (en Workbench o usando SHOW PROCESSLIST ) mientras ejecuta las siguientes líneas una por una:

with MySQLdb.connect(...) as my_curs: pass my_curs.close() my_curs.connection # None my_curs.connection.close() # throws AttributeError, but connection still open del my_curs # connection will close here