MVCC-3. Versiones de fila

Entonces, analizamos los problemas relacionados con el aislamiento e hicimos una digresi贸n sobre la organizaci贸n de los datos a un nivel bajo . Y finalmente lleg贸 a lo m谩s interesante: a la versi贸n de las l铆neas.

Titular


Como ya hemos dicho, cada fila puede estar presente simult谩neamente en la base de datos en varias versiones. Una versi贸n debe distinguirse de la otra de alguna manera, para este prop贸sito, cada versi贸n tiene dos marcas que determinan el "tiempo" de la acci贸n de esta versi贸n (xmin y xmax). Entre comillas, porque no es el tiempo lo que se usa como tal, sino un contador incremental especial. Y este contador es el n煤mero de transacci贸n.

(Como de costumbre, en realidad es m谩s complicado: la cantidad de transacciones no puede aumentar todo el tiempo debido a la capacidad limitada del mostrador. Pero consideraremos estos detalles en detalle cuando lleguemos al congelamiento).

Cuando se crea la l铆nea, xmin se establece en el n煤mero de la transacci贸n que ejecut贸 el comando INSERT, y xmax no se rellena.

Cuando se elimina una fila, el valor xmax de la versi贸n actual se marca con el n煤mero de transacci贸n que realiz贸 DELETE.

Cuando se modifica una l铆nea con el comando ACTUALIZAR, se realizan dos operaciones: BORRAR e INSERTAR. En la versi贸n actual de la l铆nea, xmax se establece igual al n煤mero de la transacci贸n que realiz贸 ACTUALIZACI脫N. Luego se crea una nueva versi贸n de la misma l铆nea; su valor xmin coincide con el valor xmax de la versi贸n anterior.

Los campos xmin y xmax se incluyen en el encabezado de la versi贸n de la fila. Adem谩s de estos campos, el encabezado contiene otros, por ejemplo:

  • infomask: una serie de bits que definen las propiedades de esta versi贸n. Hay bastantes de ellos; los principales los vamos a considerar gradualmente.
  • ctid: un enlace a la siguiente versi贸n m谩s nueva de la misma l铆nea. En la versi贸n m谩s reciente y m谩s actual de la cadena, ctid se refiere a esta versi贸n en s铆. El n煤mero tiene la forma (x, y), donde x es el n煤mero de p谩gina, y es el n煤mero de serie del puntero en la matriz.
  • mapa de bits de valores indefinidos: marca las columnas de esta versi贸n que contienen un valor indefinido (NULL). NULL no es uno de los valores habituales de los tipos de datos, por lo que el atributo debe almacenarse por separado.

Como resultado, el encabezado es bastante grande: al menos 23 bytes por versi贸n de la cadena, y generalmente m谩s debido al mapa de bits NULL. Si la tabla es "estrecha" (es decir, contiene pocas columnas), la sobrecarga puede tomar m谩s que informaci贸n 煤til.

Insertar


Echemos un vistazo m谩s de cerca a c贸mo se realizan las operaciones de cadena en un nivel bajo, y comencemos con la inserci贸n.

Para los experimentos, cree una nueva tabla con dos columnas y un 铆ndice en una de ellas:

=> CREATE TABLE t( id serial, s text ); => CREATE INDEX ON t(s); 

Inserte una l铆nea, despu茅s de comenzar la transacci贸n.

 => BEGIN; => INSERT INTO t(s) VALUES ('FOO'); 

Aqu铆 est谩 el n煤mero de nuestra transacci贸n actual:

 => SELECT txid_current(); 
  txid_current -------------- 3664 (1 row) 

