es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿El cursor.fetchmany() de psycopg2 verá transacciones confirmadas concurrentes?

Considere el siguiente código:

import psycopg2

conn = psycopg2.connect(**credenciales)
cur = conn.cursor()
cur.execute('select * from some_table')  # Imaginemos some_table como una tabla muy grande
while True:
    rows = cur.fetchmany(1000)
    if not rows:
        break
    realizar_procesamiento(rows)

cur.close()
conn.commit()

Pregunta 1: si una transacción concurrente inserta nuevas filas en some_table mientras se ejecuta el bucle, ¿se obtendrán las nuevas filas si el nivel de aislamiento de la transacción está configurado en “read committed”?

Pregunta 2: si una transacción concurrente actualiza algunas filas en some_table mientras se ejecuta el bucle, ¿se obtendrán las filas actualizadas si el nivel de aislamiento de la transacción está configurado en “read committed”?

Según la documentación de Postgres:

Read Committed es el nivel de aislamiento predeterminado en PostgreSQL. Cuando una transacción utiliza este nivel de aislamiento, una consulta SELECT (sin una cláusula FOR UPDATE/SHARE) “solo ve datos confirmados antes de que comenzara la consulta”; nunca ve datos no confirmados ni cambios confirmados durante la ejecución de la consulta por transacciones concurrentes. En efecto, una consulta SELECT ve una instantánea de la base de datos a partir del momento en que la consulta comienza a ejecutarse. Sin embargo, SELECT sí ve los efectos de las actualizaciones anteriores ejecutadas dentro de su propia transacción, aunque aún no estén confirmadas. También tenga en cuenta que “dos comandos SELECT sucesivos pueden ver datos diferentes, incluso si se encuentran dentro de una única transacción”, si otras transacciones confirman cambios después de que el primer SELECT comienza y antes de que el segundo SELECT comience.

En el código anterior solo hay una consulta SELECT en la transacción, lo que significa que no hay “comandos SELECT sucesivos”, por lo que mi suposición es que el cursor no verá ninguna nueva inserción/actualización. ¿Es correcto? Si es así, ¿cómo recuerda el cursor el estado antiguo de la base de datos durante todo el tiempo? ¿Qué sucede si el bucle se ejecuta durante varias horas/días? ¿Esta situación causa algún tipo de inflación de disco relacionada con MVCC u otro problema similar?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Respuesta 1: No, las nuevas filas no se obtendrán si el nivel de aislamiento de transacción está configurado en “lectura comprometida”. Según la documentación de Postgres, una consulta SELECT solo ve los datos comprometidos antes de que comenzara la consulta, y nunca ve datos no comprometidos ni cambios comprometidos durante la ejecución de la consulta por transacciones concurrentes. En este caso, el cursor está obteniendo datos en lotes de 1000 filas, por lo que es posible que se inserten nuevas filas en la tabla mientras se ejecuta el bucle, pero estas filas no se obtendrán mediante el cursor si se insertaron después de que se ejecutara la consulta SELECT inicial.

    Respuesta 2: Si una transacción concurrente actualiza algunas filas en some_table mientras se ejecuta el bucle, si las filas actualizadas se obtendrán o no depende de si las actualizaciones se comprometieron antes o después de que se ejecutara la consulta SELECT inicial. Si las actualizaciones se comprometieron antes de la consulta SELECT, entonces el cursor obtendrá las filas actualizadas. Si las actualizaciones se comprometieron después de la consulta SELECT, entonces el cursor no obtendrá las filas actualizadas.

    En cuanto a cómo el cursor “recuerda” el estado anterior de la base de datos durante todo el tiempo, es debido al sistema de Control de Concurrencia de Múltiples Versiones (MVCC) utilizado por Postgres. MVCC permite que múltiples transacciones accedan simultáneamente a los mismos datos sin interferir entre sí. Cuando una transacción comienza, obtiene una instantánea de la base de datos tal como existía en ese momento, y esta instantánea se utiliza durante toda la transacción. Cualquier cambio realizado por otras transacciones después de que se tomó la instantánea no es visible para la transacción actual. Así es como el cursor puede obtener datos de la tabla mientras otras transacciones están insertando o actualizando filas.

    El tiempo de ejecución del bucle no debería causar ninguna expansión de disco relacionada con MVCC porque Postgres administra automáticamente el uso del espacio en disco a través de un proceso llamado VACUUM. Cuando se elimina una fila, su espacio no se recupera de inmediato, sino que se marca como “muerto” y se reutilizará cuando se inserten nuevas filas. Sin embargo, si la tabla tiene un alto nivel de cambio (muchas inserciones, actualizaciones y eliminaciones), entonces puede ser necesario ejecutar manualmente VACUUM con más frecuencia para recuperar espacio en disco.

Comments are closed.