La última vez, nos encontramos con el dispositivo de uno de los objetos importantes de la memoria compartida, el caché del búfer. La posibilidad de perder información de la RAM es la razón principal de la necesidad de recuperación de una falla. Hoy hablaremos sobre estas herramientas.
Revista
Por desgracia, los milagros no ocurren: para sobrevivir a la pérdida de información en la RAM, todo lo necesario debe escribirse en un disco (u otro dispositivo no volátil) de manera oportuna.
Por lo tanto, esto es lo que se hizo. Junto con los cambios de datos, también se mantiene un
diario de estos cambios. Cuando cambiamos algo en una página en el caché del búfer, creamos un registro en el registro sobre este cambio. El registro contiene la información mínima suficiente para que, si es necesario, el cambio pueda repetirse.
Para que esto funcione, la entrada del diario debe ir necesariamente al disco
antes de que llegue la página modificada. De ahí el nombre: registro de escritura anticipada.
Si ocurre una falla, los datos en el disco están en un estado inconsistente: algunas páginas se escribieron antes, otras más tarde. Pero queda un diario que puede leer y volver a realizar las operaciones que ya se completaron antes de la falla, pero cuyo resultado no llegó al disco.
¿Por qué no obligar a las páginas de datos a escribirse en el disco? ¿Por qué hacer doble trabajo? Resulta muy efectivo.
En primer lugar, un registro es una secuencia secuencial de datos para escribir. Incluso los discos duros funcionan bastante bien con la grabación secuencial. Pero el registro de los datos en sí es aleatorio, porque las páginas están dispersas en el disco de manera más o menos aleatoria.
En segundo lugar, una entrada de diario puede ser mucho más pequeña que una página.
En tercer lugar, al grabar, no tiene que preocuparse por garantizar que los datos en el disco permanezcan consistentes en cualquier momento arbitrario (este requisito complica enormemente la vida).
Y en cuarto lugar, como veremos más adelante, el diario (ya que existe) puede usarse no solo para la recuperación, sino también para la copia de seguridad y la replicación.
Debe registrar todas las operaciones, durante las cuales existe un riesgo de inconsistencia en el disco en caso de falla. En particular, se registran las siguientes acciones:
- cambio de páginas en la memoria caché del búfer (como regla, estas son tablas y páginas de índice), ya que la página cambiada no va inmediatamente al disco;
- confirmación y cancelación de transacciones: el cambio de estado se produce en las memorias intermedias XACT y tampoco llega de inmediato al disco;
- operaciones de archivo (creación y eliminación de archivos y directorios, por ejemplo, creación de archivos al crear una tabla), ya que estas operaciones deben ocurrir simultáneamente con los cambios de datos.
No registrado:
- operaciones con tablas no registradas (no registradas): su nombre habla por sí mismo;
- operaciones con tablas temporales: no tiene sentido, ya que la duración de dichas tablas no excede la duración de la sesión que las creó.
Antes de PostgreSQL 10, los
índices hash no se registraban (solo servían para asignar funciones hash a diferentes tipos de datos), pero ahora esto se ha solucionado.
Dispositivo lógico

