¿En qué punto una base de datos actualiza sus índices en una transacción?

11

Estoy tratando de entender la secuencia de eventos en inserciones donde están involucrados tanto un índice como una transacción.

Por ejemplo, la documentación de Oracle establece:

Si crea [o tiene] uno o más índices antes de cargar datos, la base de datos debe actualizar cada índice a medida que se inserta cada fila.

Pero, ¿qué sucede si creo una transacción, inserto cinco filas y luego confirmo? ¿Se actualizan los índices para cada inserción, o solo en el punto de confirmación?

La lógica me dice que solo se actualizarían en el punto de confirmación, porque un índice actualizado no podría ser útil hasta que se confirmen esos registros. ¿Pero es eso cierto?

Si es así, cuando tengo 1 millón de filas para insertar, para un mejor rendimiento, ¿debería hacer una confirmación grande de todas las filas y no 10 transacciones de 100k registros? Por supuesto, me doy cuenta de que esto conlleva un mayor retroceso si la fila 999,999 falla.

Disculpas si mi terminología está un poco fuera de lugar. No soy un DBA por oficio. No estoy tan interesado en una base de datos en particular, como en las bases de datos en general, aunque Oracle y Postgres son lo que más uso. He buscado este tema pero realmente no puedo encontrar una respuesta definitiva.

Mark Ireland
fuente

Respuestas:

8

Trabajo con SQL Server y Oracle. Probablemente hay algunas excepciones, pero para esas plataformas la respuesta general es que los datos e índices se actualizarán al mismo tiempo.

Creo que sería útil establecer una distinción entre cuándo se actualizan los índices para la sesión propietaria de la transacción y para otras sesiones. Por defecto, otras sesiones no verán los índices actualizados hasta que se confirme la transacción. Sin embargo, la sesión propietaria de la transacción verá inmediatamente los índices actualizados.

Para una manera de pensarlo, considere en una mesa con una clave primaria. En SQL Server y Oracle, esto se implementa como un índice. La mayoría de las veces queremos que haya un error inmediatamente si INSERTse hace algo que violaría la clave primaria. Para que eso suceda, el índice debe actualizarse al mismo tiempo que los datos. Tenga en cuenta que otras plataformas, como Postgres, permiten restricciones diferidas que se verifican solo cuando se confirma la transacción.

Aquí hay una demostración rápida de Oracle que muestra un caso común:

CREATE TABLE X_TABLE (PK INT NULL, PRIMARY KEY (PK));

INSERT INTO X_TABLE VALUES (1);
INSERT INTO X_TABLE VALUES (1); -- no commit

La segunda INSERTdeclaración arroja un error:

Error de SQL: ORA-00001: violación exclusiva (XXXXXX.SYS_C00384850) violada

00001. 00000 - "restricción única (% s.% S) violada"

* Causa: una instrucción UPDATE o INSERT intentó insertar una clave duplicada. Para Trusted Oracle configurado en modo DBMS MAC, puede ver este mensaje si existe una entrada duplicada en un nivel diferente.

* Acción: elimine la restricción única o no inserte la clave.

Si prefiere ver una acción de actualización de índice a continuación, hay una demostración simple en SQL Server. Primero cree una tabla de dos columnas con un millón de filas y un índice no agrupado en la VALcolumna:

DROP TABLE IF EXISTS X_TABLE_IX;

CREATE TABLE X_TABLE_IX (
ID INT NOT NULL,
VAL VARCHAR(10) NOT NULL
PRIMARY KEY (ID)
);

CREATE INDEX X_INDEX ON X_TABLE_IX (VAL);

-- insert one million rows with N from 1 to 1000000
INSERT INTO X_TABLE_IX
SELECT N, N FROM dbo.Getnums(1000000);

La siguiente consulta puede usar el índice no agrupado porque el índice es un índice de cobertura para esa consulta. Contiene todos los datos necesarios para ejecutarlo. Como se esperaba, no se devuelven devoluciones.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

consulta 1

Ahora comencemos una transacción y actualicemos VALpara casi todas las filas de la tabla:

BEGIN TRANSACTION

UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;

Aquí está parte del plan de consulta para eso:

consulta 2

En un círculo rojo está la actualización del índice no agrupado. En un círculo azul está la actualización del índice agrupado, que es esencialmente los datos de la tabla. Aunque la transacción no se ha confirmado, vemos que los datos y el índice se actualizan en parte de la ejecución de la consulta. Tenga en cuenta que no siempre verá esto en un plan dependiendo del tamaño de los datos involucrados junto con posiblemente otros factores.

Con la transacción aún no confirmada, revisemos la SELECTconsulta desde arriba.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

ingrese la descripción de la imagen aquí

El optimizador de consultas aún puede usar el índice y esta vez estima que se devolverán 999999 filas. La ejecución de la consulta devuelve el resultado esperado.

Esa fue una demostración simple, pero espero que aclare un poco las cosas.

Como comentario aparte, conozco algunos casos en los que se podría argumentar que un índice no se actualiza de inmediato. Esto se hace por razones de rendimiento y el usuario final no debería poder ver datos inconsistentes. Por ejemplo, a veces las eliminaciones no se aplicarán completamente a un índice en SQL Server. Se ejecuta un proceso en segundo plano y finalmente limpia los datos. Puedes leer sobre registros de fantasmas si tienes curiosidad.

Joe Obbish
fuente
Esa es una súper respuesta, y también responde a otra cosa que me preguntaba: si se produciría una violación de la clave principal (o similar) en Insertar o en Confirmar. Gracias por una respuesta tan completa.
Mark Ireland
La pregunta relacionada (sobre cuándo ocurrirá una violación de restricción) está relacionada con si usa transacciones diferidas o no. SQL Server, por ejemplo, no ha implementado la transacción diferida, por lo que todas las violaciones se producen al final de las declaraciones. Otros DBMS tienen (Postgres, por ejemplo, aunque no para todos los tipos de restricciones), por lo que cuando se difiere una restricción, la violación se verificará en la fase de confirmación de la transacción).
ypercubeᵀᴹ
Oracle también admite restricciones diferidas
BobC
1

Mi experiencia es que la inserción de 1,000,000 de filas realmente requerirá más recursos y tardará más en completarse que si usara insertos por lotes. Esto podría implementarse, como ejemplo, en 100 insertos de 10,000 filas.

Esto reduce la sobrecarga de los lotes que se están insertando y, si un lote falla, es una reversión más pequeña.

En cualquier caso, para SQL Server hay una utilidad bcp o el comando BULK INSERT que podría usarse para hacer inserciones por lotes.

Y, por supuesto, también puede implementar su propio código para manejar este enfoque.

RLF
fuente
1
En general, si necesita insertar una gran cantidad de filas en una tabla que necesita un índice, es probable que sea más rápido eliminar el índice, cargar los datos y luego reconstruir el índice. Oracle también admite una opción de carga masiva de ruta directa, utilizando la sugerencia / * + APPEND * /.
BobC