Echa un vistazo a los contenidos de la p谩gina. La funci贸n heap_page_items de la extensi贸n pageinspect proporciona informaci贸n sobre punteros y versiones de fila:

 => SELECT * FROM heap_page_items(get_raw_page('t',0)) \gx 
 -[ RECORD 1 ]------------------- lp | 1 lp_off | 8160 lp_flags | 1 lp_len | 32 t_xmin | 3664 t_xmax | 0 t_field3 | 0 t_ctid | (0,1) t_infomask2 | 2 t_infomask | 2050 t_hoff | 24 t_bits | t_oid | t_data | \x0100000009464f4f 

Tenga en cuenta que la palabra mont贸n (mont贸n) en PostgreSQL se refiere a tablas. Este es otro uso extra帽o del t茅rmino: el mont贸n es una estructura de datos bien conocida que no tiene nada que ver con una tabla. Aqu铆, esta palabra se usa en el sentido de "todo est谩 apilado en un mont贸n", en contraste con los 铆ndices ordenados.

La funci贸n muestra los datos "tal cual" en un formato que es dif铆cil de leer. Para entenderlo, dejaremos solo una parte de la informaci贸n y la descifraremos:

 => SELECT '(0,'||lp||')' AS ctid, CASE lp_flags WHEN 0 THEN 'unused' WHEN 1 THEN 'normal' WHEN 2 THEN 'redirect to '||lp_off WHEN 3 THEN 'dead' END AS state, t_xmin as xmin, t_xmax as xmax, (t_infomask & 256) > 0 AS xmin_commited, (t_infomask & 512) > 0 AS xmin_aborted, (t_infomask & 1024) > 0 AS xmax_commited, (t_infomask & 2048) > 0 AS xmax_aborted, t_ctid FROM heap_page_items(get_raw_page('t',0)) \gx 
 -[ RECORD 1 ]-+------- ctid | (0,1) state | normal xmin | 3664 xmax | 0 xmin_commited | f xmin_aborted | f xmax_commited | f xmax_aborted | t t_ctid | (0,1) 

Esto es lo que hicimos:

  • Agregamos un cero al n煤mero de 铆ndice para llevarlo a la misma forma que t_ctid: (n煤mero de p谩gina, n煤mero de 铆ndice).
  • Descifr贸 el estado del puntero lp_flags. Aqu铆 es "normal", esto significa que el puntero realmente se refiere a la versi贸n de la cadena. Otros valores ser谩n considerados m谩s adelante.
  • De todos los bits de informaci贸n, hasta ahora solo se han asignado dos pares. Los bits xmin_committed y xmin_aborted indican si la transacci贸n con el n煤mero xmin est谩 confirmada (cancelada). Dos bits similares se refieren al n煤mero de transacci贸n xmax.

Que vemos Cuando inserta una fila en la p谩gina de la tabla, aparece un puntero con el n煤mero 1, que se refiere a la primera y 煤nica versi贸n de la fila.

En la versi贸n de la l铆nea, el campo xmin se llena con el n煤mero de la transacci贸n actual. La transacci贸n a煤n est谩 activa, por lo que ambos bits xmin_committed y xmin_aborted no est谩n establecidos.

El campo ctid de la versi贸n de la fila se refiere a la misma fila. Esto significa que no existe una versi贸n m谩s nueva.

El campo xmax se llena con un n煤mero ficticio 0, porque esta versi贸n de la l铆nea no se elimina y es relevante. Las transacciones no prestar谩n atenci贸n a este n煤mero, porque el bit xmax_aborted est谩 establecido.

Tomemos un paso m谩s para mejorar la legibilidad agregando bits de informaci贸n a los n煤meros de transacci贸n. Y crearemos una funci贸n, ya que necesitaremos la solicitud m谩s de una vez:

 => CREATE FUNCTION heap_page(relname text, pageno integer) RETURNS TABLE(ctid tid, state text, xmin text, xmax text, t_ctid tid) AS $$ SELECT (pageno,lp)::text::tid AS ctid, CASE lp_flags WHEN 0 THEN 'unused' WHEN 1 THEN 'normal' WHEN 2 THEN 'redirect to '||lp_off WHEN 3 THEN 'dead' END AS state, t_xmin || CASE WHEN (t_infomask & 256) > 0 THEN ' (c)' WHEN (t_infomask & 512) > 0 THEN ' (a)' ELSE '' END AS xmin, t_xmax || CASE WHEN (t_infomask & 1024) > 0 THEN ' (c)' WHEN (t_infomask & 2048) > 0 THEN ' (a)' ELSE '' END AS xmax, t_ctid FROM heap_page_items(get_raw_page(relname,pageno)) ORDER BY lp; $$ LANGUAGE SQL; 

