¿Actualizar una fila con el mismo valor realmente actualiza la fila?


28

Tengo una pregunta relacionada con el rendimiento. Digamos que tengo un usuario con nombre Michael. Tome la siguiente consulta:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123

¿Realmente la consulta ejecutará la actualización, aunque se actualice al mismo valor? Si es así, ¿cómo evito que suceda?


1
¿Por qué ejecutarías una declaración y simultáneamente esperarías que no se ejecute?
Max Vernon

El ORM de @MaxVernon Ruby on Rails no actualiza el registro, así que tenía curiosidad por saber si PostgreSQL hizo lo mismo.
OneSneakyMofo

1
Sugeriría que si Ruby on Rails está haciendo eso, probablemente esté haciendo una selección primero para ver si la fila necesita una actualización.
Max Vernon

Respuestas:


35

Debido al modelo MVCC de Postgres, y de acuerdo con las reglas de SQL, un UPDATEescribe una nueva versión de fila para cada fila que no está excluida en la WHEREcláusula.

Esto lo hace tiene un impacto más o menos sustancial en el rendimiento, directa e indirectamente. Las "actualizaciones vacías" tienen el mismo costo por fila que cualquier otra actualización. Disparan desencadenantes (si están presentes) como cualquier otra actualización, tienen que estar registrados en WAL y producen filas muertas que hinchan la tabla y causan más trabajo para VACUUMmás adelante como cualquier otra actualización.

Las entradas de índices y las columnas TOASTed en las que no se cambia ninguna de las columnas involucradas pueden permanecer iguales, pero eso es cierto para cualquier fila actualizada. Relacionado:

Casi siempre es una buena idea excluir esas actualizaciones vacías (cuando existe una posibilidad real de que ocurra). No proporcionó una definición de tabla en su pregunta (que siempre es una buena idea). Tenemos que suponer que first_namepuede ser NULL (lo que no sería sorprendente para un "nombre"), por lo tanto, la consulta debe usar una comparación NULL-safe :

UPDATE users
SET    first_name = 'Michael'
WHERE  id = 123
AND   first_name IS DISTINCT FROM 'Michael';

Si first_name IS NULLantes de la actualización, una prueba con solo first_name <> 'Michael'evaluaría a NULL y, como tal, excluiría la fila de la actualización. Error furtivo Sin embargo, si la columna está definidaNOT NULL , use la simple verificación de igualdad, porque es un poco más barato.

Relacionado:


1
Indexes entries and TOASTed columns where none of the involved columns are changed can stay the same¿Pero no tendrían que actualizarse para señalar la nueva ubicación de la fila?
dvtan

1
@dtgq: No con actualizaciones HOT, donde el índice puede seguir apuntando a la ubicación anterior, y las capturas de montón tienen que atravesar la cadena HOT para obtener la tupla en vivo. Agregué enlaces a más explicaciones arriba.
Erwin Brandstetter

1
¿Qué pasa con MVCC pide una actualización noop para escribir una nueva tupla?
jberryman

@jberryman: No estoy seguro de entender. De cualquier manera, por favor haga su pregunta como nueva pregunta . Siempre puede vincular a este para el contexto. Y puedes dejar un comentario aquí para vincular de nuevo (y llamar mi atención).
Erwin Brandstetter

2
@jberryman: Realmente no las razones por las cuales el proyecto fue así. Eso fue establecido hace mucho tiempo. Pero supongo que sería innecesariamente costoso verificar la igualdad de cada fila y tener una ruta de código separada para las filas sin cambios. El manejo de las ID de transacción sería más complicado: carcasa especial para rollbackmanejo de instantáneas, administración de bloqueos, WAL, y qué no ...
Erwin Brandstetter

4

Los ORM como Ruby on Rail ofrecen una ejecución diferida que marca un registro como modificado (o no) y luego, cuando es necesario o llamado, luego envía el cambio a la base de datos.