Lógicamente, un diario puede considerarse como una secuencia de registros de varias longitudes. Cada registro contiene
datos sobre una determinada operación, precedidos por un
encabezado estándar. El título, entre otras cosas, indica:
- El número de transacción al que pertenece el registro.
- administrador de recursos: el componente del sistema responsable de la grabación;
- suma de comprobación (CRC): le permite determinar la corrupción de datos;
- longitud de registro y enlace al registro anterior.
Los datos en sí tienen un formato y significado diferentes. Por ejemplo, pueden representar algún fragmento de una página que debe escribirse sobre su contenido con un cierto desplazamiento. El administrador de recursos especificado "comprende" cómo interpretar los datos en su registro. Hay administradores separados para las tablas, para cada tipo de índice, para el estado de la transacción, etc. Se puede obtener una lista completa de ellos si lo desea el comando
pg_waldump -r list
Dispositivo físico
En el disco, el registro se almacena como archivos en el directorio $ PGDATA / pg_wal. Cada archivo tiene por defecto 16 MB. El tamaño se puede aumentar para evitar una gran cantidad de archivos en un directorio. Antes de PostgreSQL 11, esto solo se podía hacer al compilar el código fuente, pero ahora puede especificar el tamaño al inicializar el clúster (la
--wal-segsize
).
Las entradas de registro se incluyen en el archivo actual en uso; cuando termina, el siguiente comienza a usarse.
Se asignan buffers especiales para el registro en la memoria compartida del servidor. El tamaño de la memoria caché del diario se establece mediante el parámetro
wal_buffers (el valor predeterminado implica la configuración automática: se asigna 1/32 de la memoria caché del búfer).
El caché de diario está organizado como un caché de memoria intermedia, pero funciona principalmente en el modo de memoria intermedia de anillo: las entradas se agregan al "encabezado" y se escriben en el disco desde la "cola".
Las posiciones de grabación ("cola") e inserción ("cabeza") muestran las funciones pg_current_wal_lsn y pg_current_wal_insert lsn respectivamente:
=> SELECT pg_current_wal_lsn(), pg_current_wal_insert_lsn();
pg_current_wal_lsn | pg_current_wal_insert_lsn --------------------+--------------------------- 0/331E4E64 | 0/331E4EA0 (1 row)
Para referirse a un registro específico, se usa el tipo de datos pg_lsn (LSN = número de secuencia de registro); este es un número de 64 bits que representa el desplazamiento de bytes antes del registro en relación con el comienzo del registro. LSN se emite como dos números de 32 bits en notación hexadecimal.
Puede averiguar en qué archivo encontraremos la posición deseada y con qué desplazamiento desde el principio del archivo:
=> SELECT file_name, upper(to_hex(file_offset)) file_offset FROM pg_walfile_name_offset('0/331E4E64');
file_name | file_offset --------------------------+------------- 000000010000000000000033 | 1E4E64 \ /\ / 0/331E4E64
El nombre del archivo consta de dos partes. Los 8 dígitos hexadecimales superiores muestran el número de la rama de tiempo (se usa al restaurar desde la copia de seguridad), el resto corresponde a los dígitos LSN más altos (y los dígitos LSN inferiores restantes indican el desplazamiento).
Los archivos de registro se pueden ver en el sistema de archivos en el directorio $ PGDATA / pg_wal /, pero a partir de PostgreSQL 10 también se pueden ver con una función especial:
=> SELECT * FROM pg_ls_waldir() WHERE name = '000000010000000000000033';
name | size | modification --------------------------+----------+------------------------ 000000010000000000000033 | 16777216 | 2019-07-08 20:24:13+03 (1 row)
Escribir hacia adelante
Veamos cómo se registra el diario y cómo se proporciona la grabación proactiva. Crea una tabla:
=> CREATE TABLE wal(id integer); => INSERT INTO wal VALUES (1);
Veremos el encabezado de la página de la tabla. Para hacer esto, necesitamos una extensión ya familiar:
=> CREATE EXTENSION pageinspect;
Comencemos la transacción y recordemos la posición de inserción en el registro:
=> BEGIN; => SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/331F377C (1 row)
Ahora hagamos alguna operación, por ejemplo, actualice la línea:
=> UPDATE wal set id = id + 1;
Este cambio se registró en el registro, la posición de inserción ha cambiado:
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/331F37C4 (1 row)
Para garantizar que la página de datos modificados no se envíe al disco antes de la entrada de diario, el LSN de la última entrada de diario relacionada con esta página se almacena en el encabezado de página:
=> SELECT lsn FROM page_header(get_raw_page('wal',0));
lsn ------------ 0/331F37C4 (1 row)
Tenga en cuenta que el diario es común a todo el clúster y que las nuevas entradas entran en él todo el tiempo. Por lo tanto, el LSN en la página puede ser menor que el valor que la función pg_current_wal_insert_lsn acaba de devolver. Pero no pasa nada en nuestro sistema, por lo que los números son los mismos.
Ahora completa la transacción.
=> COMMIT;
El registro de confirmación también va al registro y la posición cambia nuevamente:
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/331F37E8 (1 row)
Commit cambia el estado de una transacción en una estructura llamada XACT (
ya hablamos de ello ). Los estados se almacenan en archivos, pero también usan su propio caché, que ocupa 128 páginas en la memoria compartida. Por lo tanto, para las páginas XACT, se debe realizar un seguimiento del LSN de la última entrada del diario. Pero esta información no se almacena en la página en sí, sino en la RAM.
En algún momento, las entradas de diario creadas se escribirán en el disco. En cuál: hablaremos en otra ocasión, pero en nuestro caso esto ya ha sucedido:
=> SELECT pg_current_wal_lsn(), pg_current_wal_insert_lsn();
pg_current_wal_lsn | pg_current_wal_insert_lsn --------------------+--------------------------- 0/331F37E8 | 0/331F37E8 (1 row)
Después de este punto, los datos y las páginas XACT se pueden sacar del caché. Pero si fuera necesario forzarlos antes, se detectaría y las entradas del diario se verían obligadas a registrarse primero.
Conociendo las dos posiciones LSN, puede obtener el tamaño de las entradas de diario entre ellas (en bytes) simplemente restando una posición de la otra. Solo necesita convertir las posiciones al tipo pg_lsn:
=> SELECT '0/331F37E8'::pg_lsn - '0/331F377C'::pg_lsn;
?column? ---------- 108 (1 row)
En este caso, la actualización de línea y la confirmación requirieron 108 bytes en el registro.
Del mismo modo, puede estimar cuántas entradas de diario genera el servidor por unidad de tiempo en una carga determinada. Esta es información importante que se requerirá durante la configuración (de la que hablaremos la próxima vez).
Ahora usaremos la utilidad pg_waldump para ver las entradas de registro creadas.
La utilidad puede funcionar con el rango LSN (como en este ejemplo) y seleccionar registros para la transacción especificada. Debe ejecutarse en nombre del usuario del sistema operativo postgres, ya que necesita acceso a los archivos de registro en el disco.
postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/331F377C -e 0/331F37E8 000000010000000000000033
rmgr: Heap len (rec/tot): 69/ 69, tx: 101085, lsn: 0/331F377C, prev 0/331F3014, desc: HOT_UPDATE off 1 xmax 101085 ; new off 2 xmax 0, blkref #0: rel 1663/16386/33081 blk 0
rmgr: Transaction len (rec/tot): 34/ 34, tx: 101085, lsn: 0/331F37C4, prev 0/331F377C, desc: COMMIT 2019-07-08 20:24:13.945435 MSK
Aquí vemos los encabezados de las dos entradas.
La primera es la operación
HOT_UPDATE , relacionada con el administrador de recursos del montón. El nombre del archivo y el número de página se indican en el campo blkref y coinciden con la página de la tabla actualizada:
=> SELECT pg_relation_filepath('wal');
pg_relation_filepath ---------------------- base/16386/33081 (1 row)
La segunda entrada es COMMIT, relacionada con el Administrador de recursos de transacciones.
No es el formato más legible, pero puede resolverlo si es necesario.
Recuperación
Cuando iniciamos el servidor, el proceso de postmaster comienza primero y, a su vez, inicia el proceso de inicio, cuya tarea es garantizar la recuperación si ocurre una falla.
Para determinar si se requiere recuperación, el inicio busca en el archivo de control especial $ PGDATA / global / pg_control y observa el estado del clúster. Podemos verificar el estado nosotros mismos usando la utilidad pg_controldata:
postgres$ /usr/lib/postgresql/11/bin/pg_controldata -D /var/lib/postgresql/11/main | grep state
Database cluster state: in production
Un servidor cuidadosamente detenido tendrá el estado "apagado". Si el servidor no funciona y el estado permanece "en producción", esto significa que el DBMS ha caído y luego la recuperación se realizará automáticamente.
Para la recuperación, el proceso de inicio leerá secuencialmente el registro y aplicará entradas a las páginas, si es necesario. Puede verificar la necesidad comparando el LSN de la página en el disco con el LSN de la entrada del diario. Si el LSN de la página es mayor, entonces el registro no es necesario. Pero, de hecho, ni siquiera es posible, porque los registros están diseñados para una aplicación estrictamente coherente.
Hay excepciones Algunos registros se forman como una imagen de página completa (FPI, imagen de página completa), y está claro que dicha imagen se puede aplicar a una página en cualquier estado; todavía borrará todo lo que estaba allí. Se puede aplicar otro cambio en el estado de una transacción a cualquier versión de la página XACT; por lo tanto, dentro de dichas páginas no es necesario almacenar LSN.
El cambio de páginas durante la recuperación ocurre en la memoria caché del búfer, como durante el trabajo normal, para este administrador de correo comienza los procesos de fondo necesarios.
Del mismo modo, las entradas de diario se aplican a los archivos: por ejemplo, si un registro dice que el archivo debe existir, pero no existe, se crea el archivo.
Bueno, al final del proceso de recuperación, todas las tablas no registradas se sobrescriben con "dummies" de sus
capas de inicio .
Esta es una presentación muy simplificada del algoritmo. En particular, no hemos dicho nada sobre dónde comenzar a leer las entradas del diario (esta conversación tendrá que posponerse hasta que se considere el punto de control).
Y la última aclaración. El proceso de recuperación "clásico" consta de dos fases. En la primera fase (avance), las entradas del diario se acumulan y el servidor repite todo el trabajo perdido durante la falla. En el segundo (revertir), las transacciones que no se comprometieron en el momento de la falla se revierten. Pero PostgreSQL no necesita una segunda fase. Como hemos
considerado anteriormente , debido a las peculiaridades de la implementación de transacciones de versiones múltiples, no es necesario revertirlas físicamente; es suficiente que el bit de reparación no se establezca en XACT.
Continuará