De esta forma, queda mucho m谩s claro lo que est谩 sucediendo en el encabezado de la versi贸n de la cadena:

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+------+-------+-------- (0,1) | normal | 3664 | 0 (a) | (0,1) (1 row) 

Se puede obtener informaci贸n similar, pero sustancialmente menos detallada, de la tabla misma, utilizando las pseudocolumnas xmin y xmax:

 => SELECT xmin, xmax, * FROM t; 
  xmin | xmax | id | s ------+------+----+----- 3664 | 0 | 1 | FOO (1 row) 

Fijaci贸n


Al completar con 茅xito la transacci贸n, debe recordar su estado; tenga en cuenta que est谩 arreglado. Para hacer esto, use una estructura llamada XACT (y antes de la versi贸n 10 se llamaba CLOG (commit log) y este nombre todav铆a se puede encontrar en diferentes lugares).

XACT no es una tabla de cat谩logo del sistema; Estos son los archivos en el directorio PGDATA / pg_xact. En ellos, para cada transacci贸n, se asignan dos bits: comprometidos y anulados, exactamente lo mismo que en el encabezado de la versi贸n de la l铆nea. Esta informaci贸n se divide en varios archivos 煤nicamente por conveniencia, volveremos a este problema cuando consideremos la congelaci贸n. Y el trabajo con estos archivos se realiza p谩gina por p谩gina, como con todos los dem谩s.

Entonces, cuando se confirma una transacci贸n en XACT, el bit comprometido se establece para esta transacci贸n. Y eso es todo lo que sucede durante la confirmaci贸n (aunque todav铆a no estamos hablando del diario de pregrabaci贸n).

Cuando cualquier otra transacci贸n acceda a la p谩gina de la tabla que acabamos de ver, tendr谩 que responder algunas preguntas.

  1. 驴Se ha completado la transacci贸n xmin? De lo contrario, la versi贸n generada de la cadena no deber铆a ser visible.
    Dicha verificaci贸n se realiza observando otra estructura m谩s, que se encuentra en la memoria compartida de la instancia y se llama ProcArray. Contiene una lista de todos los procesos activos, y para cada uno se indica el n煤mero de su transacci贸n actual (activa).
  2. Si se completa, 驴c贸mo? 驴Por fijaci贸n o cancelaci贸n? Si se cancela, la versi贸n de la cadena tampoco deber铆a ser visible.
    Para eso es exactamente XACT. Pero, aunque las 煤ltimas p谩ginas XACT se almacenan en memorias intermedias en la RAM, no es necesario verificar el XACT cada vez. Por lo tanto, el estado de una transacci贸n una vez aclarado se registra en los bits xmin_committed y xmin_aborted de la versi贸n de la fila. Si se establece uno de estos bits, el estado de la transacci贸n xmin se considera conocido y la pr贸xima transacci贸n ya no tendr谩 que acceder a XACT.

驴Por qu茅 estos bits no son establecidos por la transacci贸n misma que realiza la inserci贸n? Cuando se produce una inserci贸n, la transacci贸n a煤n no sabe si se completar谩 con 茅xito. Y en el momento de la reparaci贸n ya no est谩 claro en qu茅 l铆neas se cambiaron las p谩ginas. Puede haber muchas de esas p谩ginas, y memorizarlas es una desventaja. Adem谩s, parte de las p谩ginas se pueden expulsar de la memoria cach茅 del b煤fer al disco; leerlos nuevamente para cambiar los bits significar铆a disminuir significativamente la confirmaci贸n.