PostgreSQL es una base de datos y no un ORM. Hubiera disminuido el rendimiento si se tomara el tiempo de verificar si un nuevo valor era el mismo que el valor actualizado en su consulta.

Por lo tanto, actualizará el valor independientemente de si es el mismo que el nuevo valor o no.

Si desea evitar esto, puede usar un código como lo sugirió Max Vernon en su respuesta.


2

Simplemente podría agregar a la wherecláusula:

UPDATE users
SET first_name = 'Michael'
WHERE users.id = 123
    AND (first_name <> 'Michael' OR first_name IS NULL);

Si first_namese define como NOT NULL, la OR first_name IS NULLparte se puede quitar.

La condición:

(first_name <> 'Michael' OR first_name IS NULL)

También se puede escribir con más elegancia como (en la respuesta de Erwin):

first_name IS DISTINCT FROM 'Michael'

Sin saber si la columna puede ser NULL, eso podría introducir un error furtivo.
Erwin Brandstetter

1
@ErwinBrandstetter Estaba actualizando la respuesta, ¡luego vi el comentario y tu respuesta!
ypercubeᵀᴹ

gracias por la edición, @ypercube - y por el comentario sobre NULL@erwin
Max Vernon

1

Desde el punto de vista de la base de datos

La respuesta a tu pregunta es sí. La actualización tendrá lugar. La base de datos no verifica el valor anterior, solo establece el nuevo valor.

Como esto sucede en la memoria (y solo se escribirá en los archivos de datos después de emitir una confirmación), el rendimiento no sería un problema.

Desde una perspectiva ORM

Normalmente tendrá un Objeto que representa una sola fila de la base de datos (puede ser mucho más complejo que eso, pero hagámoslo simple). Este objeto se gestiona en la memoria (en el nivel del servidor de aplicaciones) y solo la última versión confirmada de ese objeto realmente llegará a la base de datos en un momento determinado.

Eso puede explicar el comportamiento diferente.

Ahora, no comparemos un buque de carga con una impresora 3D. El hecho de que pueda enviar impresoras 3D utilizando buques de carga no significa que pueda haber algún tipo de comparación entre ellos.

¡Disfrutar!

Espero que esto haya aclarado algunos conceptos.


44
El rendimiento es y problema. Cada actualización debe escribirse en el disco (el registro y la tabla).
ypercubeᵀᴹ

Dependerá del RDBMS real que use. Pero la mayoría de ellos no confirma cada actualización, sino solo el último bloque confirmado que tienen en la memoria. Nunca lee o escribe una sola fila en una base de datos. Usted lee / escribe bloques y los mantiene en memoria hasta que tenga que vaciarlos para colocar un nuevo bloque en el mismo lugar. Mientras esté en la memoria, no todos los cambios seguidos se escribirán en el disco, sino solo el contenido del bloque cuando se indique que el proceso de "escritor de la base de datos" volcará ese bloque de memoria en un archivo de datos. Entonces, no ... No es un problema a menos que su aplicación mantenga el bloque sin confirmar durante demasiado tiempo.
Silvarion

1
La pregunta es sobre Postgres, no sobre ningún DBMS arbitrario. Y aunque no todas las actualizaciones tienen que escribirse una por una, cada escritura en la base de datos debe escribirse en el registro. Si no se escribe un cambio en el almacenamiento persistente, ¿cómo sobrevivirá el DBMS a un bloqueo del sistema?
ypercubeᵀᴹ

Sí, escribe en los registros, desde la memoria también durante los puntos de control. A menos que tenga una cantidad enorme de usuarios simultáneos, no debería ser un problema en absoluto. Los registros también se escriben en lotes. Creo que estamos hablando de servidores. Si está hablando de una base de datos Postgres en una computadora portátil con un disco duro de 5400 RPM, sí ... siempre tendrá problemas de rendimiento. Entonces, la respuesta final sería la primera ... Depende de muchas cosas.
Silvarion
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.