Entonces, nos familiarizamos con el dispositivo de la
memoria caché del
búfer y, usando su ejemplo, nos dimos cuenta de que cuando el contenido de la RAM desaparece en caso de falla,
se requiere un
registro de pregrabación para la recuperación. El tamaño de los archivos de registro necesarios y el tiempo de recuperación están limitados debido a un
punto de control ejecutado periódicamente.
En artículos anteriores, ya hemos analizado un número bastante grande de configuraciones importantes, de una forma u otra relacionadas con la revista. En este artículo (el último de esta serie), consideraremos aquellos problemas de ajuste que aún no se han discutido: los niveles de registro y su propósito, así como la confiabilidad y el rendimiento del registro.
Niveles de registro
El propósito principal del registro de pregrabación es proporcionar recuperación después de una falla. Pero, si aún tiene que llevar un diario, puede adaptarse para otras tareas, agregando una cierta cantidad de información adicional. Hay varios niveles de registro. Se establecen mediante el parámetro
wal_level y se organizan de modo que el registro de cada nivel siguiente incluya todo lo que se incluye en el registro del nivel anterior, más algo nuevo.
Mínimo
El nivel mínimo posible se establece mediante el valor
wal_level = mínimo y garantiza solo la recuperación después de una falla. Para ahorrar espacio, las operaciones relacionadas con el procesamiento de datos en masa (como CREATE TABLE AS SELECT o CREATE INDEX) no se registran. En cambio, los datos necesarios se escriben inmediatamente en el disco, y se agrega un nuevo objeto al directorio del sistema y se hace visible cuando se confirma la transacción. Si ocurre una falla durante la operación, los datos ya registrados permanecen invisibles y no violan la consistencia. Si la falla ocurre después de que se completa la operación, todo lo necesario ya ha llegado al disco y no necesita registrarse.
A ver Primero, establezca el nivel requerido (para esto también deberá cambiar otro parámetro:
max_wal_senders ).
=> ALTER SYSTEM SET wal_level = minimal; => ALTER SYSTEM SET max_wal_senders = 0;
student$ sudo pg_ctlcluster 11 main restart
Tenga en cuenta que cambiar el nivel requiere un reinicio del servidor.
Recuerde la posición actual en el registro:
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/353927BC (1 row)
Ahora creemos la tabla (CREAR TABLA COMO SELECCIONAR) y escriba nuevamente la posición en el registro. La cantidad de datos seleccionados por la instrucción SELECT no importa en este caso, por lo que nos limitaremos a una línea.
=> CREATE TABLE wallevel AS SELECT 1 AS n; => SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/353A7DFC (1 row)
Con la utilidad familiar pg_waldump, veamos las entradas de registro.
postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353927BC -e 0/353A7DFC
Algunos detalles, por supuesto, pueden diferir de un lanzamiento a otro, pero en este caso, esto es lo que sucedió. La entrada del administrador Heap2 se refiere a la limpieza, aquí se trata de una limpieza en la página de una de las tablas del catálogo del sistema (los objetos del sistema se distinguen fácilmente a simple vista por el número "corto" en rel):
rmgr: Heap2 len (rec/tot): 59/ 7587, tx: 0, lsn: 0/353927BC, prev 0/35392788, desc: CLEAN remxid 101126, blkref #0: rel 1663/16386/1247 blk 8 FPW
Luego hay un registro sobre cómo obtener el siguiente OID para la tabla que vamos a crear:
rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/35394574, prev 0/353927BC, desc: NEXTOID 82295
Ahora la creación real de la tabla:
rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/35394594, prev 0/35394574, desc: CREATE base/16386/74103
Sin embargo, la inserción de datos en una tabla no se registra. Luego hay numerosas entradas sobre la inserción de filas en diferentes tablas e índices: este PostgreSQL registra la tabla creada en el directorio del sistema (lo doy en forma abreviada):
rmgr: Heap len (rec/tot): 203/ 203, tx: 101127, lsn: 0/353945C0, prev 0/35394594, desc: INSERT off 71, blkref #0: rel 1663/16386/1247 blk 8 rmgr: Btree len (rec/tot): 53/ 685, tx: 101127, lsn: 0/3539468C, prev 0/353945C0, desc: INSERT_LEAF off 37, blkref #0: rel 1663/16386/2703 blk 2 FPW ... rmgr: Btree len (rec/tot): 53/ 2393, tx: 101127, lsn: 0/353A747C, prev 0/353A6788, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW
Y finalmente, la fijación de transacciones:
rmgr: Transaction len (rec/tot): 34/ 34, tx: 101127, lsn: 0/353A7DD8, prev 0/353A747C, desc: COMMIT 2019-07-23 18:59:34.923124 MSK
Réplica
Cuando restauramos el sistema desde la copia de seguridad, comenzamos desde algún estado del sistema de archivos y llevamos gradualmente los datos al punto de recuperación, reproduciendo las entradas de diario archivadas. El número de tales registros puede ser muy grande (por ejemplo, varios días), es decir, el período de recuperación no cubrirá un punto de control, sino muchos. Por lo tanto, está claro que el nivel mínimo del registro no es suficiente: si alguna operación no se registra, simplemente no sabremos que debe repetirse. Para restaurar desde una copia de seguridad,
todas las operaciones deben estar registradas.
Lo mismo es cierto para la replicación: todo lo que no esté registrado no se transferirá a la réplica y no se reproducirá. Pero, si queremos ejecutar solicitudes en una réplica, aún es complicado.
Primero, necesitamos información sobre bloqueos exclusivos que se producen en el servidor primario, ya que pueden entrar en conflicto con las solicitudes en la réplica. Dichos bloqueos se registran y aplican en la réplica (en nombre del proceso de inicio).
En segundo lugar, debe poder crear
instantáneas de datos , y para esto, como recordamos, se necesita información sobre las transacciones en curso. En el caso de una réplica, estamos hablando no solo de transacciones locales, sino también de transacciones en el servidor principal. La única forma de transmitir esta información es escribirla periódicamente en el registro (esto sucede cada 15 segundos).
El nivel de registro, que garantiza tanto la capacidad de recuperación de una copia de seguridad como la posibilidad de replicación física, se establece mediante el valor wal_level =
replica . (Antes de la versión 9.6, había dos niveles separados archive y hot_standby, pero luego se combinaron en uno común).
Comenzando con PostgreSQL 10, es este nivel el que se establece de manera predeterminada (y antes era mínimo). Por lo tanto, solo restablezca los parámetros a los valores predeterminados:
=> ALTER SYSTEM RESET wal_level; => ALTER SYSTEM RESET max_wal_senders;
student$ sudo pg_ctlcluster 11 main restart
Eliminamos la tabla y repetimos exactamente la misma secuencia de acciones que la última vez:
=> DROP TABLE wallevel; => SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/353AF21C (1 row)
=> CREATE TABLE wallevel AS SELECT 1 AS n; => SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/353BE51C (1 row)
Ahora verifique las entradas del diario.
postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353AF21C -e 0/353BE51C
Limpieza, obtención de OID, creación de una tabla y registro en el directorio del sistema; por ahora, todo está como estaba:
rmgr: Heap2 len (rec/tot): 58/ 58, tx: 0, lsn: 0/353AF21C, prev 0/353AF044, desc: CLEAN remxid 101128, blkref #0: rel 1663/16386/1247 blk 8 rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/353AF258, prev 0/353AF21C, desc: NEXTOID 82298 rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/353AF278, prev 0/353AF258, desc: CREATE base/16386/74106 rmgr: Heap len (rec/tot): 203/ 203, tx: 101129, lsn: 0/353AF2A4, prev 0/353AF278, desc: INSERT off 73, blkref #0: rel 1663/16386/1247 blk 8 rmgr: Btree len (rec/tot): 53/ 717, tx: 101129, lsn: 0/353AF370, prev 0/353AF2A4, … rmgr: Btree len (rec/tot): 53/ 2413, tx: 101129, lsn: 0/353BD954, prev 0/353BCC44, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW
Pero algo nuevo. El registro de un bloqueo exclusivo relacionado con el administrador de Standby: en este caso, está bloqueando el número de transacción (por qué es necesario, hablaremos en detalle en la próxima serie de artículos):
rmgr: Standby len (rec/tot): 42/ 42, tx: 101129, lsn: 0/353BE2D8, prev 0/353BD954, desc: LOCK xid 101129 db 16386 rel 74106
Y este es un registro sobre la inserción de filas en nuestra tabla (compare el número de archivo rel con el indicado anteriormente en el registro CREATE):
rmgr: Heap len (rec/tot): 59/ 59, tx: 101129, lsn: 0/353BE304, prev 0/353BE2D8, desc: INSERT+INIT off 1, blkref #0: rel 1663/16386/74106 blk 0
Confirmar registro:
rmgr: Transaction len (rec/tot): 421/ 421, tx: 101129, lsn: 0/353BE340, prev 0/353BE304, desc: COMMIT 2019-07-23 18:59:37.870333 MSK; inval msgs: catcache 74 catcache 73 catcache 74 catcache 73 catcache 50 catcache 49 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 snapshot 2608 relcache 74106 snapshot 1214
Y otro registro, que ocurre periódicamente y no está vinculado a la transacción completada, se refiere al administrador de Standby e informa sobre las transacciones actualmente en curso:
rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 0/353BE4E8, prev 0/353BE340, desc: RUNNING_XACTS nextXid 101130 latestCompletedXid 101129 oldestRunningXid 101130
Lógico
Finalmente, el último nivel se establece mediante el valor del parámetro
wal_level = logical y proporciona la posibilidad de decodificación lógica y replicación lógica. Debe estar habilitado en el servidor de publicación.
Desde el punto de vista de las entradas de diario, este nivel prácticamente no es diferente de la réplica: se agregan registros relacionados con los orígenes de replicación y entradas lógicas arbitrarias que se pueden agregar al registro de la aplicación. Básicamente, la decodificación lógica depende de la información sobre las transacciones en curso, ya que necesita crear una instantánea de los datos para rastrear los cambios en el catálogo del sistema.
Ahora no entraremos en detalles sobre el funcionamiento de la copia de seguridad y la replicación; este es un gran tema para una serie separada de artículos.
Fiabilidad de registro
Está claro que el mecanismo de registro en diario debe ser confiable y proporcionar garantías de la posibilidad de recuperación en cualquier situación (por supuesto, no relacionado con daños al soporte de datos). La confiabilidad está influenciada por muchos factores, de los cuales consideraremos el almacenamiento en caché, la corrupción de datos y la atomicidad de las grabaciones.
Almacenamiento en caché
Existen numerosas memorias caché en la ruta de datos al almacenamiento no volátil (como una unidad de disco duro).
Cuando un programa (cualquiera, pero en nuestro caso PostgreSQL) le pide al sistema operativo que escriba algo en el disco, el sistema operativo transfiere los datos a su caché en la RAM. La grabación real se produce de forma asíncrona, según la configuración del planificador de E / S del sistema operativo.
Cuando el sistema operativo decide escribir datos, caen en la memoria caché de la unidad (disco duro). La electrónica de la unidad también puede retrasar la grabación, por ejemplo, la recopilación de datos en grupos que son más rentables para grabar al mismo tiempo. Y si se utiliza un controlador RAID, aparece otro nivel de almacenamiento en caché entre el sistema operativo y la unidad.
Por lo tanto, si no toma medidas especiales, no está completamente claro cuándo los datos realmente se almacenarán de forma segura. Esto generalmente no es importante, pero hay lugares críticos donde PostgreSQL necesita asegurarse de que los datos se escriban de forma segura. En primer lugar, esto es el registro en diario (si la entrada del diario no llegó al disco, desaparecerá junto con el resto del contenido de la RAM) y un punto de control (debe asegurarse de que las páginas sucias estén realmente escritas en el disco). Pero hay otras situaciones, por ejemplo, la ejecución de operaciones no periodizadas al nivel mínimo, etc.
El sistema operativo proporciona herramientas que deben garantizar la escritura inmediata de datos en la memoria no volátil. Hay varias opciones, pero se reducen a dos principales: se da un comando de sincronización después de la grabación (fsync, fdatasync) o al abrir un archivo (o escribir en él), se indica un indicador especial para la sincronización o incluso la grabación directa, sin pasar por el caché del sistema operativo.
En cuanto al registro, la utilidad pg_test_fsync le permite elegir el método más adecuado para un sistema operativo específico y un sistema de archivos específico, y se instala en el parámetro de configuración
wal_sync_method . Los archivos normales siempre se sincronizan con fsync.
El punto sutil es que al elegir un método, se deben tener en cuenta las características del equipo. Por ejemplo, si usa un controlador compatible con una batería de respaldo, no hay razón para no usar su caché, ya que la batería guardará datos en caso de una falla de energía.
La documentación contiene muchos detalles sobre este tema.
En cualquier caso, la sincronización es costosa y se lleva a cabo no más de lo absolutamente necesario (volveremos a este tema un poco más abajo cuando hablemos del rendimiento).
En términos generales, la sincronización se puede desactivar (el parámetro
fsync es responsable de esto), pero en este caso debe olvidarse de la confiabilidad del almacenamiento. Al deshabilitar
fsync , acepta que los datos pueden perderse irremediablemente en cualquier momento. Probablemente, la única opción razonable para usar esta opción es aumentar temporalmente la productividad, cuando los datos se pueden restaurar fácilmente desde otra fuente (por ejemplo, durante la migración inicial).
Corrupción de datos
El equipo es imperfecto y los datos pueden dañarse en los medios al transmitir datos a través de cables de interfaz, etc. Algunos de estos errores se procesan a nivel de hardware, pero otros no.
Para detectar el problema a tiempo, las entradas de diario siempre se proporcionan con sumas de verificación.
Las páginas de datos también se pueden proteger con sumas de verificación. Por ahora, esto solo se puede hacer cuando se inicializa el clúster, pero en PostgreSQL 12 será posible activarlos y desactivarlos utilizando la utilidad pg_checksums (aunque todavía no está en marcha, pero solo cuando el servidor está detenido).
En un entorno de producción, se deben incluir las sumas de verificación, a pesar de la sobrecarga de su cálculo y control. Esto reduce la probabilidad de que no se detecte una falla a tiempo.
Reduce, pero no elimina.
En primer lugar, las sumas de verificación solo se verifican al acceder a la página; por lo tanto, el daño puede pasar desapercibido hasta que llegue a todas las copias de seguridad. Es por eso que pg_probackup busca sumas de verificación de todas las páginas del clúster durante la copia de seguridad.
En segundo lugar, una página llena de ceros se considera correcta: si el sistema de archivos "anula" el archivo por error, esto puede pasar desapercibido.
En tercer lugar, las sumas de verificación protegen solo la capa principal de los archivos de datos. Las capas restantes y otros archivos (por ejemplo, estados de transacciones XACT) no están protegidos por nada.
Por desgracia
Veamos como funciona. Primero, asegúrese de que las sumas de verificación estén habilitadas (tenga en cuenta que este no es el caso al instalar un paquete en sistemas similares a Debian):
=> SHOW data_checksums;
data_checksums ---------------- on (1 row)
El parámetro
data_checksums es de solo lectura.
Aquí está el archivo en el que se encuentra nuestra tabla:
=> SELECT pg_relation_filepath('wallevel');
pg_relation_filepath ---------------------- base/16386/24890 (1 row)
Detenga el servidor y cambie algunos bytes en la página cero, por ejemplo, elimine la última entrada del registro del encabezado LSN.
student$ sudo pg_ctlcluster 11 main stop
postgres$ dd if=/dev/zero of=/var/lib/postgresql/11/main/base/16386/24890 oflag=dsync conv=notrunc bs=1 count=8
8+0 records in 8+0 records out 8 bytes copied, 0,0083022 s, 1,0 kB/s
En principio, el servidor no se pudo detener. Es suficiente que la página se haya escrito en el disco y se haya forzado a salir de la memoria caché (de lo contrario, el servidor trabajará con la página desde la memoria caché). Pero tal escenario es más difícil de reproducir.
Ahora iniciamos el servidor e intentamos leer la tabla.
student$ sudo pg_ctlcluster 11 main start
=> SELECT * FROM wallevel;
WARNING: page verification failed, calculated checksum 23222 but expected 50884 ERROR: invalid page in block 0 of relation base/16386/24890
¿Pero qué pasa si los datos no se pueden restaurar desde la copia de seguridad? El parámetro
ignore_checksum_failure le permite intentar leer la tabla, naturalmente con el riesgo de obtener datos distorsionados.
=> SET ignore_checksum_failure = on; => SELECT * FROM wallevel;
WARNING: page verification failed, calculated checksum 23222 but expected 50884 n --- 1 (1 row)
Por supuesto, en este caso, todo va bien, porque confundimos solo el título de la página y no los datos en sí.
Y una cosa más. Cuando se activan las sumas de verificación, se escriben fragmentos de mensajes en el registro (los
examinamos anteriormente), ya que un cambio en cualquier bit, incluso no esencial, también conduce a un cambio en la suma de verificación. Cuando las sumas de comprobación están desactivadas, el parámetro
wal_log_hints es responsable de escribir bits de sugerencia en el
registro .
Los cambios en los bits de información sobre herramientas siempre se registran como una
imagen de página completa (FPI, imagen de página completa), lo que aumenta el tamaño del registro en orden. En este caso, tiene sentido habilitar la compresión de imágenes completas utilizando el parámetro
wal_compression (este parámetro apareció en la versión 9.5). A continuación nos fijamos en números específicos.
Registro de atomicidad
Y finalmente, está el problema de la atomicidad del registro. Una página de base de datos toma al menos 8 KB (puede ser 16 o 32 KB), y en un nivel bajo, la grabación se produce en bloques que generalmente son más pequeños (generalmente 512 bytes o 4 KB). Por lo tanto, en caso de una falla de energía, la página de datos puede ser parcialmente grabada. Está claro que durante la recuperación no tiene sentido aplicar entradas de diario ordinarias a dicha página.
Para protección, PostgreSQL le permite escribir en el registro la
imagen completa de la página cuando se cambia por primera vez después del inicio del punto de control (la misma imagen se registra cuando cambian los bits de información sobre herramientas). El parámetro
full_page_writes controla esto , y está habilitado de forma predeterminada.
Si se encuentra una imagen de página durante la recuperación en un registro, se escribe incondicionalmente (sin verificación LSN) en el disco: hay más confianza en ella, porque, como cualquier registro de registro, está protegida por una suma de verificación. Y ya se aplican entradas de diario regulares a esta imagen correcta garantizada.
Aunque PostgreSQL excluye el espacio no asignado de la imagen de la página completa (anteriormente analizamos la estructura de bloques), el volumen de entradas de diario generadas aumenta significativamente. Como ya se mencionó, la situación se puede mejorar comprimiendo las imágenes completas (parámetro
wal_compression ).
Para sentir de alguna manera el cambio en el tamaño del registro, realizaremos un experimento simple usando la utilidad pgbench. Inicialicemos:
student$ pgbench -i test
dropping old tables... creating tables... generating data... 100000 of 100000 tuples (100%) done (elapsed 0.15 s, remaining 0.00 s) vacuuming... creating primary keys... done.
La
opción full_page_writes está habilitada:
=> SHOW full_page_writes;
full_page_writes ------------------ on (1 row)
Ejecute el punto de interrupción e inmediatamente ejecute la prueba durante 30 segundos.
=> CHECKPOINT; => SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/38E04A08 (1 row)
student$ pgbench -T 30 test
starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 26851 latency average = 1.117 ms tps = 895.006720 (including connections establishing) tps = 895.095229 (excluding connections establishing)
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/3A69C478 (1 row)
Tamaño de registro:
=> SELECT pg_size_pretty('0/3A69C478'::pg_lsn - '0/38E04A08'::pg_lsn);
pg_size_pretty ---------------- 25 MB (1 row)
Ahora apague el parámetro full_page_writes:
=> ALTER SYSTEM SET full_page_writes = off; => SELECT pg_reload_conf();
Y repite el experimento.
=> CHECKPOINT; => SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/3A69C530 (1 row)
student$ pgbench -T 30 test
starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 27234 latency average = 1.102 ms tps = 907.783080 (including connections establishing) tps = 907.895326 (excluding connections establishing)
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/3BE87658 (1 row)
Tamaño de registro:
=> SELECT pg_size_pretty('0/3BE87658'::pg_lsn - '0/3A69C530'::pg_lsn);
pg_size_pretty ---------------- 24 MB (1 row)
Sí, el tamaño ha disminuido, pero no es tan significativo como cabría esperar.
La razón es que el clúster se inicializa con sumas de verificación en las páginas de datos y, por lo tanto, aún debe escribir imágenes de página completa en el registro al cambiar los bits de información sobre herramientas. Estos datos (en nuestro caso) representan aproximadamente la mitad del volumen total, que se puede ver mirando las estadísticas:
postgres$ /usr/lib/postgresql/11/bin/pg_waldump --stats -p /var/lib/postgresql/11/main/pg_wal -s 0/3A69C530 -e 0/3BE87658
Type N (%) Record size (%) FPI size (%) ---- - --- ----------- --- -------- --- XLOG 1721 ( 1,03) 84329 ( 0,77) 13916104 (100,00) Transaction 27235 ( 16,32) 926070 ( 8,46) 0 ( 0,00) Storage 1 ( 0,00) 42 ( 0,00) 0 ( 0,00) CLOG 1 ( 0,00) 30 ( 0,00) 0 ( 0,00) Standby 4 ( 0,00) 240 ( 0,00) 0 ( 0,00) Heap2 27522 ( 16,49) 1726352 ( 15,76) 0 ( 0,00) Heap 109691 ( 65,71) 8169121 ( 74,59) 0 ( 0,00) Btree 756 ( 0,45) 45380 ( 0,41) 0 ( 0,00) -------- -------- -------- Total 166931 10951564 [44,04%] 13916104 [55,96%]
Por compacidad, eliminé las filas cero de la tabla. Preste atención a la línea total (Total) y compare el tamaño de las imágenes completas (tamaño FPI) con el tamaño de los registros ordinarios (Tamaño de registro).
El parámetro
full_page_writes solo se puede deshabilitar si el sistema de archivos y el hardware utilizados por ellos mismos garantizan la grabación atómica. Pero, como podemos ver, no hay una gran razón para esto (suponiendo que las sumas de verificación estén incluidas).
Ahora veamos cómo ayuda la compresión.
=> ALTER SYSTEM SET full_page_writes = on; => ALTER SYSTEM SET wal_compression = on; => SELECT pg_reload_conf();
Repite el mismo experimento.
=> CHECKPOINT; => SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/3BE87710 (1 row)
student$ pgbench -T 30 test
starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 26833 latency average = 1.118 ms tps = 894.405027 (including connections establishing) tps = 894.516845 (excluding connections establishing)
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/3CBD3EA8 (1 row)
Tamaño de registro:
=> SELECT pg_size_pretty('0/3CBD3EA8'::pg_lsn - '0/3BE87710'::pg_lsn);
pg_size_pretty ---------------- 13 MB (1 row)
Conclusión: en presencia de una gran cantidad de imágenes de página completa (debido a sumas de verificación o
escrituras de página completa, es decir, casi siempre), lo más probable es que tenga sentido usar la compresión a pesar de que esto carga el procesador.
Rendimiento
Durante el funcionamiento normal del servidor, se produce una grabación secuencial continua de archivos de registro. Como no hay acceso aleatorio, los discos duros normales también hacen frente a esta tarea. Pero esta naturaleza de la carga es significativamente diferente de cómo se accede a los archivos de datos.
Por lo tanto, generalmente es ventajoso colocar el registro en un disco físico separado (o matriz de discos) montado en el sistema de archivos del servidor. En lugar del directorio $ PGDATA / pg_wal, debe crear un enlace simbólico al directorio correspondiente.
Hay un par de situaciones en las que los archivos de registro no solo deben escribirse, sino también leerse. El primero es un caso comprensible de recuperación después de una falla. El segundo es menos trivial. Ocurre si se usa la replicación de transmisión y la réplica no logra recibir entradas de diario mientras todavía están en los búferes de RAM del servidor principal. Luego, el proceso de walsender tiene que leer los datos necesarios del disco. Hablaremos de esto con más detalle cuando lleguemos a la replicación.
El registro se lleva a cabo en uno de dos modos:
- síncrono: cuando se confirma una transacción, la continuación del trabajo es imposible hasta que todas las entradas de diario sobre esta transacción estén en el disco;
- asíncrono: la transacción se completa de inmediato y el registro se escribe en segundo plano.
El modo síncrono está determinado por el parámetro
synchronous_commit y está habilitado de forma predeterminada.
Dado que la sincronización está asociada con E / S real (es decir, lenta), es beneficioso realizarla lo menos posible. Para hacer esto, el proceso de servicio que completa la transacción y escribe un registro toma una breve pausa, determinada por el parámetro
commit_delay . ,
commit_siblings . , . , , - .
commit_siblings = 5,
commit_delay = 0, .
commit_delay , OLTP-.
LSN ( , ). .
( D ACID) — , . , ( COMMIT ) .
,
synchronous_commit = off ( local).
wal writer, (
wal_writer_delay = 200ms ).
, , WAL. , , , , . (, : , , .)
, ( ) — ?
, , .
La grabación asincrónica es más eficiente que la grabación sincrónica: la confirmación de los cambios no espera a la grabación. Sin embargo, la confiabilidad disminuye: los datos capturados pueden desaparecer en caso de falla si transcurre menos de 3 × tiempo de wal_writer_delay entre la confirmación y la falla (que, por defecto, es un poco más de medio segundo).Una elección difícil - eficiencia o confiabilidad - permanece con el administrador del sistema.Tenga en cuenta: a diferencia de desactivar la sincronización ( fsync = off), el modo asíncrono no conduce a la imposibilidad de recuperación. En caso de falla, el sistema aún restaurará un estado consistente, pero quizás algunas de las últimas transacciones estarán ausentes.Parámetro Synchronous_commitse puede configurar como parte de transacciones individuales. Esto le permite aumentar la productividad al sacrificar la confiabilidad de solo una parte de la transacción. Digamos que las transacciones financieras siempre deben corregirse sincrónicamente, y los mensajes de chat a veces pueden descuidarse.En realidad, ambos modos funcionan juntos. Incluso con la confirmación sincrónica, los registros de transacciones largos se escribirán de forma asincrónica para liberar los buffers WAL. Y si, al restablecer una página desde la memoria caché del búfer, resulta que la entrada de diario correspondiente aún no está en el disco, se restablecerá inmediatamente en modo síncrono.Para tener una idea de lo que ofrece la confirmación asincrónica, intentamos repetir la prueba pgbench en este modo. => ALTER SYSTEM SET synchronous_commit = off; => SELECT pg_reload_conf();
student$ pgbench -T 30 test
starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 45439 latency average = 0.660 ms tps = 1514.561710 (including connections establishing) tps = 1514.710558 (excluding connections establishing)
900 (tps), — 1500. , , , .
. - , . Gracias a todos!
, .