La desventaja de los ahorros es que despu茅s de los cambios, cualquier transacci贸n (incluso realizar una simple lectura - SELECCIONAR) puede comenzar a cambiar las p谩ginas de datos en la memoria cach茅 del b煤fer.

Entonces, arregla el cambio.

 => COMMIT; 

Nada ha cambiado en la p谩gina (pero sabemos que el estado de la transacci贸n ya est谩 registrado en XACT):

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+------+-------+-------- (0,1) | normal | 3664 | 0 (a) | (0,1) (1 row) 

Ahora la transacci贸n que primero accede a la p谩gina tendr谩 que determinar el estado de la transacci贸n xmin y escribirla en los bits de informaci贸n:

 => SELECT * FROM t; 
  id | s ----+----- 1 | FOO (1 row) 

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+----------+-------+-------- (0,1) | normal | 3664 (c) | 0 (a) | (0,1) (1 row) 

Eliminar


Cuando se elimina una l铆nea, el n煤mero de la transacci贸n de eliminaci贸n actual se registra en el campo xmax de la versi贸n actual, y el bit xmax_aborted se restablece.

Tenga en cuenta que el valor establecido de xmax correspondiente a la transacci贸n activa act煤a como un bloqueo de fila. Si otra transacci贸n est谩 a punto de actualizar o eliminar esta fila, se ver谩 obligado a esperar a que se complete la transacci贸n xmax. Hablaremos m谩s sobre las cerraduras m谩s tarde. Por ahora, solo notamos que el n煤mero de bloqueos de fila es ilimitado. No ocupan un lugar en la RAM y el rendimiento del sistema no se ve afectado por su cantidad. Es cierto que las transacciones "largas" tienen otras desventajas, pero m谩s sobre eso m谩s adelante.

Eliminar la l铆nea.

 => BEGIN; => DELETE FROM t; => SELECT txid_current(); 
  txid_current -------------- 3665 (1 row) 

Vemos que el n煤mero de transacci贸n se registra en el campo xmax, pero los bits de informaci贸n no est谩n establecidos:

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+----------+------+-------- (0,1) | normal | 3664 (c) | 3665 | (0,1) (1 row) 

Cancelar


Revertir los cambios funciona de manera similar a commit, solo en XACT para la transacci贸n se establece el bit anulado. La cancelaci贸n es tan r谩pida como la confirmaci贸n. Aunque el comando se llama ROLLBACK, el cambio no se revierte: todo lo que la transacci贸n logr贸 cambiar en las p谩ginas de datos permanece sin cambios.

 => ROLLBACK; => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+----------+------+-------- (0,1) | normal | 3664 (c) | 3665 | (0,1) (1 row) 

Al acceder a la p谩gina, se comprobar谩 el estado y se establecer谩 el bit de sugerencia xmax_aborted en la versi贸n de la l铆nea. El n煤mero xmax en s铆 permanece en la p谩gina, pero nadie lo ver谩.

 => SELECT * FROM t; 
  id | s ----+----- 1 | FOO (1 row) 

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+----------+----------+-------- (0,1) | normal | 3664 (c) | 3665 (a) | (0,1) (1 row) 

Actualizaci贸n


La actualizaci贸n funciona como si primero estuviera eliminando la versi贸n actual de la fila y luego insertara una nueva.

 => BEGIN; => UPDATE t SET s = 'BAR'; => SELECT txid_current(); 
  txid_current -------------- 3666 (1 row) 

La solicitud produce una l铆nea (nueva versi贸n):

 => SELECT * FROM t; 
  id | s ----+----- 1 | BAR (1 row) 

Pero en la p谩gina vemos ambas versiones:

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+----------+-------+-------- (0,1) | normal | 3664 (c) | 3666 | (0,2) (0,2) | normal | 3666 | 0 (a) | (0,2) (2 rows) 

La versi贸n remota est谩 marcada con el n煤mero de transacci贸n actual en el campo xmax. Adem谩s, este valor se escribe sobre el anterior, ya que se cancel贸 la transacci贸n anterior. Y el bit xmax_aborted se restablece, porque el estado de la transacci贸n actual a煤n se desconoce.

La primera versi贸n de la l铆nea ahora se refiere al segundo (campo t_ctid), como una m谩s nueva.

Un segundo puntero y una segunda l铆nea aparecen en la p谩gina de 铆ndice, vinculando a la segunda versi贸n en la p谩gina de la tabla.

Al igual que con la eliminaci贸n, el valor xmax en la primera versi贸n de la cadena es una se帽al de que la cadena est谩 bloqueada.

Bueno, completa la transacci贸n.

 => COMMIT; 

脥ndices


Hasta ahora, solo hemos hablado de p谩ginas tabulares. 驴Y qu茅 pasa dentro de los 铆ndices?

La informaci贸n en las p谩ginas de 铆ndice depende en gran medida del tipo particular de 铆ndice. E incluso un tipo de 铆ndice tiene diferentes tipos de p谩ginas. Por ejemplo, el 谩rbol B tiene una p谩gina con metadatos y p谩ginas "normales".

Sin embargo, una p谩gina generalmente tiene una serie de punteros a las l铆neas y las l铆neas mismas (como en una p谩gina de tabla). Adem谩s, al final de la p谩gina hay un lugar para datos especiales.

Las filas en los 铆ndices tambi茅n pueden tener una estructura muy diferente seg煤n el tipo de 铆ndice. Por ejemplo, para un 谩rbol B, las filas relacionadas con p谩ginas de hoja contienen el valor de la clave de 铆ndice y un enlace (ctid) a la fila correspondiente de la tabla. En general, un 铆ndice se puede organizar de una manera completamente diferente.

El punto m谩s importante es que no hay versiones de fila en ning煤n tipo de 铆ndice. Bueno, o podemos suponer que cada l铆nea est谩 representada por exactamente una versi贸n. En otras palabras, no hay campos xmin y xmax en el encabezado de la fila del 铆ndice. Podemos suponer que los enlaces del 铆ndice conducen a todas las versiones tabulares de las filas, por lo que solo puede averiguar qu茅 versi贸n ver谩 la transacci贸n si mira la tabla. (Como de costumbre, esta no es toda la verdad. En algunos casos, el mapa de visibilidad le permite optimizar el proceso, pero lo consideraremos con m谩s detalle m谩s adelante).

Al mismo tiempo, en la p谩gina de 铆ndice encontramos punteros a ambas versiones, tanto la actual como la anterior:

 => SELECT itemoffset, ctid FROM bt_page_items('t_s_idx',1); 
  itemoffset | ctid ------------+------- 1 | (0,2) 2 | (0,1) (2 rows) 

Transacciones virtuales


En la pr谩ctica, PostgreSQL usa optimizaciones para "guardar" los n煤meros de transacci贸n.

Si una transacci贸n solo lee datos, entonces no afecta la visibilidad de las versiones de fila. Por lo tanto, al principio, el proceso de publicaci贸n emite una transacci贸n de n煤mero virtual (xid virtual). El n煤mero consta de un identificador de proceso y un n煤mero secuencial.

La emisi贸n de este n煤mero no requiere sincronizaci贸n entre todos los procesos y, por lo tanto, es muy r谩pida. Conoceremos otra raz贸n para usar n煤meros virtuales cuando hablemos de la congelaci贸n.

Los n煤meros virtuales no se tienen en cuenta en las instant谩neas de datos.

En diferentes momentos, las transacciones virtuales con n煤meros que ya se han utilizado pueden aparecer en el sistema, y 鈥嬧媏sto es normal. Pero ese n煤mero no se puede escribir en las p谩ginas de datos, porque la pr贸xima vez que acceda a la p谩gina, puede perder todo significado.

 => BEGIN; => SELECT txid_current_if_assigned(); 
  txid_current_if_assigned -------------------------- (1 row) 

Si la transacci贸n comienza a cambiar datos, se le da un n煤mero de transacci贸n real y 煤nico.

 => UPDATE accounts SET amount = amount - 1.00; => SELECT txid_current_if_assigned(); 
  txid_current_if_assigned -------------------------- 3667 (1 row) 

 => COMMIT; 

Transacciones anidadas


Guardar puntos


SQL define los puntos de guardado que le permiten deshacer una parte de una transacci贸n sin interrumpirla por completo. Pero esto no encaja en el esquema anterior, ya que el estado de una transacci贸n es uno para todos sus cambios, y f铆sicamente no se revierten los datos.

Para implementar dicha funcionalidad, una transacci贸n con un punto de guardado se divide en varias transacciones anidadas separadas (subtransacci贸n), cuyo estado se puede controlar por separado.

Las transacciones anidadas tienen su propio n煤mero (m谩s alto que el n煤mero de transacci贸n principal). El estado de las transacciones anidadas se registra de la manera habitual en XACT, sin embargo, el estado final depende del estado de la transacci贸n principal: si se cancela, todas las transacciones anidadas tambi茅n se cancelan.

La informaci贸n sobre el anidamiento de transacciones se almacena en archivos en el directorio PGDATA / pg_subtrans. Se accede a los archivos a trav茅s de buffers en la memoria compartida de la instancia, organizados de la misma manera que los buffers XACT.

No confunda transacciones anidadas y transacciones aut贸nomas. Las transacciones aut贸nomas de ninguna manera dependen unas de otras, y las anidadas dependen. No hay transacciones aut贸nomas en el PostgreSQL habitual y, tal vez, para mejor: en el caso de que se necesiten muy, muy raramente, y su presencia en otros DBMS provoca abuso, del cual todos sufren.

Borre la tabla, inicie la transacci贸n e inserte la l铆nea:

 => TRUNCATE TABLE t; => BEGIN; => INSERT INTO t(s) VALUES ('FOO'); => SELECT txid_current(); 
  txid_current -------------- 3669 (1 row) 

 => SELECT xmin, xmax, * FROM t; 
  xmin | xmax | id | s ------+------+----+----- 3669 | 0 | 2 | FOO (1 row) 

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+------+-------+-------- (0,1) | normal | 3669 | 0 (a) | (0,1) (1 row) 

Ahora ponga un punto de guardado e inserte otra fila.

 => SAVEPOINT sp; => INSERT INTO t(s) VALUES ('XYZ'); => SELECT txid_current(); 
  txid_current -------------- 3669 (1 row) 

Tenga en cuenta que la funci贸n txid_current () devuelve el n煤mero de la transacci贸n principal, no anidada.

 => SELECT xmin, xmax, * FROM t; 
  xmin | xmax | id | s ------+------+----+----- 3669 | 0 | 2 | FOO 3670 | 0 | 3 | XYZ (2 rows) 

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+------+-------+-------- (0,1) | normal | 3669 | 0 (a) | (0,1) (0,2) | normal | 3670 | 0 (a) | (0,2) (2 rows) 

Volvemos al punto de guardado e insertamos la tercera fila.

 => ROLLBACK TO sp; => INSERT INTO t(s) VALUES ('BAR'); => SELECT xmin, xmax, * FROM t; 
  xmin | xmax | id | s ------+------+----+----- 3669 | 0 | 2 | FOO 3671 | 0 | 4 | BAR (2 rows) 

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+----------+-------+-------- (0,1) | normal | 3669 | 0 (a) | (0,1) (0,2) | normal | 3670 (a) | 0 (a) | (0,2) (0,3) | normal | 3671 | 0 (a) | (0,3) (3 rows) 

En la p谩gina, seguimos viendo la fila agregada por la transacci贸n anidada cancelada.

Arreglamos los cambios.

 => COMMIT; => SELECT xmin, xmax, * FROM t; 
  xmin | xmax | id | s ------+------+----+----- 3669 | 0 | 2 | FOO 3671 | 0 | 4 | BAR (2 rows) 

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+----------+-------+-------- (0,1) | normal | 3669 (c) | 0 (a) | (0,1) (0,2) | normal | 3670 (a) | 0 (a) | (0,2) (0,3) | normal | 3671 (c) | 0 (a) | (0,3) (3 rows) 

Ahora puede ver claramente que cada transacci贸n anidada tiene su propio estado.

Tenga en cuenta que las transacciones anidadas no se pueden usar expl铆citamente en SQL, es decir, no puede iniciar una nueva transacci贸n sin completar la actual. Este mecanismo se usa impl铆citamente cuando se usan puntos de guardado, y tambi茅n cuando se manejan excepciones PL / pgSQL y en una serie de otros casos m谩s ex贸ticos.

 => BEGIN; 
 BEGIN 
 => BEGIN; 
 WARNING: there is already a transaction in progress BEGIN 
 => COMMIT; 
 COMMIT 
 => COMMIT; 
 WARNING: there is no transaction in progress COMMIT 

Errores y atomicidad de las operaciones.


驴Qu茅 sucede si se produce un error durante la operaci贸n? Por ejemplo, as铆:

 => BEGIN; => SELECT * FROM t; 
  id | s ----+----- 2 | FOO 4 | BAR (2 rows) 

 => UPDATE t SET s = repeat('X', 1/(id-4)); 
 ERROR: division by zero 

Ha ocurrido un error Ahora la transacci贸n se considera abortada y no se permite una sola operaci贸n:

 => SELECT * FROM t; 
 ERROR: current transaction is aborted, commands ignored until end of transaction block 

E incluso si intenta confirmar los cambios, PostgreSQL informar谩 la cancelaci贸n:

 => COMMIT; 
 ROLLBACK 

驴Por qu茅 no puedo continuar la transacci贸n despu茅s de una falla? El hecho es que podr铆a ocurrir un error para que podamos acceder a parte de los cambios: la atomicidad de ni siquiera la transacci贸n, pero el operador ser铆a violado. Como en nuestro ejemplo, donde el operador logr贸 actualizar una l铆nea antes del error:

 => SELECT * FROM heap_page('t',0); 
  ctid | state | xmin | xmax | t_ctid -------+--------+----------+-------+-------- (0,1) | normal | 3669 (c) | 3672 | (0,4) (0,2) | normal | 3670 (a) | 0 (a) | (0,2) (0,3) | normal | 3671 (c) | 0 (a) | (0,3) (0,4) | normal | 3672 | 0 (a) | (0,4) (4 rows) 

Debo decir que en psql hay un modo que a煤n le permite continuar con la transacci贸n despu茅s de una falla, como si las acciones del operador err贸neo fueran revertidas.

 => \set ON_ERROR_ROLLBACK on => BEGIN; => SELECT * FROM t; 
  id | s ----+----- 2 | FOO 4 | BAR (2 rows) 

 => UPDATE t SET s = repeat('X', 1/(id-4)); 
 ERROR: division by zero 

 => SELECT * FROM t; 
  id | s ----+----- 2 | FOO 4 | BAR (2 rows) 

 => COMMIT; 

Es f谩cil adivinar que, en este modo, psql establece un punto de guardado impl铆cito delante de cada comando y, en caso de falla, inicia un retroceso. Este modo no se usa de manera predeterminada, ya que establecer puntos de guardado (incluso sin retroceder a ellos) est谩 asociado con una sobrecarga significativa.

Continuar谩

Source: https://habr.com/ru/post/445820/


All Articles