Documentos de Académico
Documentos de Profesional
Documentos de Cultura
242 Optimización de consultas para un buen rendimiento
Tiempo de Ejecución: 89.536ms
(8 filas)
PostgreSQL se da cuenta de que la cantidad de grupos ahora es mucho mayor y cambia rápidamente su estrategia.
El problema es que un hash que contiene tantas entradas no cabe en la memoria. Por lo tanto, la estrategia alternativa es
ordenar en disco:
prueba=# MOSTRAR trabajo_mem;
trabajo_mem
4 MB
(1 fila)
Como podemos ver, la variable work_mem gobierna el tamaño del hash que usa la cláusula GROUP BY. Dado que hay
demasiadas entradas, PostgreSQL tiene que encontrar una estrategia que no requiera que mantengamos todo el conjunto de
datos en la memoria. La solución es ordenar los datos por ID y agruparlos. Una vez que se han ordenado los datos,
PostgreSQL puede moverse hacia abajo en la lista y formar un grupo tras otro. Si se cuenta el primer tipo de valor, se lee el
resultado parcial y se puede emitir. Luego, se puede procesar el siguiente grupo. Una vez que el valor en la lista ordenada
cambia al moverse hacia abajo, nunca volverá a aparecer; por lo tanto, el sistema sabe que un resultado parcial está listo.
Para acelerar la consulta, se puede establecer un valor más alto para la variable work_mem sobre la marcha (y, por
supuesto, globalmente):
test=# SET work_mem A '1 GB';
COLOCAR
Ahora, el plan, una vez más, contará con un agregado de hash rápido y eficiente:
test=# SET enable_hashagg TO activado;
COLOCAR
test=# EXPLICAR ANALIZAR SELECCIONAR id, contar(*) DESDE t_test GRUPO POR 1;
PLAN DE CONSULTA
HashAgregate (costo=4082,00...6082,00 filas=200000 ancho=12) (tiempo real=76,967...118,926 filas=200000
bucles=1)
Clave de grupo: id
> Exploración secuencial en t_test
(costo=0.00..3082.00 filas=200000 ancho=4)
(tiempo real=0.008..13.570 filas=200000 bucles=1)
Machine Translated by Google
Ajuste de parámetros para un buen rendimiento de consultas 243
Tiempo de planificación: 0,073 ms
Tiempo de ejecución: 126.456 ms
(5 filas)
PostgreSQL sabe (o al menos asume) que los datos caben en la memoria y cambian al plan más rápido.
Como puede ver, el tiempo de ejecución es menor. La consulta no será tan rápida como en el caso del nombre
GROUP BY porque se deben calcular muchos más valores hash, pero podrá ver un beneficio agradable y confiable
en la gran mayoría de los casos. Como se indicó anteriormente, este comportamiento depende de la versión de bits.
Acelerar la clasificación
La variable work_mem no solo acelera la agrupación. También puede tener un impacto muy agradable en cosas simples
como la clasificación, que es un mecanismo esencial que todos los sistemas de bases de datos del mundo dominan.
La siguiente consulta muestra una operación simple utilizando la configuración predeterminada de 4 MB:
test=# SET work_mem TO predeterminado;
COLOCAR
test=# EXPLICAR ANALIZAR SELECCIONAR * DESDE t_test ORDENAR POR nombre, id;
PLAN DE CONSULTA
Ordenar (costo=38293.40..38793.40 filas=200000 ancho=9)
(tiempo real=59.599..71.535 filas=200000 bucles=1)
Clave de clasificación: nombre, id
Método de clasificación: Disco de combinación externo: 3728kB
> Seq Scan en t_test (costo = 0.00..3082.00 filas = 200000 ancho = 9)
(tiempo real=0.018..13.436 filas=200000 bucles=1)
Tiempo de planificación: 0,111 ms
Tiempo de Ejecución: 78.038ms
(6 filas)
PostgreSQL necesita 13,4 milisegundos para leer los datos y más de 50 milisegundos para ordenarlos. Debido a la poca
cantidad de memoria disponible, la clasificación debe realizarse utilizando archivos temporales. El método de combinación
de disco externo solo requiere pequeñas cantidades de RAM, pero tiene que enviar datos intermedios a un dispositivo de
almacenamiento comparativamente lento, lo que, por supuesto, conduce a un bajo rendimiento.
Machine Translated by Google
244 Optimización de consultas para un buen rendimiento
Aumentar la configuración de la variable work_mem hará que PostgreSQL use más memoria para ordenar:
test=# SET work_mem A '1 GB';
COLOCAR
test=# EXPLICAR ANALIZAR SELECCIONAR * DESDE t_test ORDENAR POR nombre, id;
PLAN DE CONSULTA
Ordenar (costo=34873.90..35373.90 filas=200000 ancho=9)
(tiempo real=47.159..53.283 filas=200000 bucles=1)
Clave de clasificación: nombre, id
Método de clasificación: Quicksort Memoria: 17082kB
> Seq Scan en t_test (costo=0.00..3082.00 filas=200000
ancho = 9) (tiempo real = 0.032..14.824 filas = 200000 bucles = 1)
Tiempo de planificación: 0,143 ms
Tiempo de Ejecución: 59.526ms
(6 filas)
Dado que ahora hay suficiente memoria, la base de datos hará toda la clasificación en la memoria y, por lo tanto,
acelerará el proceso de manera espectacular. La ordenación tarda solo 39 milisegundos ahora, lo que es una mejora
de 7 veces en comparación con la consulta que teníamos anteriormente. Más memoria conducirá a una clasificación
más rápida y acelerará el sistema.
Hasta ahora, ha visto dos mecanismos que se pueden usar para ordenar datos: disco de combinación externa y memoria de
ordenación rápida. Además de estos dos mecanismos, existe un tercer algoritmo, memoria heapsort topN. Se puede usar para
proporcionarle solo las N filas superiores:
test=# EXPLICAR ANALIZAR SELECCIONAR *
DESDE t_test
ORDENAR POR nombre, id
LÍMITE 10;
PLAN DE CONSULTA
Límite (costo=7403.93..7403.95 filas=10 ancho=9)
(tiempo real=31.837..31.838 filas=10 bucles=1)
> Ordenar (costo=7403.93..7903.93 filas=200000 ancho=9)
(tiempo real=31.836..31.837 filas=10 bucles=1)
Clave de clasificación: nombre, id
Machine Translated by Google
Ajuste de parámetros para un buen rendimiento de consultas 245
Método de clasificación: topN heapsort Memoria: 25kB
> Exploración secuencial en t_test
(costo=0.00..3082.00 filas=200000 ancho=9)
(tiempo real=0.011..13.645 filas=200000 bucles=1)
Tiempo de planificación: 0,053 ms
Tiempo de ejecución: 31.856 ms
(7 filas)
El algoritmo es ultrarrápido y la consulta completa se realizará en poco más de 30 milisegundos. La parte de
clasificación ahora es de solo 18 milisegundos y, por lo tanto, es casi tan rápida como leer los datos en primer lugar.
En PostgreSQL 13, se ha agregado un nuevo algoritmo:
test=# CREAR ÍNDICE idx_id EN t_test (id);
CREAR ÍNDICE
test=# explicar analizar SELECCIONAR * DESDE t_test ORDENAR POR id, nombre;
PLAN DE CONSULTA
Clasificación incremental (costo=0,46...15289,42 filas=200000 ancho=9)
(tiempo real=0.047..71.622 filas=200000
bucles=1)
Clave de clasificación: id, nombre
Clave preordenada: id
Grupos de clasificación completa: 6250 Método de clasificación: clasificación rápida
Memoria promedio: 26 kB Memoria máxima: 26 kB
> Escaneo de índice usando idx_id en t_test
(costo=0.42..6289.42 filas=200000 ancho=9)
(tiempo real=0.032..37.965 filas=200000 bucles=1)
Tiempo de planificación: 0,165 ms
Tiempo de Ejecución: 83.681ms
(7 filas)
La ordenación incremental se usa si los datos ya están ordenados por algunas variables. En este caso, idx_id devolverá
datos ordenados por id. Todo lo que tenemos que hacer es ordenar los datos ya ordenados por nombre.
Tenga en cuenta que la variable work_mem se asigna por operación. En teoría, una consulta puede necesitar la
variable work_mem más de una vez. No es una configuración global, es realmente por operación. Por lo tanto,
debe configurarlo con cuidado.
Machine Translated by Google
246 Optimización de consultas para un buen rendimiento
Lo único que debemos tener en cuenta es que hay muchos libros que afirman que establecer la variable
work_mem demasiado alta en un sistema OLTP puede hacer que su servidor se quede sin memoria. Sí,
si 1000 personas ordenan 100 MB al mismo tiempo, esto puede provocar fallas en la memoria. Sin
embargo, ¿espera que el disco pueda manejar eso? Lo dudo. La solución es solo repensar lo que estás haciendo.
De todos modos, la clasificación simultánea de 100 MB 1000 veces no debería ocurrir en un sistema OLTP. Considere
implementar índices adecuados, escribir mejores consultas o simplemente repensar sus requisitos. En cualquier circunstancia,
clasificar tantos datos al mismo tiempo con tanta frecuencia es una mala idea: deténgase antes de que esas cosas detengan
su aplicación.
Agilizar las tareas administrativas
Hay más operaciones que tienen que hacer alguna clasificación o asignación de memoria de algún tipo. Los administrativos,
como la cláusula CREATE INDEX, no se basan en la variable work_mem y usan la variable maintenance_work_mem en su
lugar. Así es como funciona:
test=# DROP INDEX idx_id;
ÍNDICE DE GOTA
test=# SET mantenimiento_trabajo_mem TO '1 MB';
COLOCAR
prueba=# \ tiempo
El tiempo está encendido.
test=# CREAR ÍNDICE idx_id EN t_test (id);
CREAR ÍNDICE
Tiempo: 104.268ms
Como puede ver, crear un índice en 2 millones de filas lleva alrededor de 100 milisegundos, lo que es realmente lento. Por
lo tanto, la variable maintenance_work_mem se puede usar para acelerar la clasificación, que es esencialmente lo que hace
la cláusula CREATE INDEX:
test=# SET mantenimiento_trabajo_mem TO '1 GB';
COLOCAR
test=# CREAR ÍNDICE idx_id2 EN t_test (id);
CREAR ÍNDICE
Tiempo: 46.774ms
La velocidad ahora se ha duplicado solo porque la clasificación se ha mejorado mucho.
Hay más trabajos administrativos que pueden beneficiarse de más memoria. Las más destacadas son la cláusula VACUUM
(para limpiar índices) y la cláusula ALTER TABLE. Las reglas para la variable maintenance_work_mem son las mismas que
para la variable work_mem. La configuración es por operación, y solo la memoria requerida se asigna sobre la marcha.
Machine Translated by Google
Hacer uso de consultas paralelas 247
En PostgreSQL 11, se agregó una característica adicional al motor de la base de datos: PostgreSQL ahora
puede crear índices btree en paralelo, lo que puede acelerar drásticamente la indexación de tablas grandes.
El parámetro que se encarga de configurar el paralelismo es el siguiente:
test=# MOSTRAR max_parallel_maintenance_workers;
max_parallel_maintenance_workers
(1 fila)
max_parallel_maintenance_workers controla la cantidad máxima de procesos de trabajo que puede usar CREATE
INDEX. Como para cada operación paralela, PostgreSQL determinará la cantidad de trabajadores en función del
tamaño de la tabla. Al indexar tablas grandes, la creación de índices puede ver mejoras drásticas. Hice algunas
pruebas exhaustivas y resumí mis hallazgos en una de mis publicaciones de blog: https://www.cybertec
postgresql.com/en/postgresqlparallelcreate indexforbetterperformance/. Aquí, obtendrá información
importante sobre el rendimiento relacionada con la creación de índices.
Sin embargo, la creación de índices no es lo único que admite la simultaneidad.
Hacer uso de consultas paralelas
Desde la versión 9.6, PostgreSQL admite consultas paralelas. Esta compatibilidad con el paralelismo se
ha mejorado gradualmente con el tiempo, y la versión 11 ha agregado aún más funciones a esta importante
característica. En esta sección, veremos cómo funciona el paralelismo y qué se puede hacer para acelerar
las cosas.
Antes de profundizar en los detalles, es necesario crear algunos datos de muestra de la siguiente manera:
test=# CREAR TABLA t_parallel COMO
SELECCIONE * DESDE generar_series (1, 25000000) COMO id;
SELECCIONE 25000000
Después de cargar los datos iniciales, podemos ejecutar nuestra primera consulta paralela. Un recuento simple mostrará
cómo se ve una consulta paralela en general:
test=# explicar SELECCIONAR recuento(*) DESDE t_parallel;
PLAN DE CONSULTA
Finalizar Agregado (costo=241829.17..241829.18 filas=1 ancho=8)
> Reunir (costo=241828,96...241829,17 filas=2 ancho=8)
Machine Translated by Google
248 Optimización de consultas para un buen rendimiento
Trabajadores previstos: 2
> Agregado parcial
(costo=240828.96..240828.97 filas=1 ancho=8)
> Parallel Seq Scan en t_parallel (costo = 0.00..214787.17
filas = 10416717 ancho = 0)
(5 filas)
Echemos un vistazo detallado al plan de ejecución de la consulta. Primero, PostgreSQL realiza un
escaneo secuencial paralelo. Esto implica que PostgreSQL usará más de una CPU para procesar la
tabla (bloque por bloque) y creará agregados parciales. El trabajo del nodo Gather es recopilar los datos
y pasarlos para realizar la agregación final. El nodo Gather es el final del paralelismo. Es importante
mencionar que el paralelismo (actualmente) nunca está anidado. Nunca puede haber un nodo Gather
dentro de otro nodo Gather. En este ejemplo, PostgreSQL se ha decidido por dos procesos de trabajo. ¿Porqué es eso?
Consideremos la siguiente variable:
test=# MOSTRAR max_parallel_workers_per_gather;
max_parallel_workers_per_gather
2
(1 fila)
max_parallel_workers_per_gather limita a dos la cantidad de procesos de trabajo permitidos debajo del
nodo de recopilación. Lo importante es esto: si una tabla es pequeña, nunca usará paralelismo. El
tamaño de una tabla debe ser de al menos 8 MB, según lo definido por la siguiente configuración:
test=# MOSTRAR min_parallel_table_scan_size;
min_parallel_table_scan_size
8MB
(1 fila)
Ahora, la regla para el paralelismo es la siguiente: el tamaño de la tabla debe triplicarse para que PostgreSQL
agregue un proceso de trabajo más. En otras palabras, para obtener 4 trabajadores adicionales, necesita al menos
81 veces más datos. Esto tiene sentido porque el tamaño de su base de datos aumenta 100 veces y el sistema de
almacenamiento generalmente no es 100 veces más rápido. Por lo tanto, el número de núcleos útiles es algo limitado.
Machine Translated by Google
Hacer uso de consultas paralelas 249
Sin embargo, nuestra tabla es bastante grande:
prueba=# \d+
Lista de relaciones
publico | t_paralelo | mesa | hora | permanente | 864 megabytes
(1 fila)
En este ejemplo, max_parallel_workers_per_gather limita la cantidad de núcleos. Si cambiamos esta configuración,
PostgreSQL decidirá sobre más núcleos:
test=# SET max_parallel_workers_per_gather A 10;
COLOCAR
test=# explicar SELECCIONAR recuento(*) DESDE t_parallel;
PLAN DE CONSULTA
Finalizar Agregado (costo=174120.82..174120.83 filas=1 ancho=8)
> Reunir (costo=174120.30..174120.81 filas=5 ancho=8)
Trabajadores previstos: 5
> Agregado parcial
(costo=173120.30..173120.31 filas=1 ancho=8)
> Parallel Seq Scan en t_parallel (costo = 0.00..160620.24
filas = 5000024 ancho = 0)
JAT:
Funciones: 4
Opciones: Incrustación falsa, Optimización falsa, Expresiones
verdadero, deformante verdadero
(8 filas)
En este caso, tenemos 5 trabajadores (como se esperaba).
Sin embargo, hay casos en los que deseará que la cantidad de núcleos que se utilizan para una determinada
tabla sea mucho mayor. Solo imagine una base de datos de 200 GB, 1 TB de RAM y un solo usuario. Este
usuario podría usar toda la CPU sin dañar a nadie más. ALTER TABLE se puede usar para anular lo que
acabamos de discutir:
test=# ALTER TABLE t_parallel SET (parallel_workers = 9);
ALTERAR TABLA
Machine Translated by Google
250 Optimización de consultas para un buen rendimiento
Si desea invalidar la regla x3 para determinar la cantidad de CPU deseadas, puede usar ALTER
TABLE para codificar explícitamente la cantidad de CPU.
Tenga en cuenta que max_parallel_workers_per_gather seguirá siendo efectivo y servirá como límite superior.
Si observa el plan, verá que la cantidad de núcleos en realidad se considerará (eche un vistazo a
Trabajadores planificados para ver esta información):
test=# explicar SELECCIONAR recuento(*) DESDE t_parallel;
PLAN DE CONSULTA
Finalizar agregado (costo=146343,32...146343,33 filas=1 ancho=8)
> Reunir (costo=146342.39..146343.30 filas=9 ancho=8)
Trabajadores previstos: 9
> Agregado parcial
(costo=145342,39..145342,40 filas=1 ancho=8)
> Escaneo de secuencia paralela en t_parallel
(costo=0.00..138397.91 filas=2777791 ancho=0)
JAT:
Funciones: 4
Opciones: Incrustación falsa, Optimización falsa, Expresiones
true, Deforming true (8 filas)
Tiempo: 2.454ms
Sin embargo, eso no significa que esos núcleos también se usen:
test=# explicar analizar SELECT count(*) FROM t_parallel;
PLAN DE CONSULTA
Finalizar agregado (costo=146343,32...146343,33 filas=1 ancho=8)
(tiempo real=1375.606..1375.606 filas=1
bucles=1)
> Reunir (costo=146342.39..146343.30 filas=9 ancho=8)
(tiempo real=1374.411..1376.442 filas=8 bucles=1)
Trabajadores previstos: 9
Trabajadores Lanzados: 7
> Agregado parcial
(costo=145342.39..145342.40 filas=1 ancho=8)
Machine Translated by Google
Hacer uso de consultas paralelas 251
(tiempo real=1347.573..1347.573 filas=1
bucles = 8)
> Escaneo de secuencia paralela en t_parallel
(costo=0.00..138397.91 filas=2777791 ancho=0)
(tiempo real=0.049..844.601 filas=3125000
bucles = 8)
Tiempo de planificación: 0,028 ms
JAT:
Funciones: 18
Opciones: Alineación falsa, Optimización falsa, Expresiones verdadera, Deformación verdadera
Temporización: Generación 1.703 ms, Inline 0.000 ms,
Optimización 1.119 ms, Emisión 14.707
ms, Total 17.529 ms
Tiempo de Ejecución: 1164.922ms
(12 filas)
Como puede ver, solo se lanzaron siete núcleos, a pesar de que se planificaron nueve procesos.
¿Cuál es la razón de esto? En este ejemplo, entran en juego dos variables más:
prueba = # MOSTRAR max_worker_processes;
max_worker_processes
8
(1 fila)
prueba = # MOSTRAR max_parallel_workers;
max_parallel_workers
8
(1 fila)
El primer proceso le dice a PostgreSQL cuántos procesos de trabajo están generalmente disponibles. max_parallel_
Workers indica cuántos trabajadores están disponibles para consultas paralelas. ¿Por qué hay dos parámetros?
Los procesos en segundo plano no solo los usa la infraestructura de consultas en paralelo, sino que también se pueden
usar para otros fines y, por lo tanto, la mayoría de los desarrolladores deciden usar dos parámetros.
En general, en Cybertec (https://www.cybertecpostgresql.com) tendemos a establecer max_worker_processes en la cantidad
de CPU en el servidor. Esto se debe a que parece que usar más no suele ser beneficioso.
Machine Translated by Google
252 Optimización de consultas para un buen rendimiento
¿Qué puede hacer PostgreSQL en paralelo?
Como ya mencionamos en esta sección, el soporte para el paralelismo ha mejorado gradualmente desde
PostgreSQL 9.6. En cada versión, se ha agregado una nueva funcionalidad.
Las siguientes son las operaciones más importantes que se pueden hacer en paralelo:
• Exploraciones secuenciales paralelas
• Escaneos de índices paralelos (solo árboles B)
• Análisis de montones de mapas de bits paralelos
• Combinaciones paralelas (todos los tipos de combinaciones)
• Creación de árboles B paralelos (CREAR ÍNDICE)
• Agregación paralela
• Anexo paralelo:
VACÍO
CREAR ÍNDICE
En PostgreSQL 11, se agregó soporte para la creación de índices paralelos. Las operaciones de clasificación normales
aún no son completamente paralelas; hasta ahora, solo la creación de btree se puede realizar en paralelo. Para controlar
la cantidad de paralelismo, necesitamos aplicar el siguiente parámetro:
test=# MOSTRAR max_parallel_maintenance_workers;
max_parallel_maintenance_workers
(1 fila)
Las reglas para el paralelismo son básicamente las mismas que para las operaciones normales.
Si desea acelerar la creación de índices, considere consultar una de mis publicaciones de blog relacionadas con la
creación y el rendimiento de índices: https://www.cybertecpostgresql.com/en/postgresqlparallelcreateindexfor
better actuación/.
Paralelismo en la práctica
Ahora que hemos introducido los conceptos básicos del paralelismo, aprenderemos lo que significa en el mundo real.
Echemos un vistazo a la siguiente consulta:
test=#explicar SELECCIONAR * DESDE t_parallel;
PLAN DE CONSULTA
Machine Translated by Google
Introducción a la compilación JIT 253
Seq Scan en t_parallel (costo=0.00..360621.20 filas=25000120
ancho=4)
(1 fila)
¿Por qué PostgreSQL no usa una consulta paralela? La tabla es lo suficientemente grande y el trabajador de
PostgreSQL está disponible, entonces, ¿por qué no usa una consulta paralela? La respuesta es que la comunicación
entre procesos es realmente costosa. Si PostgreSQL tiene que enviar filas entre procesos, una consulta puede ser
más lenta que en el modo de proceso único. El optimizador usa parámetros de costo para castigar la comunicación entre procesos:
#parallel_tuple_cost = 0.1
Cada vez que se mueva una tupla entre procesos, se sumarán 0,1 puntos al cálculo. Para ver cómo PostgreSQL ejecuta consultas
paralelas cuando está obligado a hacerlo, he incluido el siguiente ejemplo:
test=# SET force_parallel_mode TO activado;
COLOCAR
test=#explicar SELECCIONAR * DESDE t_parallel;
PLAN DE CONSULTA
Reunir (costo=1000.00..2861633.20 filas=25000120 ancho=4)
Trabajadores previstos: 1
Copia única: verdadero
> Seq Scan en t_parallel (costo = 0.00..360621.20
filas = 25000120 ancho = 4)
(4 filas)
Como puede ver, los costos son más altos que en el modo de un solo núcleo. En el mundo real, este es un tema
importante porque muchas personas se preguntarán por qué PostgreSQL opta por un solo núcleo.
En un ejemplo real, también es importante ver que más núcleos no conducen automáticamente a más velocidad.
Se requiere un delicado acto de equilibrio para encontrar el número perfecto de núcleos.
Introducción a la compilación JIT
La compilación JIT ha sido uno de los temas candentes en PostgreSQL 11. Ha sido una tarea importante y los
primeros resultados parecen prometedores. Sin embargo, comencemos con los fundamentos: ¿de qué se trata la
compilación JIT ? Cuando ejecuta una consulta, PostgreSQL tiene que resolver muchas cosas en tiempo de
ejecución. Cuando se compila PostgreSQL, no sabe qué tipo de consulta ejecutará a continuación, por lo que debe
estar preparado para todo tipo de escenarios.
Machine Translated by Google
254 Optimización de consultas para un buen rendimiento
El núcleo es genérico, lo que significa que puede hacer todo tipo de cosas. Sin embargo, cuando está en una
consulta, solo desea ejecutar la consulta actual lo más rápido posible, no otras cosas aleatorias. El punto es
que, en tiempo de ejecución, sabe mucho más sobre lo que tiene que hacer que en tiempo de compilación
(es decir, cuando se compila PostgreSQL). Ese es exactamente el punto: cuando la compilación JIT está
habilitada, PostgreSQL verificará su consulta y, si toma mucho tiempo, se creará un código altamente
optimizado para su consulta sobre la marcha (justo a tiempo).
Configuración de JIT
Para usar JIT, debe agregarse en tiempo de compilación (cuando se ejecuta ./configure). Están disponibles
las siguientes opciones de configuración:
withllvm build con soporte JIT basado en LLVM
...
Ruta LLVM_CONFIG al comando llvmconfig
Algunas distribuciones de Linux incluyen un paquete adicional que contiene soporte para JIT. Si desea utilizar
JIT, asegúrese de que esos paquetes estén instalados.
Una vez que se haya asegurado de que JIT está disponible, los siguientes parámetros de configuración estarán
disponibles para que pueda ajustar la compilación JIT para sus consultas:
#jit = en # permitir la compilación JIT
#jit_provider = 'llvmjit' #jit_above_cost = # Implementación JIT a usar # realizar
100000 compilación JIT si
# disponible
# y consulta mas cara, # 1 deshabilita
#jit_optimize_above_cost = 500000 # optimizar funciones JITed
# si la consulta es #
más cara, 1 deshabilita
#jit_inline_above_cost = 500000 # intento de insertar operadores y
# funciones si la consulta es
# más caro, # 1 desactiva
jit_above_cost significa que JIT solo se considera si el costo esperado es de al menos 100,000 unidades.
¿Por qué es eso relevante? Si una consulta no es lo suficientemente larga, la sobrecarga de la compilación puede
ser mucho mayor que la ganancia potencial. Por lo tanto, solo se intenta la optimización. Sin embargo, hay dos más
Machine Translated by Google
Introducción a la compilación JIT 255
parámetros: se intentan optimizaciones muy profundas si se considera que la consulta es más cara que
500.000 unidades. En este caso, las llamadas a funciones se insertarán.
En este punto, PostgreSQL solo admite máquinas virtuales de bajo nivel (LLVM) como backend JIT. Tal vez
backends adicionales también estarán disponibles en el futuro. Por ahora, LLVM hace un muy buen trabajo
y cubre la mayoría de los entornos que se utilizan en contextos profesionales.
Ejecutando consultas
Para mostrarle cómo funciona JIT, compilaremos un ejemplo simple. Comencemos por crear una tabla grande, una que
contenga una gran cantidad de datos. Recuerde, la compilación JIT solo es útil si la operación es lo suficientemente grande.
Para empezar, 50 millones de filas deberían ser suficientes. El siguiente ejemplo muestra cómo llenar la tabla:
jit=# CREAR TABLA t_jit COMO
SELECCIONAR (aleatorio()*10000)::int COMO x,
(aleatorio()*100000)::int AS y,
(aleatorio()*1000000)::int AS z
DESDE generar_series(1, 50000000) COMO id;
SELECCIONE 50000000
jit=# ANÁLISIS DE VACÍO t_jit;
VACÍO
En este caso, usaremos la función aleatoria para generar algunos datos. Para mostrarle cómo funciona JIT y facilitar la
lectura de los planes de ejecución, puede desactivar las consultas paralelas. JIT funciona bien con consultas paralelas,
pero los planes de ejecución tienden a ser mucho más largos:
jit=# SET max_parallel_workers_per_gather A 0;
COLOCAR
jit=# SET jit TO desactivado;
COLOCAR
jit=# explicar (analizar, detallado)
SELECCIONE promedio(z+ypi()),
promedio(ypi()), max(x/pi())
DESDE t_jit DONDE
((y+z))>((yx)*0.000001);
PLAN DE CONSULTA
Agregado (costo=1936901,68...1936901,69 filas=1 ancho=24)
(tiempo real=20617.425..20617.425 filas=1 bucles=1)
Salida: avg((((z + y))::doble precisión
Machine Translated by Google
256 Optimización de consultas para un buen rendimiento
'3.14159265358979'::doble precisión)),
avg(((y)::doble precisión
'3.14159265358979'::doble precisión)),
max(((x)::doble precisión / '3.14159265358979'::doble
precisión))
> Exploración secuencial en public.t_jit
(costo=0.00..1520244.00 filas=16666307 ancho=12)
(tiempo real=0.061..15322.555 filas=50000000 bucles=1)
Salida: x, y, z
Filtro: (((t_jit.y + t_jit.z))::numérico >
(((t_jit.y t_jit.x))::numérico * 0.000001))
Tiempo de planificación: 0,078 ms
Tiempo de ejecución: 20617.473ms
(7 filas)
En este caso, la consulta tardó 20 segundos.
Nota importante :
he utilizado una función VACUUM para garantizar que todos los bits de sugerencia, etc., se hayan configurado
correctamente para garantizar una comparación justa entre una consulta JIT y una consulta normal.
Repitamos esta prueba con JIT habilitado:
jit=# SET jit TO activado;
COLOCAR
jit=# explicar (analizar, detallado)
SELECCIONE promedio(z+ypi()), promedio(ypi()), max(x/pi())
DESDE t_jit DONDE
((y+z))>((yx)*0.000001);
PLAN DE CONSULTA
Agregado (costo=1936901,68...1936901,69 filas=1 ancho=24)
(tiempo real=15585.788..15585.789 filas=1 bucles=1)
Salida: avg((((z + y))::doble precisión
'3.14159265358979'::doble precisión)),
avg(((y)::doble precisión '3.14159265358979'::doble
precisión)),
Machine Translated by Google
Resumen 257
max(((x)::doble precisión /
'3.14159265358979'::doble precisión))
> Exploración secuencial en public.t_jit
(costo=0.00..1520244.00 filas=16666307 ancho=12)
(tiempo real=81.991..13396.227 filas=50000000 bucles=1)
Salida: x, y, z Filtro:
(((t_jit.y + t_jit.z))::numeric >
(((t_jit.y t_jit.x))::numérico * 0.000001))
Tiempo de planificación: 0,135 ms
JAT:
Funciones: 5
Opciones: Alineación verdadera, Optimización verdadera, Expresiones verdaderas,
Temporización
verdadera deformante: Generación 2.942 ms, Inline 15.717 ms,
Optimización 40.806ms,
Emisión 25.233ms,
Total 84.698ms
Tiempo de ejecución: 15588.851 ms
(11 filas)
En este caso, puede ver que la consulta es mucho más rápida que antes, lo que ya es significativo. En algunos
casos, los beneficios pueden ser incluso mayores. Sin embargo, tenga en cuenta que volver a compilar el código
también implica un esfuerzo adicional, por lo que no tiene sentido para todos los tipos de consultas.
Comprender el optimizador de PostgreSQL puede ser muy beneficioso para proporcionar un buen rendimiento.
Tiene sentido profundizar en estos temas para asegurar un buen desempeño.
Resumen
En este capítulo, se analizaron varias optimizaciones de consultas. Aprendió sobre el optimizador y sobre varias
optimizaciones internas, como plegado constante, vista en línea, uniones y mucho más.
Todas estas optimizaciones contribuyen a un buen rendimiento y ayudan a acelerar considerablemente las cosas.
Ahora que hemos cubierto esta introducción a las optimizaciones, en el próximo capítulo, el Capítulo 7, Escritura de
procedimientos almacenados, hablaremos sobre los procedimientos almacenados. Conocerás todas las opciones que tiene
PostgreSQL con las que podemos manejar código definido por el usuario.
Machine Translated by Google
Machine Translated by Google
7
Escribir procedimientos almacenados
En el Capítulo 6, Optimización de consultas para un buen rendimiento, aprendimos mucho sobre el optimizador, así como sobre
las optimizaciones que se realizan en el sistema. En este capítulo, aprenderemos acerca de los procedimientos almacenados y
cómo usarlos de manera eficiente y sencilla. Aprenderá de qué se compone un procedimiento almacenado , qué idiomas están
disponibles y cómo puede acelerar las cosas de manera agradable. Además de eso, se le presentarán algunas de las funciones
más avanzadas de PL/pgSQL y aprenderá a escribir un buen código del lado del servidor.
En este capítulo se tratarán los siguientes temas:
• Comprender los lenguajes de procedimientos almacenados
• Exploración de varios lenguajes de procedimientos almacenados
• Mejora de funciones
• Uso de funciones para varios propósitos
Al final de este capítulo, podrá escribir procedimientos almacenados buenos y eficientes.
Comprender los lenguajes de procedimientos almacenados
Cuando se trata de funciones y procedimientos almacenados, PostgreSQL difiere significativamente de otros sistemas de bases
de datos. La mayoría de los motores de bases de datos lo obligan a usar un determinado lenguaje de programación para escribir
código del lado del servidor. Microsoft SQL Server ofrece TransactSQL, mientras que Oracle lo alienta a usar PL/ SQL.
PostgreSQL no lo obliga a usar un lenguaje determinado; en cambio, le permite decidir sobre lo que sabe y le gusta más.
La razón por la que PostgreSQL es tan flexible también es bastante interesante en un sentido histórico. Hace muchos años , uno
de los desarrolladores de PostgreSQL más conocidos, Jan Wieck, que había escrito innumerables parches en sus inicios, tuvo la
idea de utilizar Tool Command Language (Tcl) como lenguaje de programación del lado del servidor. El problema era que nadie
quería usar Tcl y nadie quería tener estas cosas en el motor de la base de datos. La solución al problema fue hacer la interfaz de
idioma
Machine Translated by Google
260 Escribir procedimientos almacenados
tan flexible que básicamente cualquier lenguaje podría integrarse fácilmente con PostgreSQL. En este
punto nació la cláusula CREATE LANGUAGE. Aquí está la sintaxis de CREAR IDIOMA:
test=# \h CREAR IDIOMA
Comando: CREAR IDIOMA
Descripción: definir un nuevo lenguaje procesal
Sintaxis:
CREAR [ O REEMPLAZAR ] [ DE CONFIANZA ] [PROCEDIMIENTO]
Nombre del lenguaje
HANDLER manejador_de_llamadas [ EN LÍNEA manejador_en línea ]
[ Función de validación del VALIDADOR ]
CREAR [ O REEMPLAZAR ] [ DE CONFIANZA ] [PROCEDIMIENTO]
Nombre del lenguaje
URL: https://www.postgresql.org/docs/15/sqlcreatelanguage.html
Hoy en día, se pueden usar muchos lenguajes diferentes para escribir funciones y procedimientos almacenados. La
flexibilidad que se ha agregado a PostgreSQL realmente ha valido la pena; ahora podemos elegir entre un amplio
conjunto de lenguajes de programación.
¿Cómo maneja PostgreSQL exactamente los lenguajes? Si echamos un vistazo a la sintaxis de la cláusula
CREATE LANGUAGE, veremos algunas palabras clave:
• HANDLER: esta función es en realidad el vínculo entre PostgreSQL y cualquier lenguaje externo que desee
utilizar. Está a cargo de mapear las estructuras de datos de PostgreSQL a lo que necesite el lenguaje y
ayuda a pasar el código.
• VALIDADOR: Es el policía de la infraestructura. Si está disponible, estará a cargo de entregar sabrosos
errores de sintaxis al usuario final. Muchos lenguajes pueden analizar el código antes de ejecutarlo.
PostgreSQL puede usar eso y decirle si una función es correcta o no cuando la crea. Desafortunadamente,
no todos los idiomas pueden hacer esto, por lo que, en algunos casos, aún tendrá problemas para
aparecer en tiempo de ejecución.
• EN LÍNEA: si está presente, PostgreSQL podrá ejecutar bloques de código anónimo utilizando esta función
de controlador.
Después de verificar esas opciones, podemos pasar a comprender algunas diferencias fundamentales entre
funciones y procedimientos.
Machine Translated by Google
Comprender los lenguajes de procedimientos almacenados 261
Comprender los fundamentos de los procedimientos almacenados frente a
las funciones
Antes de profundizar en la anatomía de un procedimiento almacenado, es importante hablar sobre funciones
y procedimientos en general. El término procedimiento almacenado se ha utilizado tradicionalmente para
hablar de una función. Por lo tanto, es esencial que entendamos la diferencia entre una función y un procedimiento.
Una función es parte de una instrucción SQL normal y no se le permite iniciar o confirmar transacciones.
Aquí hay un ejemplo:
SELECCIONE func(id) DESDE large_table;
Supongamos que func(id) se llama 50 millones de veces. Si usa la función llamada Confirmar, ¿qué debería suceder
exactamente? Es imposible simplemente finalizar una transacción en medio de una consulta y lanzar una nueva . Se
violaría todo el concepto de integridad transaccional, consistencia, etc.
Por el contrario, un procedimiento puede controlar transacciones e incluso ejecutar múltiples transacciones una
tras otra . Sin embargo, no puede ejecutarlo dentro de una instrucción SELECT. En su lugar, debe invocar CALL.
La siguiente lista muestra la sintaxis del comando CALL:
prueba=# \h LLAMADA
Dominio: LLAMAR
Descripción: invocar un procedimiento
Sintaxis:
Nombre de llamada ( [ argumento ] [, ...] )
URL: https://www.postgresql.org/docs/15/sqlcall.html
Por lo tanto, existe una distinción fundamental entre funciones y procedimientos. La terminología que encontrará en
Internet no siempre es clara. Sin embargo, debe ser consciente de esas importantes diferencias. En PostgreSQL, las
funciones han existido desde el principio. Sin embargo, el concepto de un procedimiento, como se describe en esta
sección, es nuevo y solo se introdujo en PostgreSQL 11. En este capítulo, veremos las funciones y los procedimientos
en detalle. Comencemos por entender la anatomía de una función.
La anatomía de una función.
Antes de profundizar en un lenguaje específico, veremos la anatomía de una función típica. Para fines de demostración,
veamos la siguiente función, que solo suma dos números:
test=# CREAR O REEMPLAZAR FUNCIÓN mysum(int, int)
DEVOLUCIONES int AS
'
SELECCIONE $1 + $2;
Machine Translated by Google
262 Escribir procedimientos almacenados
' IDIOMA 'sql';
CREAR FUNCIÓN
Lo primero que hay que observar es que esta función está escrita en SQL. PostgreSQL necesita saber qué
lenguaje estamos usando, por lo que debemos especificarlo en la definición.
Tenga en cuenta que el código de la función se pasa a PostgreSQL como una cadena ('). Esto es algo notable
porque permite que una función se convierta en una caja negra para la maquinaria de ejecución.
En otros motores de bases de datos, el código de la función no es una cadena, sino que se adjunta directamente a la
declaración. Esta capa de abstracción simple es lo que le da al administrador de funciones de PostgreSQL todo su poder.
Dentro de la cadena, básicamente puede usar todo lo que el lenguaje de programación de su elección tiene para ofrecer.
En este ejemplo, simplemente sumaremos dos números que se han pasado a la función. Se
utilizan dos variables enteras. La parte importante aquí es que PostgreSQL le proporciona
sobrecarga de funciones. En otras palabras, la función mysum(int, int) no es lo mismo que la
función mysum(int8, int8).
PostgreSQL ve estas cosas como dos funciones distintas. La sobrecarga de funciones es una buena
característica; sin embargo, debe tener mucho cuidado de no implementar accidentalmente demasiadas
funciones si su lista de parámetros cambia de vez en cuando. Asegúrese siempre de que realmente se eliminen
las funciones que ya no se necesitan.
La cláusula CREATE OR REPLACE FUNCTION no cambiará la lista de parámetros. Por lo tanto, puede usarlo
solo si la firma no cambia. Se producirá un error o simplemente implementará una nueva función.
Ejecutemos la función mysum:
prueba=# SELECCIONA mysum(10, 20);
mysum
30
(1 fila)
El resultado aquí es 30, lo cual no es realmente sorprendente. Después de esta introducción a las funciones, es importante
centrarse en el siguiente tema importante, las citas.
Introducción a la cotización del dólar
Pasar código a PostgreSQL como una cadena es muy flexible. Sin embargo, el uso de comillas simples puede ser un problema.
En muchos lenguajes de programación, las comillas simples aparecen con frecuencia. Para poder usar
estas comillas, debe escaparlas al pasar la cadena a PostgreSQL. Durante muchos años, este ha sido el
Machine Translated by Google
Comprender los lenguajes de procedimientos almacenados 263
procedimiento estándar. Afortunadamente, esos viejos tiempos han pasado y hay nuevos medios disponibles para
pasar el código a PostgreSQL. Uno de ellos es la cotización en dólares, como se muestra en el siguiente código:
test=# CREAR O REEMPLAZAR FUNCIÓN mysum(int, int)
DEVOLUCIONES int AS
$$
SELECCIONE $1 + $2;
$$ IDIOMA 'sql';
CREAR FUNCIÓN
En lugar de usar comillas para comenzar y terminar cadenas, simplemente puede usar $$. Actualmente, dos idiomas
han asignado un significado a $$. En Perl, así como en scripts de Bash, $$ representa el ID del proceso. Para superar
este pequeño obstáculo, podemos usar $ antes de casi cualquier cosa para comenzar y terminar la cadena. El
siguiente ejemplo muestra cómo funciona:
test=# CREAR O REEMPLAZAR FUNCIÓN mysum(int, int)
DEVOLUCIONES int AS
$cuerpo$
SELECCIONE $1 + $2;
$cuerpo$ IDIOMA 'sql';
CREAR FUNCIÓN
Toda esta flexibilidad le permite superar el problema de cotizar de una vez por todas. Siempre que la cadena inicial y
la cadena final coincidan, no habrá ningún problema.
Hacer uso de bloques de código anónimo
Hasta ahora, hemos escrito los procedimientos almacenados más simples posibles y también hemos aprendido a
ejecutar código. Sin embargo, hay más en la ejecución del código que solo funciones completas. Además de
funciones, PostgreSQL permite el uso de bloques de código anónimo. La idea es ejecutar el código que se necesita solo una vez.
Este tipo de ejecución de código es especialmente útil para hacer frente a tareas administrativas. Los bloques
de código anónimo no toman parámetros y no se almacenan permanentemente en la base de datos ya que no
tienen nombres.
Aquí hay un ejemplo simple que muestra un bloque de código anónimo en acción:
prueba=# HACER
$$
COMENZAR
LEVANTAR AVISO 'hora actual: %', ahora();
FIN;
Machine Translated by Google
264 Escribir procedimientos almacenados
$$ IDIOMA 'plpgsql';
AVISO: hora actual: 20221110 11:18:08.640424+01
HACER
En este ejemplo, el código solo emite un mensaje y se cierra. De nuevo, el bloque de código tiene que saber qué
idioma usa. Esta cadena se pasa a PostgreSQL usando cotizaciones simples en dólares.
Uso de funciones y transacciones
Como sabe, todo lo que expone PostgreSQL en el espacio del usuario es una transacción. Lo mismo, por
supuesto, se aplica si está escribiendo funciones. Una función siempre es parte de la transacción en la que se
encuentra. No es autónoma, como un operador o cualquier otra operación.
Aquí hay un ejemplo:
test=# SELECCIONA ahora(), mysum(id, id)
DESDE generar_series(1, 3) COMO id; | mysum
ahora
+
20211123 11:25:54.800394+01 | 20211123 2
11:25:54.800394+01 | 20211123 11:25:54.800394+01 4
| 6
(3 filas)
Las tres llamadas a funciones ocurren en la misma transacción. Esto es importante de entender porque implica que
no puede hacer demasiado control de flujo transaccional dentro de una función. ¿ Qué sucede cuando se confirma
la segunda llamada de función? Simplemente no puede funcionar.
Sin embargo, Oracle tiene un mecanismo que permite transacciones autónomas. La idea es que incluso si una
transacción se revierte, es posible que aún se necesiten algunas partes y se deben conservar. Un ejemplo clásico
es el siguiente:
1. Inicie una función para buscar datos secretos.
2. Agregue una línea de registro al documento para indicar que alguien ha modificado estos importantes datos secretos.
3. Confirme la línea de registro pero revierta el cambio.
4. Conservar la información, indicando que se ha intentado modificar los datos.
Las transacciones autónomas se pueden utilizar para resolver problemas como este. La idea es poder realizar
una transacción dentro de la transacción principal de forma independiente. En este caso, prevalecerá la entrada
en la tabla de registro, mientras que el cambio se revertirá.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 265
A partir de PostgreSQL 15.0, no se implementan transacciones autónomas. Sin embargo, ya existen
parches que implementan esta característica. Todavía está por verse cuándo estas características
llegarán al núcleo.
Para darle una idea de cómo funcionarán las cosas, aquí hay un fragmento de código basado en los primeros parches:
...
COMO
$$
DECLARAR
PRAGMA AUTÓNOMO_TRANSACCIÓN;
COMENZAR
PARA i EN 0..9 BUCLE
INICIAR TRANSACCIÓN;
INSERTAR EN VALORES test1 (i);
SI i % 2 = 0 ENTONCES
COMPROMETERSE;
DEMÁS
RETROCEDER;
TERMINARA SI;
FIN DEL BUCLE;
RETORNO 42;
FIN;
$$;
...
El objetivo de este ejemplo es mostrarle que podemos decidir si confirmar o revertir la transacción autónoma sobre la
marcha.
Exploración de varios lenguajes de procedimientos almacenados
Como ya dijimos en este capítulo, PostgreSQL le brinda el poder de escribir funciones y almacenar procedimientos en
varios lenguajes. Las siguientes opciones están disponibles y se envían junto con el núcleo de PostgreSQL:
• SQL
• PL/pgSQL
Machine Translated by Google
266 Escribir procedimientos almacenados
• PL/Perl y PL/PerlU
• PL/Python
• PL/Tcl y PL/TclU
SQL es la opción obvia para escribir funciones, y debe usarse siempre que sea posible, ya que le da la mayor
libertad al optimizador. Sin embargo, si desea escribir código un poco más complejo, PL/ pgSQL podría ser el
lenguaje de su elección.
PL/pgSQL ofrece control de flujo y mucho más. En este capítulo, se mostrarán algunas de las características
más avanzadas y menos conocidas de PL/pgSQL, pero tenga en cuenta que este capítulo no pretende ser
un tutorial completo sobre PL/pgSQL.
El núcleo contiene código para ejecutar funciones del lado del servidor en Perl. Básicamente, la lógica es la
misma aquí. El código se pasará como una cadena y Perl lo ejecutará. Recuerda que PostgreSQL no habla
Perl; simplemente tiene el código para pasar las cosas al lenguaje de programación externo.
Tal vez haya notado que Perl y Tcl están disponibles en dos versiones: lenguaje confiable (PL/Perl y
PL/Tcl) y lenguaje no confiable (PL/PerlU y PL/TclU). La diferencia entre un idioma confiable y uno no
confiable es realmente importante. En PostgreSQL, un idioma se carga directamente en la conexión
de la base de datos. Por lo tanto, el lenguaje puede hacer muchas cosas críticas. Para deshacerse de
los problemas de seguridad, se inventó el concepto de lenguajes confiables. La idea es que un idioma
confiable esté restringido al núcleo mismo del idioma, por lo tanto, no es posible hacer lo siguiente:
• Incluir bibliotecas
• Abrir sockets de red •
Realizar llamadas al sistema de cualquier tipo, lo que incluiría abrir archivos
Perl ofrece algo llamado modo corrupto, que se usa para implementar esta característica en PostgreSQL. Perl se
restringirá automáticamente al modo de confianza y generará un error si está a punto de ocurrir una violación de
seguridad. En modo no confiable, todo es posible; por lo tanto, solo el superusuario puede ejecutar código que no sea de confianza.
Si desea ejecutar código confiable y no confiable, debe activar ambos idiomas, es decir, plperl y plperlu (pltcl y
pltclu, respectivamente).
Python actualmente solo está disponible como un lenguaje que no es de confianza; por lo tanto, los administradores
deben tener mucho cuidado con la seguridad en general. Una función que se ejecuta en modo no confiable puede
eludir todos los mecanismos de seguridad que aplica PostgreSQL. Solo tenga en cuenta que Python se ejecuta
como parte de la conexión de su base de datos y no es responsable de la seguridad.
Comencemos con el tema más esperado de este capítulo.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 267
Introducción a PL/pgSQL
En esta sección, se le presentarán algunas de las funciones más avanzadas de PL/pgSQL, que son
importantes para escribir código adecuado y altamente eficiente.
Antes de sumergirnos en algunos temas más avanzados, podemos echar un vistazo a algunas cosas más simples.
Estructuración de código PL/pgSQL
Antes de profundizar, queremos volver a la anatomía de una función simple. En el caso de PL/pgSQL, estamos
hablando de un lenguaje orientado a bloques. ¿Qué significa en la vida real? Lo primero que puede ver es un
bloque DECLARE. En este bloque se declaran las variables. Luego hay un bloque BEGIN, que contiene el
código real:
CREAR O REEMPLAZAR FUNCIÓN calculadora_inversion(
IN v_importe numérico, IN v_interés numérico,
EN v_años int)
DEVOLUCIONES numérico AS
$$
DECLARAR
v_sum ALIAS POR $1; v_resultado
numérico := 0;
COMENZAR
v_resultado := v_cantidad
* pow(1 + v_interés, v_años);
RETORNO v_resultado;
FIN;
$$ IDIOMA 'plpgsql';
En nuestro ejemplo, hay un poco más para ver. Lo que ves en la vida real es a menudo el concepto de
parámetros IN/OUT. Podemos nombrar esos parámetros pasados a una función y usarlos directamente en
lugar de usar $1 y $2. Lo que también es posible es usar alias. v_sum se puede usar como v_amount, por lo
que el efecto de un alias y un parámetro con nombre es prácticamente el mismo desde el punto de vista de la codificación.
Por supuesto, no es necesario asignar código a una variable antes de devolverla. Podemos devolver directamente
el resultado de la expresión. Sin embargo, sirve como ejemplo para que podamos ver cómo funcionan las cosas.
Llamemos a la función:
test=# SELECT calculadora_inversion(1000, 0.1, 2);
calculadora_de_inversiones
Machine Translated by Google
268 Escribir procedimientos almacenados
1210.0000000000000000
(1 fila)
El resultado no es una gran sorpresa.
Consideraciones de rendimiento
Antes de profundizar en PL/pgSQL y sus conceptos de lenguaje, tiene sentido comprender algunas
consideraciones básicas de rendimiento: en PostgreSQL puede elegir entre muchos lenguajes diferentes.
Sin embargo, no todos ellos son creados iguales. Si está buscando un buen rendimiento, tiene sentido
ceñirse a SQL siempre que sea necesario. PL/pgSQL es un buen lenguaje pero viene con un poco de sobrecarga.
Para operaciones simples que no necesitan control de flujo, use SQL; será mucho más rápido.
Para probar este punto, implementé una segunda encarnación de esta función haciendo lo mismo en SQL:
CREAR O REEMPLAZAR FUNCIÓN
simple_invest(numérico, numérico, numérico)
DEVOLUCIONES numérico AS
$$
SELECCIONE $1 * pow(1 + $2, $3);
$$ IDIOMA 'sql';
Comparar el rendimiento de esas funciones es fácil. En mi ejemplo, simplemente he usado
generar_series para ejecutar la función 1 millón de veces:
test=# explicar analizar
SELECCIONE inversión_calculadora(x, 0.1, 2)
DESDE generar_series(1, 1000000) COMO x;
PLAN DE CONSULTA
Escaneo de función en generar_series x
(costo=0.00..262500.00 filas=1000000 ancho=32)
(tiempo real=83.927..1237.593 filas=1000000 bucles=1)
Tiempo de planificación: 0,073 ms
JAT:
Funciones: 4
Opciones: Incrustación falsa, Optimización falsa,
Expresiones verdaderas, Deformantes verdaderas
Temporización: Generación 1.354 ms, Inline 0.000 ms,
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 269
Optimización 4.150 ms, Emisión 10.002 ms, Total 15.507 ms
Tiempo de Ejecución: 1345.603ms
(7 filas)
Necesitamos 1,3 segundos para generar 1 millón de filas y llamar a la función. Hagamos lo mismo usando la función SQL
simple:
test=# explicar analizar SELECT simple_invest(x, 0.1, 2)
DESDE generar_series(1, 1000000) COMO x;
PLAN DE CONSULTA
Escaneo de función en generar_series x
(costo=0.00..15000.00 filas=1000000 ancho=32)
(tiempo real=69.795..281.639 filas=1000000 bucles=1)
Tiempo de planificación: 0,114 ms
Tiempo de Ejecución: 310.512ms
(3 filas)
Tomó 0,31 segundos, esa es una gran diferencia que debe tenerse en cuenta. Por supuesto, no todas las
funciones se pueden escribir en SQL simple y tampoco tendría sentido hacerlo. Sin embargo, para funciones
simples, puede marcar una gran diferencia porque el optimizador puede alinear cosas y optimizarlas.
Bucles y control de flujo
El control de flujo no es algo que pueda hacer fácilmente en SQL. PL/pgSQL definitivamente será necesario para
lograr operaciones más complejas:
1. En el siguiente ejemplo, queremos agregar algunos controles de cordura a nuestros datos entrantes:
CREAR O REEMPLAZAR FUNCIÓN calculadora_inversion(
IN v_importe numérico, IN v_interés numérico,
EN v_años int)
DEVOLUCIONES numérico AS
$$
COMENZAR
SI v_años < 1
ENTONCES
RAISE EXCEPTION 'usar valores > 1 año en lugar de %',
v_años;
ELSEIF v_años > 100
Machine Translated by Google
270 Escribir procedimientos almacenados
ENTONCES
AUMENTAR ADVERTENCIA 'predecir más de 100 años no es
recomendado';
DEMÁS
RETORNO v_importe * pow(1 + v_interés, v_años);
TERMINARA SI;
VOLVER NULO;
FIN;
$$ IDIOMA 'plpgsql';
En PL/pgSQL puede usar la sintaxis simple IF/ELSE. Lo que es importante aquí es que no agregamos un
bloque DECLARE esta vez, ya que podemos escaparnos sin declarar variables.
2. Llamemos a la función:
test=# SELECT calculadora_inversion(1000, 0.1, 2); ERROR: use valores> 1 año en
lugar de 2 CONTEXTO: función PL/pgSQL inversión_ calculadora
(numérico, numérico, entero) línea 5 en RAISE
3. RAISE EXCEPTION activará un error, saldrá de la función y hará que falle toda la instrucción SQL , mientras
que RAISE WARNING simplemente activará el envío de un mensaje de registro:
test=# SELECCIONA calculadora_de_inversión(
1000, 0,1, 2344523);
ADVERTENCIA: no se recomienda predecir más de 100 años
calculadora_de_inversiones
(1 fila)
En este caso, el código devuelve NULL y nos advierte que el período es simplemente demasiado largo.
Pasemos ahora al manejo de las comillas y el formato de cadena.
Manejo de citas y el formato de cadena
Una de las cosas más importantes en la programación de bases de datos es citar. Si no usa las comillas adecuadas,
seguramente tendrá problemas con la inyección de SQL y abrirá brechas de seguridad inaceptables.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 271
¿Qué es la inyección SQL?
• Consideremos el siguiente ejemplo:
CREAR FUNCIÓN roto(texto) DEVOLUCIONES void COMO
$$
DECLARAR
texto v_sql;
COMENZAR
v_sql := 'SELECCIONAR nombre de esquema
DESDE pg_tables
WHERE nombre de tabla = ''' || $1 || '''';
LEVANTAR AVISO 'v_sql: %', v_sql;
DEVOLVER;
FIN;
$$ IDIOMA 'plpgsql';
En este ejemplo, el código SQL simplemente se pega sin preocuparse por la seguridad.
Todo lo que estamos haciendo aquí es usar el || operador para concatenar cadenas. Esto funciona bien si
ejecuta consultas normales.
• Considere el siguiente ejemplo, que muestra un código roto:
SELECCIONE roto ('t_test');
Sin embargo, tenemos que estar preparados para las personas que intentan explotar sus sistemas.
• Considere el siguiente ejemplo:
SELECT roto('''; DROP TABLE t_test; ');
Ejecutar la función con este parámetro mostrará un problema.
• El siguiente código muestra la inyección SQL clásica:
AVISO: v_sql: SELECCIONE nombre de esquema DESDE pg_tables
'
WHERE nombretabla = ''; DROP TABLA t_test;
CONTEXT: función PL/pgSQL rota (texto) línea 6 en RAISE
roto
(1 fila)
Machine Translated by Google
272 Escribir procedimientos almacenados
Dejar caer una tabla cuando solo desea hacer una búsqueda no es algo deseable.
Definitivamente no es aceptable hacer que la seguridad de su aplicación dependa de los
parámetros que se pasan a sus declaraciones.
• Para evitar la inyección de SQL, PostgreSQL ofrece varias funciones; estos deben usarse en todo momento para
garantizar que su seguridad permanezca intacta:
test=# SELECT quote_literal(E'o''reilly'), quote_ident(E'o''reilly'); cita_literal
| cita_ident
+
'o''reilly' | "o'reilly" (1 fila)
La función quote_literal escapará una cadena de tal manera que ya nada malo pueda pasar .
Agregará todas las comillas alrededor de la cadena y evitará los caracteres problemáticos
dentro de la cadena. Por lo tanto, no es necesario iniciar y finalizar la cadena manualmente.
• La segunda función que se muestra aquí es quote_ident. Se puede utilizar para citar nombres de objetos
correctamente. Tenga en cuenta que se utilizan comillas dobles, que es exactamente lo que se necesita para
manejar los nombres de las tablas. El siguiente ejemplo muestra cómo usar nombres complejos:
test=# CREATE TABLE "Algún nombre estúpido" ("ID" int);
CREAR MESA
test=# \d "Algún nombre estúpido"
Tabla "público. Algún nombre estúpido"
Columna | Tipo | Intercalación | Anulable | Por defecto
++++
IDENTIFICACIÓN
| entero | | |
Normalmente, todos los nombres de las tablas en PostgreSQL están en minúsculas. Sin embargo, si se utilizan
comillas dobles , los nombres de los objetos pueden contener letras mayúsculas. En general, no es una buena idea
usar este tipo de trucos, ya que tendrías que usar comillas dobles todo el tiempo, lo que puede ser un poco inconveniente.
• Ahora que ha tenido una introducción básica a las comillas, es importante echar un vistazo a
cómo se manejan los valores NULL. El siguiente código muestra cómo NULL es tratado por
la función quote_ literal:
test=# SELECCIONE quote_literal(NULL);
cita_literal
(1 fila)
Si llama a la función quote_literal en un valor NULL, simplemente devolverá NULL. No
hay necesidad de cuidar de citar en este caso.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 273
• PostgreSQL proporciona aún más funciones para ocuparse explícitamente de un valor NULL:
prueba = # SELECCIONE cita_nullable (123), cita_nullable
(NULL); cita_nullable | cita_nullable
+
'123' | NULO (1 fila)
• No solo es posible citar cadenas y nombres de objetos; también es posible utilizar PL/pgSQL integrado
para formatear y preparar consultas completas. La belleza aquí es que puede usar la función de
formato para agregar parámetros a una declaración. Así es como funciona:
CREAR FUNCIÓN simple_format() DEVUELVE texto COMO $$
DECLARAR
v_cadena de texto;
v_resultado texto;
COMENZAR
v_string := format('SELECT nombre de esquema|| ''.'' || nombre de tabla
DESDE pg_tables
DONDE %I = $1
AND %I = $2', 'nombre de esquema', 'nombre de tabla');
EJECUTAR v_string UTILIZANDO 'public', 't_test'
EN v_resultado;
LEVANTAR AVISO 'resultado: %', v_resultado;
RETORNO v_cadena;
FIN;
$$ IDIOMA 'plpgsql';
Los nombres de los campos se pasan a la función de formato. Finalmente, la cláusula USING de la
instrucción EXECUTE está ahí para agregar los parámetros a la consulta, que luego se ejecuta.
Nuevamente, la belleza aquí es que no puede ocurrir una inyección de SQL.
• Esto es lo que sucede cuando se llama a la función simple_format:
prueba=# SELECCIONA formato_simple (); AVISO:
resultado: public.t_test simple_format
Machine Translated by Google
274 Escribir procedimientos almacenados
SELECCIONE nombre de esquema|| '.' || nombretabla +
DESDE pg_tables +
DONDE nombreesquema = $1 +
Y nombre de tabla = $2
(1 fila)
• Como puede ver, el mensaje de depuración muestra correctamente la tabla, incluido el esquema, y devuelve
correctamente la consulta. Sin embargo, la función de formato puede hacer mucho más. Aquí hay unos ejemplos:
test=# SELECT format('Hola, %s %s','PostgreSQL', 15);
formato
Hola, PostgreSQL 15
(1 fila)
prueba=# SELECCIONA formato('Hola, %s %10s',
'PostgreSQL', 15);
formato
Hola, PostgreSQL 15
(1 fila)
La función de formato puede usar opciones de formato como se muestra en el ejemplo. %10s significa que la
cadena que queremos agregar se completará agregando espacios en blanco.
• En algunos casos, puede ser necesario utilizar una variable más de una vez. El siguiente
ejemplo muestra dos parámetros, que se agregan más de una vez a la cadena que queremos crear.
Lo que puede hacer es usar $1, $2, etc. para identificar las entradas en la lista de argumentos:
prueba=# SELECCIONAR formato('%1$s, %1$s, %2$s',
'uno dos');
formato
uno, uno, dos
(1 fila)
La función de formato es muy poderosa y muy importante cuando desea evitar la inyección
de SQL, y debe hacer un buen uso de esta poderosa función.
Vamos a cubrir la gestión de ámbitos a continuación.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 275
Gestión de ámbitos
Después de tratar las comillas y la seguridad básica (inyección de SQL) en general, nos centraremos en otro
tema importante: los alcances.
Al igual que los lenguajes de programación más populares, PL/pgSQL usa variables según su contexto.
Las variables se definen en la instrucción DECLARE de una función. Sin embargo, además, PL/pgSQL le
permite anidar una declaración DECLARE:
CREAR FUNCIÓN scope_test () DEVUELVE int COMO $$
DECLARAR
int := 0;
COMENZAR
LEVANTAR AVISO 'i1: %', i;
DECLARAR
yo ent;
COMENZAR
LEVANTAR AVISO 'i2: %', i;
FIN;
RETORNO yo;
FIN;
$$ IDIOMA 'plpgsql';
En la instrucción DECLARE, se define la variable i y se le asigna un valor. Luego, se muestra i.
La salida, por supuesto, será 0. Luego, comienza una segunda instrucción DECLARE. Contiene una encarnación
adicional de i, a la que no se le asigna un valor. Por lo tanto, el valor será NULL. Tenga en cuenta que
PostgreSQL ahora mostrará la i interna.
Esto es lo que sucede:
prueba=# SELECCIONA scope_test();
AVISO: i1: 0
AVISO: i2: <NULO>
alcance_prueba
0
(1 fila)
Como era de esperar, los mensajes de depuración mostrarán 0 y NULL.
Machine Translated by Google
276 Escribir procedimientos almacenados
PostgreSQL te permite usar todo tipo de trucos. Sin embargo, se recomienda enfáticamente que
mantenga su código simple y fácil de leer.
Comprender el manejo avanzado de errores
Para los lenguajes de programación, en cada programa y en cada módulo, el manejo de errores es algo importante .
Se espera que todo salga mal de vez en cuando y, por lo tanto, es vital manejar los errores de manera adecuada y
profesional. En PL/pgSQL, puede usar bloques EXCEPTION para manejar errores. La idea es que si el bloque BEGIN
hace algo mal, el bloque EXCEPTION se ocupará de ello y manejará el problema correctamente. Al igual que muchos
otros lenguajes, como Java, puede reaccionar ante diferentes tipos de errores y detectarlos por separado:
• En el siguiente ejemplo, el código podría encontrarse con un problema de división por cero. El objetivo es
detectar este error y reaccionar en consecuencia:
CREAR FUNCIÓN error_test1(int, int) DEVUELVE int COMO $$
COMENZAR
LEVANTAR AVISO 'mensaje de depuración: %/%', $1, $2;
COMENZAR
DEVOLUCIÓN $1 / $2;
EXCEPCIÓN
CUANDO división_por_cero ENTONCES
RAISE NOTICE 'división por cero detectada: %', sqlerrm;
CUANDO otros ENTONCES
LEVANTAR AVISO 'algún otro error: %', sqlerrm;
FIN;
LEVANTAR AVISO 'todos los errores manejados';
RETORNO 0;
FIN;
$$ IDIOMA 'plpgsql';
El bloque BEGIN claramente puede arrojar un error porque puede haber una división por cero. Sin embargo,
el bloque EXCEPTION detecta el error que estamos viendo y también se encarga de todos los demás
problemas potenciales que pueden surgir inesperadamente.
Técnicamente, esto es más o menos lo mismo que un punto de guardado; por lo tanto, el error no hace que
toda la transacción falle por completo. Solo el bloque que está causando el error estará sujeto a una mini
reversión.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 277
• Al inspeccionar la variable sqlerrm, también puede tener acceso directo al mensaje de error
sí mismo. Ejecutemos el código:
prueba=# SELECCIONA error_prueba1(9, 0);
AVISO: mensaje de depuración: 9 / 0
AVISO: división por cero detectada: división por cero
AVISO: todos los errores manejados
error_prueba1
(1 fila)
PostgreSQL detecta la excepción y muestra el mensaje en el bloque EXCEPTION. Tiene la amabilidad de
mostrarnos la línea que contiene el error. Esto hace que sea mucho más fácil depurar y corregir el código si
está roto.
• En algunos casos, también tiene sentido generar su propia excepción. Como era de esperar, esto es
bastante fácil de hacer:
RAISE unique_violation USING MESSAGE = 'Usuario duplicado || id_usuario;
identificación: '
Aparte de esto, PostgreSQL ofrece muchos códigos de error y excepciones predefinidos.
La siguiente página contiene una lista completa de estos mensajes de error: https://www.postgresql. org/docs/15/
static/errcodesappendix.html.
Haciendo uso de GET DIAGNOSTICS
Muchas personas que han usado Oracle en el pasado pueden estar familiarizadas con la cláusula GET DIAGNOSTICS.
La idea detrás de la cláusula GET DIAGNOSTICS es permitir a los usuarios ver lo que está pasando en el sistema. Si
bien la sintaxis puede parecer un poco extraña para las personas que están acostumbradas al código moderno, sigue
siendo una herramienta valiosa que puede mejorar sus aplicaciones.
Desde mi punto de vista, hay dos tareas principales para las que se puede utilizar la cláusula GET DIAGNOSTICS:
• Inspeccionar el recuento de filas
• Obtener información de contexto y obtener un seguimiento
Inspeccionar el conteo de filas es definitivamente algo que necesitará durante la programación diaria.
La extracción de información de contexto es útil si desea depurar aplicaciones.
Machine Translated by Google
278 Escribir procedimientos almacenados
El siguiente ejemplo muestra cómo se puede usar la cláusula GET DIAGNOSTICS dentro de su código:
CREAR FUNCIÓN get_diag() DEVUELVE int COMO $$
DECLARAR
RC int;
_sqlstate texto;
_mensaje de texto;
_texto de contexto;
COMENZAR
EJECUTAR 'SELECCIONAR * DESDE generar_series(1, 10)';
OBTENER DIAGNÓSTICOS rc = ROW_COUNT;
LEVANTAR AVISO 'recuento de filas: %', rc;
SELECCIONE rc / 0;
EXCEPCIÓN
CUANDO OTROS ENTONCES
OBTENGA DIAGNÓSTICOS APILADOS
_sqlstate = return_sqlstate, _message = mensaje_texto,
_context = pg_exception_context;
LEVANTAR AVISO 'sqlstate: %, mensaje: %, contexto: [%]',
_sqlstate,
_mensaje,
reemplazar( _contexto, E'n', ' < ');
RETORNO rc;
FIN;
$$ IDIOMA 'plpgsql';
Lo primero después de declarar esas variables es ejecutar una instrucción SQL y solicitar a la cláusula
GET DIAGNOSTICS un recuento de filas, que luego se muestra en un mensaje de depuración. Luego,
la función obliga a PL/pgSQL a fallar. Una vez que esto suceda, utilizaremos la cláusula GET
DIAGNOSTICS para extraer información del servidor para mostrarla.
Esto es lo que sucede cuando llamamos a la función get_diag:
prueba=# SELECCIONA get_diag();
AVISO: recuento de filas: 10
CONTEXTO: función PL/pgSQL get_diag() línea 12 en RAISE
AVISO: sqlstate: 22012,
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 279
mensaje: división por cero, contexto:
[sentencia SQL "SELECT rc/0"
< Función PL/pgSQL get_diag() línea 14 en
instrucción SQL]
CONTEXTO: Función PL/pgSQL get_diag() línea 22 en RAISE
obtener_diag
10
(1 fila)
Como puede ver, la cláusula GET DIAGNOSTICS nos brinda información detallada sobre las actividades en el
sistema.
Uso de cursores para obtener datos en fragmentos
Si ejecuta SQL, la base de datos calculará el resultado y lo enviará a su aplicación. Una vez que se ha enviado
todo el conjunto de resultados al cliente, la aplicación puede continuar con su trabajo. El problema es este: ¿qué
sucede si el conjunto de resultados es tan grande que ya no cabe en la memoria? ¿ Qué pasa si la base de datos
devuelve 10 mil millones de filas? La aplicación cliente normalmente no puede manejar tantos datos a la vez y, de
hecho, no debería hacerlo. La solución a este problema es un cursor. La idea detrás de un cursor es que los datos
se generan solo cuando se necesitan (cuando se llama a FETCH). Por lo tanto, la aplicación ya puede comenzar
a consumir datos mientras la base de datos los genera. Además de eso, se requiere mucha menos memoria para
realizar esta operación.
Cuando se trata de PL/pgSQL, los cursores también juegan un papel importante. Cada vez que recorre un
conjunto de resultados, PostgreSQL usará automáticamente un cursor internamente. La ventaja es que el consumo
de memoria de sus aplicaciones se reducirá drásticamente, y casi no hay posibilidad de quedarse sin memoria
debido a la gran cantidad de datos que se procesan. Hay varias formas de usar los cursores.
Aquí está el ejemplo más simple de usar un cursor dentro de una función:
CREAR O REEMPLAZAR FUNCIÓN c(int)
DEVUELVE conjunto de texto COMO
$$
DECLARAR
registro v_rec;
COMENZAR
PARA v_rec IN SELECCIONAR nombre de tabla
DESDE pg_tables
LÍMITE $1
BUCLE
Machine Translated by Google
280 Escribir procedimientos almacenados
VOLVER SIGUIENTE v_rec.tablename;
FIN DEL BUCLE;
DEVOLVER;
FIN;
$$ IDIOMA 'plpgsql';
Este código es interesante por dos razones:
• En primer lugar, es una función de retorno establecida (SRF). Produce una columna completa y no solo
una fila. La forma de lograr esto es usar el conjunto de variables en lugar de solo el tipo de datos. La
cláusula RETURN NEXT construirá el conjunto de resultados hasta que hayamos llegado al final. La
cláusula RETURN le dirá a PostgreSQL que queremos dejar la función y que tenemos los resultados.
• La segunda cuestión importante es que al recorrer el cursor se creará automáticamente un cursor interno.
En otras palabras, no hay necesidad de tener miedo de quedarse sin memoria.
PostgreSQL optimizará la consulta para que intente producir el primer 10 % de los datos (definidos por la
variable cursor_tuple_fraction) lo más rápido posible.
Esto es lo que devolverá la consulta:
prueba=# SELECCIONA * DESDE c(3);
C
t_test
pg_statistic pg_type
(3 filas)
En este ejemplo, simplemente habrá una lista de tablas aleatorias. Si el resultado difiere de su lado, esto es algo
esperado.
Lo que acaba de ver es, en mi opinión, la forma más frecuente y común de usar cursores implícitos en PL/pgSQL.
El siguiente ejemplo muestra un mecanismo más antiguo que muchas personas con experiencia en Oracle
podrían conocer:
CREAR O REEMPLAZAR FUNCIÓN d(int)
DEVUELVE conjunto de texto COMO
$$
DECLARAR
cursor de referencia v_cur;
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 281
v_texto de datos;
COMENZAR
ABRIR v_cur PARA
SELECCIONE el nombre de la tabla
DESDE pg_tables
LÍMITE $1;
MIENTRAS es verdadero BUCLE
FETCH v_cur EN v_data;
SI SE ENCUENTRA ENTONCES
VOLVER SIGUIENTE v_data;
DEMÁS
DEVOLVER;
TERMINARA SI;
FIN DEL BUCLE;
FIN;
$$ IDIOMA 'plpgsql';
En este ejemplo, el cursor se declara y abre explícitamente. En el interior, los datos del bucle se recuperan explícitamente y
se devuelven a la persona que llama. Básicamente, la consulta hace exactamente lo mismo. Es simplemente una cuestión de
gusto con respecto a la sintaxis que prefieren los desarrolladores.
¿Aún tienes la sensación de que aún no sabes lo suficiente sobre cursores? Hay más; aquí hay una tercera opción para hacer
exactamente lo mismo:
CREAR O REEMPLAZAR FUNCIÓN e(int)
DEVUELVE conjunto de texto COMO
$$
DECLARAR
v_cur CURSOR (param1 int) PARA
SELECCIONE el nombre de la tabla
DESDE pg_tables
LIMIT param1;
v_texto de datos;
COMENZAR
ABRIR v_cur ($1);
MIENTRAS es verdadero BUCLE
FETCH v_cur EN v_data;
SI SE ENCUENTRA ENTONCES
Machine Translated by Google
282 Escribir procedimientos almacenados
VOLVER SIGUIENTE v_data;
DEMÁS
DEVOLVER;
TERMINARA SI;
FIN DEL BUCLE;
FIN;
$$ IDIOMA 'plpgsql';
En este caso, el cursor se alimenta con un parámetro entero que proviene directamente de la llamada a la función ($1).
A veces, el propio procedimiento almacenado no utiliza un cursor, sino que lo devuelve para su uso posterior. En este
caso, puede devolver un cursor de referencia simple como valor de retorno:
CREAR O REEMPLAZAR FUNCIÓN cursor_test(c refcursor)
DEVOLUCIONES refcursor AS
$$
COMENZAR
ABRIR c PARA SELECCIONAR *
DESDE generar_series(1, 10) COMO id;
RETORNO c;
FIN;
$$ IDIOMA plpgsql;
La lógica aquí es bastante simple. El nombre del cursor se pasa a la función. Luego, el cursor se abre y se
devuelve. La belleza aquí es que la consulta detrás del cursor se puede crear sobre la marcha y compilar
dinámicamente.
La aplicación puede buscar desde el cursor como desde cualquier otra aplicación. Así es como funciona:
prueba=# COMENZAR;
COMENZAR
test=# SELECT cursor_test('mytest'); prueba_cursor
mi prueba
(1 fila)
test=# FETCH SIGUIENTE DE mytest;
identificación
1
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 283
(1 fila)
test=# FETCH SIGUIENTE DE mytest;
identificación
(1 fila)
Tenga en cuenta que solo funciona cuando se usa un bloque de transacción. Sin una transacción explícita,
el cursor fallará.
En esta sección, hemos aprendido que los cursores solo producirán datos a medida que se consumen. Esto es
válido para la mayoría de las consultas. Sin embargo, hay una trampa en este ejemplo; cada vez que se utiliza un
SRF, se debe materializar todo el resultado . No se crea sobre la marcha sino, al contrario, de golpe. La razón de
esto es que SQL debe poder volver a escanear una relación, lo que es fácilmente posible en el caso de una tabla
normal. Sin embargo, para las funciones, la situación es diferente. Por lo tanto, siempre se calcula y materializa un
SRF, lo que hace que el cursor en este ejemplo sea totalmente inútil. En otras palabras, debemos tener cuidado al escribir funciones.
En algunos casos, el peligro se esconde en los detalles ingeniosos.
Utilizando tipos compuestos
En la mayoría de los demás sistemas de bases de datos, los procedimientos almacenados solo se utilizan con tipos
de datos primitivos, como enteros, numéricos, varchar, etc. Sin embargo, PostgreSQL es muy diferente. Podemos usar
todos los tipos de datos que están disponibles para nosotros. Esto incluye tipos de datos primitivos, compuestos y
personalizados. Simplemente no hay restricciones en lo que respecta a los tipos de datos. Para liberar todo el poder
de PostgreSQL, los tipos compuestos son muy importantes y, a menudo, los utilizan las extensiones que se encuentran
en Internet. El siguiente ejemplo muestra cómo se puede pasar un tipo compuesto a una función y cómo se puede
usar internamente. Finalmente, el tipo compuesto se devolverá nuevamente como se muestra en el siguiente bloque de código:
CREAR TIPO my_cool_type AS (texto s, texto t);
CREAR FUNCIÓN f(my_cool_type)
DEVUELVE my_cool_type COMO $$
DECLARAR
v_row mi_tipo_genial;
COMENZAR
LEVANTAR AVISO 'esquema: (%) / tabla: (%)'
, $1.s, $1.t;
SELECCIONE nombre de esquema, nombre de tabla
EN v_fila
Machine Translated by Google
284 Escribir procedimientos almacenados
DESDE pg_tables
DONDE tablename = trim($1.t)
Y nombre de esquema = recorte ($ 1.s)
LÍMITE 1;
RETORNO v_fila;
FIN;
$$ IDIOMA 'plpgsql';
El problema principal aquí es que simplemente puede usar $1.field_name para acceder al tipo compuesto.
Devolver el tipo compuesto tampoco es difícil.
Solo tiene que ensamblar la variable de tipo compuesto sobre la marcha y devolverla, como cualquier otro tipo de
datos. Incluso puede usar matrices fácilmente o incluso estructuras más complejas.
El siguiente código muestra lo que devolverá PostgreSQL:
prueba=# SELECCIONA (f).s, (f).t
FROM f ('("public", "t_test")'::my_cool_type);
AVISO: esquema: (público) / tabla: (t_test)
s | t
+
publico | prueba_t
(1 fila)
Pasemos ahora a cubrir la escritura de disparadores en PL/pgSQL.
Escribir disparadores en PL/pgSQL
El código del lado del servidor es especialmente popular si desea reaccionar ante ciertos eventos que ocurren
en la base de datos. Un activador le permite llamar a una función si ocurre una cláusula INSERT, UPDATE,
DELETE o TRUNCATE en una tabla. La función a la que llama el activador puede modificar los datos que han
cambiado en su tabla o simplemente realizar una operación necesaria.
En PostgreSQL, los disparadores se han vuelto más poderosos a lo largo de los años y ahora brindan un amplio conjunto
de funciones:
test=# \h CREAR DISPARADOR
Dominio: CREAR DISPARADOR
Descripción: define un nuevo disparador
Sintaxis:
REEMPLAZAR ] [ RESTRICCIÓN ] TRIGGER nombre { ANTES | CREAR [ O
DESPUÉS | EN LUGAR DE } { evento [ O ... ] }
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 285
ON nombre_tabla
[ FROM nombre_tabla_referenciada ]
[ NO APLAZABLE | [ APLAZABLE ]
[ INICIALMENTE INMEDIATO | INICIALMENTE DIFERIDO] ]
[ REFERENCIA { { VIEJO | NUEVO } TABLA [ COMO ]
nombre_relación_transición } [ ... ] ]
[PARA [CADA UNO] { FILA | DECLARACIÓN } ]
[ CUANDO ( condición ) ]
EJECUTAR { FUNCIÓN | PROCEDIMIENTO }
nombre_función (argumentos)
donde evento puede ser uno de:
INSERTAR
ACTUALIZAR [ DE nombre_columna [, ... ] ]
BORRAR
TRUNCAR
URL: https://www.postgresql.org/docs/15/sqlcreatetrigger.html
Lo primero que debe observar es que siempre se activa un disparador para una tabla o una vista y llama a una
función. Tiene un nombre, y puede suceder antes o después de un evento. La belleza de PostgreSQL es que puede
tener innumerables disparadores en una sola tabla. Si bien esto no será una sorpresa para los usuarios
incondicionales de PostgreSQL, quiero señalar que esto no es posible en muchos motores de bases de datos
comerciales costosos que todavía están en uso en todo el mundo.
Si hay más de un activador en la misma tabla, la siguiente regla, que se introdujo hace muchos años en
PostgreSQL 7.3, será útil: los activadores se activan en orden alfabético. En primer lugar, todos los
desencadenantes ANTES ocurren en orden alfabético. Luego, PostgreSQL realiza la operación de fila
para la cual se activó el disparador y continúa ejecutándose después de los disparadores en orden alfabético.
En otras palabras, el orden de ejecución de los disparadores es absolutamente determinista y el número de disparadores
es básicamente ilimitado.
Los disparadores pueden modificar los datos antes o después de que haya ocurrido la modificación real. En general, esta es una
buena manera de verificar los datos y detectar errores si se violan algunas restricciones personalizadas.
El siguiente ejemplo muestra un activador que se activa en la cláusula INSERT y que cambia los datos
que se agregan a la tabla:
CREAR TABLA t_sensor (serie,
identificación
t marca de tiempo,
temperatura numérica
);
Machine Translated by Google
286 Escribir procedimientos almacenados
Nuestra tabla solo almacena un par de valores. El objetivo ahora es llamar a una función tan pronto como se inserte una fila:
CREAR O REEMPLAZAR FUNCIÓN trig_func()
DEVOLUCIONES gatillo AS
$$
COMENZAR
SI NUEVO.temperatura < 273
ENTONCES
NUEVO.temperatura := 0;
TERMINARA SI;
VOLVER NUEVO;
FIN;
$$ IDIOMA 'plpgsql';
Como dijimos anteriormente, el activador siempre llamará a una función, lo que le permite usar un código muy
abstracto. Lo importante aquí es que la función de disparo tiene que devolver un disparo. Para acceder a la fila
que está a punto de insertar puede acceder a la variable NUEVA.
Los activadores INSERTAR y ACTUALIZAR siempre proporcionan una NUEVA variable. UPDATE y DELETE
ofrecerán una variable llamada OLD. Estas variables contienen la fila que está a punto de modificar.
En mi ejemplo, el código verifica si la temperatura es demasiado baja. Si es así y el valor no es correcto, se ajusta
dinámicamente. Para asegurarse de que se pueda utilizar la fila modificada, simplemente se devuelve NUEVO. Si
se llama a un segundo disparador después de este, la siguiente llamada de función ya verá la fila modificada.
En el siguiente paso, se puede crear el disparador usando el comando CREATE TRIGGER:
CREAR DISPARADOR sensor_trig
ANTES DE INSERTAR EN t_sensor
POR CADA FILA
EJECUTAR PROCEDIMIENTO trig_func();
Esto es lo que hará el gatillo:
test=# INSERTAR EN t_sensor (ts, temperatura)
VALORES ('20170504 14:43', 300) DEVOLUCIÓN *; | temperatura
identificación | t
++
1 | 20170504 14:43:00 | 0
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 287
(1 fila)
INSERTAR 0 1
Como puede ver, el valor se ha ajustado correctamente. El contenido de la tabla muestra 0 para la temperatura.
Si está utilizando disparadores, debe tener en cuenta el hecho de que un disparador sabe mucho sobre sí mismo.
Puede acceder a un par de variables que le permiten escribir código más sofisticado y, por lo tanto, lograr una
mejor abstracción.
Primero sueltemos el gatillo:
test=# DROP TRIGGER sensor_trig ON t_sensor;
GATILLO DE CAÍDA
El gatillo se soltó con éxito.
Entonces, se puede agregar una nueva función:
CREAR O REEMPLAZAR FUNCIÓN trig_demo()
Activador de DEVOLUCIONES COMO
$$
COMENZAR
LEVANTAR AVISO 'TG_NAME: %', TG_NAME;
LEVANTAR AVISO 'TG_RELNAME: %', TG_RELNAME;
LEVANTAR AVISO 'TG_TABLE_SCHEMA: %', TG_TABLE_SCHEMA;
LEVANTAR AVISO 'TG_TABLE_NAME: %', TG_TABLE_NAME;
LEVANTAR AVISO 'TG_WHEN: %', TG_WHEN;
LEVANTAR AVISO 'TG_LEVEL: %', TG_LEVEL;
LEVANTAR AVISO 'TG_OP: %', TG_OP;
LEVANTAR AVISO 'TG_NARGS: %', TG_NARGS;
LEVANTAR AVISO 'TG_ARGV: %', TG_NAME;
VOLVER NUEVO;
FIN;
$$ IDIOMA 'plpgsql';
CREAR DISPARADOR sensor_trig
ANTES DE INSERTAR EN t_sensor
POR CADA FILA
EJECUTAR PROCEDIMIENTO trig_demo();
Machine Translated by Google
288 Escribir procedimientos almacenados
Todas las variables que se utilizan aquí están predefinidas y están disponibles de forma predeterminada. Todo lo que hace
nuestro código es mostrarlos para que podamos ver su contenido:
test=# INSERTAR EN t_sensor (ts, temperatura)
VALORES ('20170504 14:43', 300) DEVOLUCIÓN *;
AVISO: TG_NAME: demo_trigger
AVISO: TG_RELNAME: t_sensor
AVISO: TG_TABLE_SCHEMA: público
AVISO: TG_TABLE_NAME: t_sensor
AVISO: TG_CUANDO: ANTES
AVISO: TG_LEVEL: FILA
AVISO: TG_OP: INSERTAR
AVISO: TG_NARGS: 0 id | t
| temperatura
++
2 | 20170504 14:43:00 | 300
(1 fila)
INSERTAR 0 1
Lo que vemos aquí es que el activador conoce su nombre, la mesa para la que ha sido activado y mucho más.
Para aplicar acciones similares en varias tablas, estas variables ayudan a evitar el código duplicado simplemente
escribiendo una sola función. Esto se puede usar para todas las tablas que nos interesen.
Hasta ahora, hemos visto activadores simples a nivel de fila, que se activan una vez por declaración. Sin
embargo, con la introducción de PostgreSQL 10.0, hay un par de funciones nuevas. Los disparadores a nivel
de declaración ya existen desde hace un tiempo. Sin embargo, no fue posible acceder a los datos que fueron
modificados por un activador. Esto se solucionó en PostgreSQL 10.0 y ahora es posible utilizar tablas de
transición, que contienen todos los cambios que se realizaron:
• El siguiente código contiene un ejemplo completo que muestra cómo se puede utilizar una tabla de transición:
CREAR O REEMPLAZAR FUNCIÓN Transition_trigger()
DEVUELVE EL DISPARADOR COMO $$
DECLARAR
registro v_record;
COMENZAR
SI (TG_OP = 'INSERTAR') ENTONCES
LEVANTAR AVISO 'nuevos datos:';
PARA v_record IN SELECCIONAR * DESDE new_table
BUCLE
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 289
LEVANTAR AVISO '%', v_record;
FIN DEL BUCLE;
DEMÁS
LEVANTAR AVISO 'datos antiguos:';
PARA v_record IN SELECCIONAR * DESDE old_table
BUCLE
LEVANTAR AVISO '%', v_record;
FIN DEL BUCLE;
TERMINARA SI;
VOLVER NULO; el resultado es ignorado ya que este es un
DESPUÉS del gatillo
FIN;
$$ IDIOMA plpgsql;
CREAR DISPARADOR Transition_test_trigger_ins
DESPUÉS DE INSERTAR EN t_sensor
REFERENCIA NUEVA TABLA COMO new_table
PARA CADA DECLARACIÓN EJECUTAR PROCEDIMIENTO Transition_ trigger();
CREAR DISPARADOR Transition_test_trigger_del
DESPUÉS DE ELIMINAR EN t_sensor
REFERENCIA A LA TABLA ANTIGUA COMO old_table
PARA CADA DECLARACIÓN EJECUTAR PROCEDIMIENTO Transition_ trigger();
En este caso, necesitamos dos definiciones de disparador porque no podemos comprimir todo en una sola
definición. Dentro de la función de activación, la tabla de transición es fácil de usar: se puede acceder a
ella como a una tabla normal.
• Probemos el código del disparador insertando algunos datos:
INSERTAR EN t_sensor
SELECCIONAR *, ahora(), aleatorio() * 20
DE generar_serie(1, 5);
ELIMINAR DE t_sensor;
Machine Translated by Google
290 Escribir procedimientos almacenados
• En mi ejemplo, el código simplemente emitirá AVISO para cada entrada en la tabla de transición:
AVISO: nuevos datos:
AVISO: (1,"20171004 15:47:14.129151",10.4552665632218)
AVISO: (2,"20171004 15:47:14.129151",12.8670312650502)
AVISO: (3,"20171004 15:47:14.129151",14.3934494629502)
AVISO: (4,"20171004 15:47:14.129151",4.35718866065145)
AVISO: (5,"20171004 15:47:14.129151",10.9121138229966)
INSERTAR 0 5
AVISO: datos antiguos:
AVISO: (1,"20171004 15:47:14.129151",10.4552665632218)
AVISO: (2,"20171004 15:47:14.129151",12.8670312650502)
AVISO: (3,"20171004 15:47:14.129151",14.3934494629502)
AVISO: (4,"20171004 15:47:14.129151",4.35718866065145)
AVISO: (5,"20171004 15:47:14.129151",10.9121138229966)
ELIMINAR 5
Tenga en cuenta que no es necesariamente una buena idea usar tablas de transición para miles de millones de filas.
PostgreSQL realmente es escalable, pero en algún momento, verá que también hay implicaciones de rendimiento.
Escribir procedimientos almacenados en PL/pgSQL
Ahora, avancemos y aprendamos a escribir procedimientos. En esta sección, aprenderá a escribir procedimientos
almacenados reales, que se introdujeron en PostgreSQL 11. Para crear un procedimiento, debe usar CREAR
PROCEDIMIENTO. La sintaxis de este comando es notablemente similar a CREATE FUNCTION.
Solo hay algunas diferencias menores, que se pueden ver en la siguiente definición de sintaxis:
test=# \h CREAR PROCEDIMIENTO
Dominio: CREAR PROCEDIMIENTO
Descripción: definir un nuevo procedimiento
Sintaxis:
CREAR [ O REEMPLAZAR ] PROCEDIMIENTO
[
[ { PREDETERMINADO
| = } [ nombre de argumento ] tipo de argumento
nombre ([argmode] default_expr] [, ...]] )
{ IDIOMA nombre_idioma
| TRANSFORMAR { PARA TIPO type_name } [, ... ] | [ EXTERNO ]
INVOCADOR DE SEGURIDAD |
[ EXTERNO ] DEFINICIÓN DE SEGURIDAD
| SET configuración_parámetro { TO valor | = valor
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 291
| DESDE ACTUAL }
| como 'definición'
| COMO 'obj_file', 'link_symbol' | cuerpo_sql } ...
URL: https://www.postgresql.org/docs/15/sqlcreateprocedure. html
El siguiente ejemplo muestra un procedimiento almacenado que ejecuta dos transacciones. La primera
transacción es COMMIT y, por lo tanto, crea dos tablas. El segundo procedimiento es ROLLBACK:
test=# CREAR PROCEDIMIENTO test_proc()
IDIOMA plpgsql
COMO $$
COMENZAR
CREAR TABLA a (ayuda int); CREAR TABLA b (oferta int);
COMPROMETERSE;
CREAR TABLA c (cid int);
RETROCEDER;
FIN;
$$;
CREAR PROCEDIMIENTO
Como podemos ver, un procedimiento puede realizar un manejo explícito de transacciones. La idea detrás de un procedimiento
es poder ejecutar trabajos por lotes y otras operaciones, que son difíciles de realizar en una función.
Para ejecutar el procedimiento, debe usar CALL, como se muestra en el siguiente ejemplo:
prueba=# CALL test_proc();
LLAMAR
Las dos primeras tablas estaban comprometidas. La tercera tabla no se ha creado debido a la reversión en el interior
el procedimiento:
prueba=# \d
Lista de relaciones
Esquema | Nombre | Tipo | Dueño
+++
publico | a | mesa | hora
Machine Translated by Google
292 Escribir procedimientos almacenados
publico | b | mesa | hora
(2 filas)
Los procedimientos son una de las características más importantes que se introdujeron en PostgreSQL 11
y hacen una contribución significativa a la eficiencia del desarrollo de software.
Introducción a PL/Perl
Hay mucho más que decir sobre PL/pgSQL. Sin embargo, no se puede cubrir todo en un solo libro, y algunos temas
están definitivamente más allá del alcance de este libro, por lo que es hora de pasar al siguiente lenguaje procesal.
PL/Perl ha sido adoptado por muchas personas como el lenguaje ideal para el procesamiento de cadenas. Como
sabrá, Perl es famoso por sus capacidades de manipulación de cadenas y, por lo tanto, sigue siendo bastante
popular después de todos estos años.
Para habilitar PL/Perl, tiene dos opciones:
test=# CREAR EXTENSIÓN plperl;
CREAR EXTENSIÓN
test=# CREAR EXTENSIÓN plperlu;
CREAR EXTENSIÓN
Puede implementar Perl confiable o no confiable. Si desea ambos, debe habilitar ambos idiomas.
Para mostrarle cómo funciona PL/Perl, he implementado una función que simplemente analiza una dirección de correo
electrónico y devuelve verdadero o falso. Así es como funciona:
test=# CREAR O REEMPLAZAR FUNCIÓN verificar_email(texto)
DEVUELVE booleano AS
$$
si ($_[0] =~ /^[az09.]+@[az09.]+$/) {
devolver verdadero;
} falso retorno;
$$ IDIOMA 'plperl';
CREAR FUNCIÓN
Se pasa un parámetro de prueba a la función. Dentro de la función, se puede acceder a todos esos parámetros de
entrada usando $_. En este ejemplo, se ejecuta la expresión regular y se devuelve la función.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 293
La función se puede llamar, como cualquier otro procedimiento escrito en cualquier otro idioma. La siguiente
lista muestra cómo se puede llamar a la función:
prueba = # SELECCIONE verificar_email ('hs@cybertec.at');
verificar_correo electrónico
t
(1 fila)
test=# SELECCIONA verificar_email('totalmente incorrecto');
verificar_correo electrónico
F
(1 fila)
El listado anterior muestra que la función valida el código correctamente. Tenga en cuenta que no puede
cargar paquetes, etc., si se encuentra dentro de una función de confianza. Por ejemplo, si desea utilizar el
comando w para buscar palabras, Perl cargará internamente utf8.pm, que, por supuesto, no está permitido.
Uso de PL/Perl para la abstracción de tipos de datos
Como ya se indicó en este capítulo, las funciones en PostgreSQL son bastante universales y se pueden usar en
muchos contextos diferentes:
• Si desea utilizar funciones para mejorar la calidad de sus datos, puede utilizar una cláusula CREAR DOMINIO:
test=# \h CREAR DOMINIO
Dominio: CREAR DOMINIO
Descripción: definir un nuevo dominio
Sintaxis:
CREAR DOMINIO nombre [ COMO ] tipo_datos
[ COLABORAR colación ]
[ expresión DEFAULT ] [ restricción
[ ... ] ]
donde la restricción es:
[ CONSTRAINT nombre_restricción ]
{ NO NULO | NULO | COMPROBAR (expresión) }
URL: https://www.postgresql.org/docs/15/sqlcreateddomain. html
Machine Translated by Google
294 Escribir procedimientos almacenados
En este ejemplo, la función PL/Perl se usará para crear un dominio llamado correo electrónico, que, a su vez,
se puede usar como un tipo de datos.
• El siguiente código muestra cómo se puede crear el dominio:
test=# CREAR DOMINIO email COMO texto
COMPROBAR (verify_email(VALOR) = verdadero);
CREAR DOMINIO
El comando CREATE DOMAIN crea el tipo adicional y aplica automáticamente restricciones de
verificación para asegurarse de que la restricción se use de manera consistente en toda la base de datos.
• Como mencionamos anteriormente, el dominio funciona como un tipo de datos normal:
test=# CREATE TABLE t_email (id serial, data email);
CREAR MESA
• La función Perl asegura que nada que viole nuestras comprobaciones pueda ser insertado en la base de datos,
como el siguiente ejemplo demuestra con éxito:
test=# INSERTAR EN t_email (datos)
VALORES ('en algún lugar@ejemplo.com');
INSERTAR 0 1
test=# INSERTAR EN t_email (datos)
VALORES ('somewhere_wrong_example.com');
ERROR: el valor del correo electrónico del dominio infringe la verificación
restricción "email_check"
Perl podría ser una buena opción para procesar cadenas, pero, como siempre, debe decidir si quiere este código
directamente en la base de datos o no.
Decidir entre PL/Perl y PL/PerlU
Hasta ahora, el código Perl no ha causado ningún problema relacionado con la seguridad porque todo lo que hicimos fue usar
expresiones regulares. La pregunta aquí es, ¿qué pasa si alguien intenta hacer algo desagradable dentro de la función de Perl?
• Como ya dijimos, PL/Perl simplemente generará un error, como puede ver en la siguiente lista:
test=# CREAR O REEMPLAZAR FUNCIÓN test_security()
DEVUELVE booleano AS
$$
uso estricto;
mi $fp = abrir("/etc/contraseña", "r");
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 295
falso retorno;
$$ IDIOMA 'plperl';
ERROR: 'abierto' atrapado por la máscara de operación en la línea
CONTEXT: compilación de la función PL/Perl "test_security"
La lista muestra que PL/Perl se quejará tan pronto como intente crear la función y se mostrará un error al
instante.
• Si realmente desea ejecutar código no confiable en Perl, debe usar PL/PerlU, como se muestra en el
siguiente bloque de código:
test=# CREAR O REEMPLAZAR FUNCIÓN first_line()
DEVUELVE texto COMO
$$
abierto(mi $fh, '<:codificación(UTF8)', "/etc/passwd")
o elog(AVISO, "¡No se pudo abrir el archivo '$filename' $!");
mi $fila = <$fh>;
cerrar ($fh);
devolver $fila;
$$ IDIOMA 'plperlu';
CREAR FUNCIÓN
El procedimiento sigue siendo el mismo. Devuelve una cadena. Sin embargo, está permitido hacer de
todo. La única diferencia es que la función está marcada como plperlu.
• El resultado no es sorprendente:
prueba=# SELECCIONA primera_línea();
primera linea
raíz:x:0:0:raíz:/raíz:/bin/bash+
(1 fila)
La primera línea se mostrará como se esperaba.
A continuación, hagamos uso de la interfaz SPI.
Machine Translated by Google
296 Escribir procedimientos almacenados
Haciendo uso de la interfaz SPI
De vez en cuando, su procedimiento Perl tiene que realizar algún trabajo de base de datos. Recuerde, la función es
parte de la conexión de la base de datos. Por lo tanto, no tiene sentido crear realmente una conexión de base de datos.
Para comunicarse con la base de datos, la infraestructura del servidor PostgreSQL proporciona la Interfaz de programación
del servidor (SPI), que es una interfaz C que puede usar para comunicarse con las partes internas de la base de datos.
Todos los lenguajes de procedimiento que lo ayudan a ejecutar código del lado del servidor usan esta interfaz para
exponer la funcionalidad. PL/Perl hace lo mismo y, en esta sección, aprenderá a usar el contenedor de Perl en la interfaz SPI.
Lo más importante que puede querer hacer es simplemente ejecutar SQL y recuperar la cantidad de
filas que se han obtenido. La función spi_exec_query está aquí para hacer exactamente eso. El
primer parámetro que se pasa a la función es el parámetro de consulta. El segundo parámetro tiene
el número de filas que realmente desea recuperar. Para simplificar, decidí buscarlos todos. El
siguiente código muestra un ejemplo de esto:
test=# CREAR O REEMPLAZAR FUNCIÓN spi_sample(int)
DEVOLUCIONES nulas AS
$$
mi $rv = spi_exec_query(" SELECCIONAR *
DESDE generar_series(1, $_[0])", $_[0]
);
elog(AVISO, "filas obtenidas: " . $rv>{procesado}); . $rv
elog(AVISO, "estado: " >{estado});
devolver;
$$ IDIOMA 'plperl';
El SPI ejecutará la consulta y mostrará el número de filas. Lo importante aquí es que todos los
lenguajes de procedimientos almacenados proporcionan un medio para enviar mensajes de registro.
En el caso de PL/Perl, esta función se llama elog, que toma dos parámetros. El primero define la
importancia del mensaje (INFO, AVISO, ADVERTENCIA, ERROR, etc.), y el segundo parámetro
contiene el mensaje real.
El siguiente mensaje muestra lo que devuelve la consulta:
prueba=# SELECT spi_sample(9);
AVISO: filas obtenidas: 9
AVISO: estado: SPI_OK_SELECT
spi_muestra
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 297
(1 fila)
La llamada al SPI funciona bien y se muestra el estado.
Uso del SPI para funciones de devolución de conjuntos
En muchos casos, no solo desea ejecutar SQL y olvidarse de él. En la mayoría de los casos, un
procedimiento repetirá el resultado y hará algo con él. El siguiente ejemplo muestra cómo puede recorrer
el resultado de una consulta. Además de eso, decidí reforzar un poco el ejemplo y hacer que la función
devuelva un tipo de datos compuesto. Trabajar con tipos compuestos en Perl es muy fácil porque
simplemente puede introducir los datos en un hash y devolverlo.
La función return_next construirá gradualmente el conjunto de resultados hasta que la función finalice con
una declaración de devolución simple.
El ejemplo del siguiente código genera una tabla que consta de valores aleatorios:
CREAR TIPO random_type AS (a float8, b float8);
CREAR O REEMPLAZAR FUNCIÓN spi_srf_perl(int)
DEVUELVE setof random_type COMO $$ my
$rv = spi_query("SELECCIONE random() COMO a, random() COMO
b
DESDE generar_series(1, $_[0])");
while (definido (mi $fila = spi_fetchrow($rv))) {
elog(AVISO, "datos: " $fila>{a} . . .
$fila>{b}); " / " return_next({a
>{b}}); => $fila>{a}, b => $fila
}
devolver;
$$ IDIOMA 'plperl';
CREAR FUNCIÓN
Primero, se ejecuta la función spi_query y se inicia un ciclo usando la función spi_fetchrow.
Dentro del bucle, el tipo compuesto se ensamblará y se insertará en el conjunto de resultados.
Machine Translated by Google
298 Escribir procedimientos almacenados
Como era de esperar, la función devolverá un conjunto de valores aleatorios:
prueba=# SELECCIONAR * DESDE spi_srf_perl(3); AVISO:
datos: 0.154673356097192 / 0.278830723837018 CONTEXTO: Función PL/Perl
"spi_srf_perl"
AVISO: datos: 0.615888888947666 / 0.632620786316693 CONTEXTO: Función PL/Perl
"spi_srf_perl"
AVISO: datos: 0.910436692181975 / 0.753427186980844
CONTEXTO: Función PL/Perl "spi_srf_perl" | b_col
a_col
+
0.154673356097192 | 0.278830723837018 0.615888888947666
| 0.632620786316693 0.910436692181975 | 0.753427186980844
(3 filas)
Tenga en cuenta que las funciones de devolución de conjuntos deben materializarse para que todo el conjunto de resultados pueda
almacenarse en la memoria.
Escapar en PL/Perl y funciones de soporte
Hasta ahora, solo hemos usado números enteros, por lo que la inyección SQL o los nombres de tablas especiales no fueron un problema.
Además, las siguientes funciones están disponibles:
• quote_literal: Esto devuelve una comilla de cadena como una cadena literal
• quote_nullable: Esto cita una cadena
• quote_ident: Esto cita identificadores SQL (nombres de objetos, etc.)
• decode_bytea: esto decodifica un campo de matriz de bytes de PostgreSQL
• encode_bytea: Esto codifica datos y los convierte en una matriz de bytes
• encode_literal_array: Esto codifica una matriz de literales
• encode_typed_literal: esto convierte una variable de Perl en el valor del tipo de datos que se pasa como
segundo argumento y devuelve una representación de cadena de este valor
• encode_array_constructor: Esto devuelve el contenido de la matriz a la que se hace referencia como una cadena
en formato de constructor de matriz
• looks_like_number: Esto devuelve verdadero si una cadena se parece a un número
• is_array_ref: Esto devuelve verdadero si algo es una referencia de matriz
Estas funciones están siempre disponibles y se pueden llamar directamente sin tener que incluir una biblioteca.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 299
Compartir datos entre llamadas a funciones
A veces, es necesario compartir datos entre llamadas. La infraestructura tiene los medios para hacerlo. En Perl,
se puede usar un hash para almacenar cualquier dato que se necesite. Echa un vistazo al siguiente ejemplo:
CREAR FUNCIÓN perl_shared(texto) DEVUELVE int COMO
$$
si ( !definido $_COMPARTIDO{$_[0]} ) {
$_COMPARTIDO{$_[0]} = 0;
}
demás
{
$_COMPARTIDO{$_[0]}++;
} devuelve $_COMPARTIDO{$_[0]};
$$ IDIOMA 'plperl';
La variable $_SHARED se inicializará con 0 tan pronto como nos demos cuenta de que la clave que se pasó a la
función aún no está allí. Para cada otra llamada, se agrega 1 al contador, dejándonos con el siguiente resultado:
prueba=# SELECCIONE perl_shared('alguna_clave') DESDE generar_series(1, 3);
perl_shared
0
1
2
(3 filas)
En el caso de una declaración más compleja, el desarrollador generalmente no sabe en qué orden se llamarán las
funciones. Es importante tener eso en cuenta. En la mayoría de los casos, no puede confiar en una orden de ejecución.
Escribir disparadores en Perl
Todos los lenguajes de procedimientos almacenados que se envían con el núcleo de PostgreSQL le permiten
escribir disparadores en ese lenguaje. Lo mismo, por supuesto, se aplica a Perl. Dado que la extensión de este
capítulo es limitada, he decidido no incluir un ejemplo de un activador escrito en Perl, sino señalarle la
documentación oficial de PostgreSQL: https://www.postgresql.org/docs/15/plperl triggers .html.
Machine Translated by Google
300 Escribir procedimientos almacenados
Básicamente, escribir un disparador en Perl no difiere de escribir uno en PL/pgSQL. Todas las variables predefinidas están en
su lugar y, en lo que respecta a los valores devueltos, las reglas se aplican en todos los lenguajes de procedimientos almacenados.
Introducción a PL/Python
Si no es un experto en Perl, PL/Python podría ser lo adecuado para usted. Python ha sido parte de la
infraestructura de PostgreSQL durante mucho tiempo y, por lo tanto, es una implementación sólida y bien probada.
Cuando se trata de PL/Python, hay una cosa que debe tener en cuenta: PL/Python solo está disponible como un
lenguaje no confiable. Desde el punto de vista de la seguridad, es importante tenerlo en cuenta en todo momento.
Tenga en cuenta que en los viejos tiempos, solíamos admitir Python 2. Sin embargo, esos días quedaron atrás.
A partir de ahora, solo será Python 3.
Para habilitar PL/Python, puede ejecutar la siguiente línea desde su línea de comando y probar el nombre de
la base de datos que desea usar con PL/Python:
test=# CREAR IDIOMA plpython3u;
CREAR EXTENSIÓN
Una vez que el lenguaje está habilitado, es posible escribir código.
Alternativamente, puede usar una cláusula CREATE LANGUAGE. Además, tenga en cuenta que para usar
lenguajes del lado del servidor, se necesitan paquetes de PostgreSQL que contengan esos lenguajes
(postgresql plpython$(VERSIONNUMBER), etc.).
Escribir código PL/Python simple
En esta sección, aprenderá a escribir procedimientos simples de Python. El ejemplo que vamos a comentar
aquí es bastante sencillo: si vas a visitar a un cliente en coche en Austria, puedes deducirte 42 euros por
kilómetro en gastos para reducir tu IRPF. Entonces, lo que hace la función es tomar la cantidad de kilómetros
y devolver la cantidad de dinero que podemos descontar de nuestra factura de impuestos. Así es como funciona:
CREAR O REEMPLAZAR FUNCIÓN calcular_deducción (km flotante)
DEVOLUCIONES numérico AS
$$
si km <= 0:
elog(ERROR, 'número de kilómetros no válido')
demás:
ida y vuelta km * 0,42
$$ IDIOMA 'plpython3u';
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 301
La función garantiza que solo se acepten valores positivos. Finalmente, el resultado se calcula y se
devuelve. Como puede ver, la forma en que se pasa una función de Python a PostgreSQL no difiere
realmente de Perl o PL/pgSQL.
Uso de la interfaz SPI
Como todos los lenguajes de procedimiento, PL/Python le da acceso a la interfaz SPI:
1. El siguiente ejemplo muestra cómo se pueden sumar números:
CREAR FUNCIÓN add_numbers(rows_desired integer)
DEVUELVE un entero COMO
$$
suma = 0
cursor = plpy.cursor("SELECCIONAR * DESDE generar_series(1, %d) AS id" %
(filas_deseado))
mientras que es cierto:
filas = cursor.fetch(filas_deseado)
si no filas:
romper
por fila en filas:
mysum += fila['id']
devolver mysum
$$ IDIOMA 'plpython3u';
Cuando pruebe este ejemplo, asegúrese de que la llamada al cursor sea en realidad una sola línea.
Python tiene que ver con la sangría, por lo que hace una diferencia si su código consta de una o
dos líneas.
2. Una vez que se ha creado el cursor, podemos recorrerlo y sumar esos números. Las columnas
dentro de esas filas se puede hacer referencia fácilmente usando nombres de columna.
3. Llamar a la función devolverá el resultado deseado:
prueba=# SELECCIONA añadir_números(10);
añadir_números
55
(1 fila)
Machine Translated by Google
302 Escribir procedimientos almacenados
Si desea inspeccionar el conjunto de resultados de una instrucción SQL, PL/Python ofrece varias
funciones que le permiten recuperar más información del resultado. Nuevamente, esas funciones son
envoltorios de lo que SPI ofrece en el nivel C.
4. La siguiente función inspecciona un resultado más de cerca:
CREAR O REEMPLAZAR FUNCIÓN result_diag(rows_desired integer)
DEVUELVE un entero COMO
$$
rv = plpy.execute("SELECCIONAR *
DESDE generar_series(1, %d) COMO id" % (filas_deseado))
plpy.notice(rv.nrows())
plpy.notice(rv.status())
plpy.notice(rv.colnames())
plpy.notice(rv.coltypes())
plpy.notice(rv.coltypmods())
volver 0
$$ IDIOMA 'plpython3u';
La función nrows() mostrará el número de filas.
La función status() nos dice si todo salió bien.
La función colnames() devuelve una lista de columnas.
La función coltypes() devuelve los ID de objeto de los tipos de datos en el conjunto de resultados.
23 es el número interno de enteros, como se muestra en el siguiente código:
test=# SELECCIONE typname DESDE pg_type DONDE oid = 23;
nombre del tipo
int4
(1 fila)
5. Luego viene typmod. Considere algo como varchar(20): la parte de configuración del
type es de lo que se trata typmod.
Machine Translated by Google
Exploración de varios lenguajes de procedimientos almacenados 303
6. Finalmente, hay una función para devolver todo como una cadena con fines de depuración. Llamar
a la función devolverá el siguiente resultado:
prueba=# SELECT result_diag(3);
AVISO: 3
AVISO: 5
AVISO: ['id']
AVISO: [23]
AVISO: [1]
resultado_diag
0
(1 fila)
El listado muestra lo que devuelve nuestra función de diagnóstico. Hay muchas más funciones en la interfaz
SPI que pueden ayudarlo a ejecutar SQL.
Manejo de errores
De vez en cuando, es posible que tenga que detectar un error. Por supuesto, esto también es posible en Python.
El siguiente ejemplo muestra cómo funciona esto:
CREAR O REEMPLAZAR FUNCIÓN trial_error()
DEVUELVE texto COMO
$$
intente: rv = plpy.execute("SELECCIONE seguramente_un_error_de_sintaxis")
excepto plpy.SPIError:
devuelve "captamos el error"
demás:
devuelve "todo bien"
$$ IDIOMA 'plpython3u';
Puede usar un bloque de prueba o excepción normal y acceder a plpy para tratar el error que desea
detectar . La función puede regresar normalmente sin destruir su transacción, de la siguiente manera:
prueba=# SELECCIONA ensayo_error();
error de prueba
Machine Translated by Google
304 Escribir procedimientos almacenados
detectamos el error
(1 fila)
Recuerde, PL/Python tiene acceso total a las funciones internas de PostgreSQL. Por lo tanto, también puede exponer
todo tipo de errores a su procedimiento. Aquí hay un ejemplo:
excepto spieExceptions.DivisionByZero:
devuelve "encontró una división por cero" excepto
excepciones de espionaje.Violación única: devuelve "encontró
una infracción única"
excepto plpy.SPIError, e:
devolver "otro error, SQLSTATE %s" % e.sqlstate
El código muestra cómo se pueden detectar varios errores de Python. Detectar errores en Python es realmente
fácil y puede ayudar a evitar que sus funciones fallen.
En esta sección, ha aprendido sobre el manejo de errores de Python. En la siguiente sección, profundizaremos un
poco más y veremos cómo podemos ayudar al optimizador.
Mejora de funciones
Hasta ahora, ha visto cómo escribir funciones básicas y disparadores en varios idiomas. Por supuesto, se admiten
muchos más idiomas. Algunos de los más destacados son PL/R (R es un poderoso paquete de estadísticas) y PL/
v8 (que se basa en el motor JavaScript de Google). Sin embargo, esos lenguajes están más allá del alcance de
este capítulo (independientemente de su utilidad).
En esta sección, nos centraremos en mejorar el rendimiento de una función. Hay algunas formas en las que
podemos acelerar el procesamiento:
• Reducir el número de llamadas a funciones • Usar
planes almacenados en caché
• Dar pistas al optimizador
En esta sección, se discutirán estos tres temas. Comencemos con la reducción del número de llamadas a
funciones y veamos cómo se puede hacer esto.
Reducir el número de llamadas a funciones
En muchos casos, el rendimiento es malo porque las funciones se llaman con demasiada frecuencia. En mi opinión, y no
puedo enfatizar este punto lo suficiente, llamar a las cosas con demasiada frecuencia es la razón principal del mal desempeño.
Machine Translated by Google
Mejora de funciones 305
Al crear una función, puede elegir entre tres tipos: volátil, estable e inmutable. Aquí hay un
ejemplo de esos tres tipos de funciones en acción:
prueba=# SELECCIONA al azar(), al azar(); |
aleatorio aleatorio
+
0.276252629235387 | 0.710661871358752
(1 fila)
prueba=# SELECCIONA ahora(), ahora();
ahora | ahora
+
20211123 15:36:22.097+01 | 20211123 15:36:22.097+01
(1 fila)
prueba=# SELECCIONA pi();
Pi
3.14159265358979
(1 fila)
Una función volátil significa que la función no se puede optimizar. Tiene que ser ejecutado una y otra vez. Una función volátil
también puede ser la razón por la que no se utiliza un determinado índice. Por defecto, cada función se considera volátil.
Una función estable siempre devolverá los mismos datos dentro de la misma transacción. Se puede optimizar
y las llamadas se pueden eliminar. La función now() es un buen ejemplo de una función estable; dentro de la
misma transacción, devuelve los mismos datos.
Las funciones inmutables son el estándar de oro porque permiten la mayoría de las optimizaciones, ya que
siempre devuelven el mismo resultado si se les da la misma entrada. Como primer paso para optimizar
funciones, siempre asegúrese de que estén marcadas correctamente agregando volátil, estable o inmutable
al final de la definición.
En la siguiente subsección, aprenderá sobre los planes almacenados en caché.
Machine Translated by Google
306 Escribir procedimientos almacenados
Uso de planes almacenados en caché
En PostgreSQL, una consulta se ejecuta en cuatro etapas:
1. Analizador: esto verifica la sintaxis.
2. Sistema de reescritura: Esto se encarga de las reglas.
3. Optimizer/planner: Esto optimiza la consulta.
4. Ejecutor: Ejecuta el plan proporcionado por el planificador.
Si la consulta es breve, los tres primeros pasos requieren relativamente mucho tiempo en comparación con el tiempo de
ejecución real. Por lo tanto, tiene sentido almacenar en caché los planes de ejecución. PL/pgSQL básicamente hace todo
el almacenamiento en caché del plan automáticamente detrás de escena. No tienes que preocuparte por eso. PL/Perl y
PL/Python le darán la opción.
La interfaz SPI proporciona funciones para que pueda manejar y ejecutar consultas preparadas, por lo
que el programador tiene la opción de preparar o no una consulta. En el caso de consultas largas, en
realidad tiene sentido utilizar consultas no preparadas. Las consultas cortas siempre deben estar
preparadas para reducir los gastos generales internos.
Asignación de costos a las funciones
Desde el punto de vista del optimizador, una función es básicamente como un operador. PostgreSQL también
tratará los costos de la misma manera que si fuera un operador estándar. El problema es el siguiente: sumar dos
números suele ser más barato que intersectar las líneas de costos usando una función proporcionada por
PostGIS. Lo que pasa es que el optimizador no sabe si una función es barata o cara.
Afortunadamente, podemos decirle al optimizador que haga las funciones más baratas o más caras. Aquí está la
sintaxis de CREATE FUNCTION:
test=# \h CREAR FUNCIÓN
Comando: CREAR FUNCIÓN
Descripción: Definir una nueva función
Sintaxis:
CREAR [ O REEMPLAZAR ] FUNCIÓN
...
| COSTO costo_ejecución
| FILAS resultado_filas
...
El parámetro COST indica cuánto más caro que un operador estándar es realmente su operador. Es
un multiplicador de cpu_operator_cost y no es un valor estático. En general, el valor predeterminado
Machine Translated by Google
Uso de funciones para diversos fines 307
el valor es 100 a menos que la función se haya escrito en C. Ahora que hemos aprendido todo sobre las
funciones, exploremos más sobre ellas en la siguiente sección.
Uso de funciones para diversos fines
En PostgreSQL, los procedimientos almacenados se pueden usar para casi todo. En este capítulo, ya ha
aprendido acerca de la cláusula CREATE DOMAIN, etc., pero también es posible crear sus propios operadores,
conversiones de tipos e incluso cotejos.
En esta sección, verá cómo se puede crear una conversión tipográfica simple y cómo se puede utilizar para su
ventaja. Para definir una conversión de tipo, considere echar un vistazo a la cláusula CREATE CAST. La sintaxis
de este comando se muestra en el siguiente código:
test=# \h CREAR REPARTO
Dominio: CREAR REPARTO
Descripción: definir un nuevo elenco
Sintaxis:
CREAR CAST (tipo_origen COMO tipo_destino)
CON FUNCIÓN nombre_función [ (tipo_argumento [, ...]) ]
[ ASIGNACIÓN ASIGNACIÓN | COMO IMPLÍCITO]
CREAR CAST (tipo_origen COMO tipo_destino)
SIN FUNCIÓN
[ ASIGNACIÓN ASIGNACIÓN | COMO IMPLÍCITO]
CREAR CAST (tipo_origen COMO tipo_destino)
CON ENTRADA
[ ASIGNACIÓN ASIGNACIÓN | COMO IMPLÍCITO]
URL: https://www.postgresql.org/docs/15/sqlcreatecast.html
Usar este material es muy simple. Simplemente le dice a PostgreSQL qué procedimiento se supone que debe
llamar para convertir cualquier tipo a su tipo de datos deseado.
En PostgreSQL estándar, no puede convertir una dirección IP en un valor booleano. Por lo tanto, es un buen
ejemplo:
1. Primero, se debe definir el procedimiento almacenado:
CREAR FUNCIÓN inet_to_boolean(inet)
DEVUELVE booleano AS
$$
COMENZAR
RETORNO verdadero;
Machine Translated by Google
308 Escribir procedimientos almacenados
FIN;
$$ IDIOMA 'plpgsql';
Para simplificar, devuelve verdadero. Sin embargo, puede usar cualquier código en cualquier idioma para
realizar la transformación real.
2. En el siguiente paso, ya es posible definir el tipo CAST:
CREAR REPARTO (inet AS booleano)
CON FUNCIÓN inet_to_boolean(inet) COMO IMPLÍCITO;
Lo primero que debemos hacer es decirle a PostgreSQL que queremos convertir inet en booleano. Luego, la
función aparece en la lista y le decimos a PostgreSQL que preferimos una conversión implícita.
3. Es un proceso simple y directo, y podemos probar el yeso de la siguiente manera:
prueba=# SELECCIONE '192.168.0.34'::inet::booleano;
bool
t
(1 fila)
El tipo de fundición fue un éxito.
La misma lógica también se puede aplicar para definir intercalaciones. Nuevamente, se puede usar un procedimiento almacenado para
realizar cualquier cosa que necesite hacer la intercalación:
test=# \h CREAR COLACIÓN
Dominio: CREAR COLACIÓN
Descripción: define una nueva colación
Sintaxis:
CREAR COLLACIÓN [SI NO EXISTE] nombre (
[ LUGAR = lugar, ]
[ LC_COLLATE = lc_collate, ]
[ LC_CTYPE = lc_ctype, ]
[ PROVEEDOR = proveedor, ]
[ DETERMINISTA = booleano, ]
[ VERSIÓN = versión ]
)
CREAR COLLACIÓN [SI NO EXISTE] nombre
DESDE colación_existente
URL: https://www.postgresql.org/docs/15/sqlcreatecollation. html
Machine Translated by Google
Resumen 309
La sintaxis de CREATE COLLATION es realmente simple. Si bien es posible crear intercalaciones, sigue siendo una de
esas características que rara vez se usa.
Los procedimientos y funciones almacenados ofrecen mucho más. Muchas cosas son posibles y se pueden hacer bien y
de manera eficiente.
Resumen
En este capítulo, ha aprendido a escribir procedimientos almacenados. Después de una introducción teórica, nuestra
atención se centró en algunas características seleccionadas de PL/pgSQL. Además de eso, aprendió a usar PL/Perl y PL/
Python, que son dos lenguajes importantes que proporciona PostgreSQL. Por supuesto, hay muchos más idiomas
disponibles. Sin embargo, debido a las limitaciones del alcance de este libro, no se pudieron cubrir en detalle. Si desea
saber más, consulte el siguiente sitio web: https://wiki.postgresql.org/wiki/PL_Matrix . También aprendimos cómo mejorar
las llamadas a funciones y cómo podemos usarlas para otros propósitos para acelerar las aplicaciones y hacer mucho más.
En el Capítulo 8, Administrar la seguridad de PostgreSQL, aprenderá sobre la seguridad de PostgreSQL. Aprenderá cómo
administrar usuarios y permisos en general, y además de eso, también aprenderá sobre la seguridad de la red.
Preguntas
1. ¿Cuál es la diferencia entre una función y un procedimiento almacenado?
2. ¿Cuál es la diferencia entre un idioma confiable y uno no confiable?
3. En general, ¿las funciones son buenas o malas?
4. ¿Qué lenguajes del lado del servidor están disponibles en PostgreSQL?
5. ¿Qué es un disparador?
6. ¿Qué lenguajes se pueden usar para escribir funciones?
7. ¿Qué idioma es el más rápido?
Las respuestas a estas preguntas se pueden encontrar en el repositorio de GitHub (https://github.com/PacktPublishing/
MasteringPostgreSQL15 ).
Machine Translated by Google
Machine Translated by Google
8
Administrar la seguridad de PostgreSQL
En el Capítulo 7, Escritura de procedimientos almacenados, aprendimos sobre los procedimientos almacenados y la escritura de
código del lado del servidor. Después de conocer muchos otros temas importantes, ahora es el momento de cambiar a la seguridad
de PostgreSQL. Aquí, aprenderemos cómo asegurar un servidor y configurar permisos para evitar brechas de seguridad.
La seguridad es un aspecto importante y se está volviendo cada vez más importante a medida que las aplicaciones están expuestas
a más y más amenazas internas y externas.
En este capítulo se tratarán los siguientes temas:
• Administrar la seguridad de la red
• Profundizando en la seguridad a nivel de fila
• Inspección de permisos
• Reasignación de objetos y eliminación de usuarios
Al final del capítulo, podremos configurar profesionalmente la seguridad de PostgreSQL. Ahora comencemos con la administración
de la seguridad de la red.
Administrar la seguridad de la red
La seguridad es algo complejo y tenemos que abordar este tema sistemáticamente para garantizar el éxito. Uno de los aspectos
clave es la seguridad de la red, que es el primer tema en el que queremos centrarnos. Sin embargo, antes de sumergirnos de lleno
en la seguridad de la red, tiene sentido encontrar un modelo mental para manejar la seguridad de PostgreSQL en general.
Aquí está mi modelo mental personal, que me ha servido bien a lo largo de los años:
• Vincular direcciones: listen_addresses en el archivo postgresql.conf
• Control de acceso basado en host: el archivo pg_hba.conf
• Permisos a nivel de instancia: usuarios, funciones, creación de base de datos, inicio de sesión y replicación
• Permisos a nivel de base de datos: conexión, creación de esquemas y más
Machine Translated by Google
312 Administrar la seguridad de PostgreSQL
• Permisos a nivel de esquema: uso de un esquema y creación de objetos dentro de un esquema
• Permisos a nivel de tabla: seleccionar, insertar, actualizar y más • Permisos a
nivel de columna: permitir o restringir el acceso a las columnas
• RLS: Restricción de acceso a filas
Para leer un valor (una celda en una tabla), PostgreSQL tiene que asegurarse de que tenemos permisos suficientes
en todos los niveles. Toda la cadena de permisos tiene que ser correcta. Mi pequeño modelo me ha servido bien a lo
largo de los años para depurar problemas relacionados con la seguridad una y otra vez en aplicaciones del mundo real.
Es de esperar que le ayude a utilizar un enfoque más sistemático, lo que conducirá a una mayor seguridad.
Comprender las direcciones de enlace y las conexiones
Al configurar un servidor PostgreSQL, una de las primeras cosas que debe hacer es definir el acceso remoto. Por
defecto, PostgreSQL no acepta conexiones remotas. Lo importante aquí es que PostgreSQL ni siquiera rechaza la
conexión, porque simplemente no escucha en el puerto. Si intentamos conectarnos, el mensaje de error en realidad
vendrá del sistema operativo, porque a PostgreSQL no le importa en absoluto.
Suponiendo que hay un servidor de base de datos que usa la configuración predeterminada en 192.168.0.123,
sucederá lo siguiente:
[hs@prueba ~]$ telnet 192.168.0.123 5432
Probando 192.168.0.123...
telnet: conectarse a la dirección 192.168.0.123: conexión rechazada
telnet: no se puede conectar al host remoto
Telnet intenta crear una conexión en el puerto 5432 y la caja remota lo rechaza instantáneamente. Desde el
exterior, parece que PostgreSQL no se está ejecutando en absoluto.
La clave del éxito se puede encontrar en el archivo postgresql.conf:
# Configuración de conexión –
# listen_addresses = 'localhost'
# en qué dirección(es) IP escuchar;
# lista de direcciones separadas por comas; # por defecto es
'localhost'; use '*' para todos
# (el cambio requiere reiniciar)
La configuración de listen_addresses le dirá a PostgreSQL qué direcciones escuchar. Técnicamente hablando,
esas direcciones son direcciones de enlace. ¿Qué significa eso realmente? Supongamos que tenemos cuatro
tarjetas de red en nuestra máquina. Podemos escuchar, digamos, en tres de esas direcciones de Protocolo de Internet (IP) .
Machine Translated by Google
Administrar la seguridad de la red 313
PostgreSQL tiene en cuenta las solicitudes a esas tres tarjetas y no escucha en la cuarta.
El puerto está simplemente cerrado.
Configurando listen_addresses
correctamente Tenemos que poner la dirección IP de nuestro servidor en listen_addresses, y no las IPs de los clientes.
Si ponemos * en PostgreSQL, escucharemos todas las direcciones IP asignadas a su máquina.
Asegúrese de que su servidor esté
reiniciado Tenga en cuenta que cambiar listen_addresses requiere un reinicio del servicio PostgreSQL.
No se puede cambiar sobre la marcha sin reiniciar.
Sin embargo, hay más configuraciones relacionadas con la administración de la conexión que es muy
importante comprender. Son los siguientes:
#puerto = 5432 # (el cambio requiere
reiniciar)
max_connections = 100 reinicio) # (el cambio requiere
#superuser_reserved_connections = 3 # (el cambio requiere reiniciar)
#unix_socket_directories = '/tmp' # lista separada por comas
# de directorios
# (el cambio requiere
Reanudar)
''
#unix_socket_group = reiniciar) # (el cambio requiere
#unix_socket_permissions = 0777 notación octal # comienza con 0 para usar
# (el cambio requiere
Reanudar)
En primer lugar, PostgreSQL escucha en un solo puerto de Protocolo de control de transmisión (TCP) , cuyo
valor predeterminado es 5432. Tenga en cuenta que PostgreSQL escuchará en un solo puerto. Cada vez que
llega una solicitud, el postmaster se bifurcará y creará un nuevo proceso para manejar la conexión. De forma
predeterminada, se permiten hasta 100 conexiones normales. Además de eso, se reservan tres conexiones
adicionales para los superusuarios. Esto significa que podemos tener 97 conexiones más 3 superusuarios o
100 conexiones de superusuario. Para empezar, primero echaremos un vistazo a las conexiones y el rendimiento.
Machine Translated by Google
314 Administrar la seguridad de PostgreSQL
Nota
Tenga en cuenta que estas configuraciones relacionadas con la conexión también necesitarán un reinicio. La razón de
esto es que se asigna una cantidad estática de memoria a la memoria compartida, que no se puede cambiar sobre la marcha.
Inspección de conexiones y rendimiento
Mientras hago consultoría de PostgreSQL, muchas personas me preguntan si aumentar el límite de conexión tendrá
un impacto en el rendimiento de la base de datos en general. La respuesta no es mucho, ya que siempre hay
algunos gastos generales debido a los cambios de contexto. Hace poca diferencia en cuanto a cuántas conexiones hay.
Sin embargo, lo que sí marca la diferencia es la cantidad de instantáneas abiertas. Cuantas más instantáneas abiertas haya ,
mayor será la sobrecarga en el lado de la base de datos. En otras palabras, podemos aumentar max_connections de forma
económica. En la siguiente sección, aprenderemos cómo evitar TCP por razones de seguridad.
Inspección del impacto de max_connections en el rendimiento Si está interesado
en algunos datos del mundo real, considere echar un vistazo a https://www. cybertecpostgresql.com/max_connections
performanceimpacts/.
Vivir en un mundo sin TCP
En algunos casos, es posible que no queramos usar una red. A menudo sucede que una base de datos solo hablará con
una aplicación local de todos modos. Tal vez nuestra base de datos PostgreSQL se envió junto con nuestra aplicación, o
tal vez simplemente no queremos correr el riesgo de usar una red. En este caso, los sockets de Unix son lo que necesita.
Los sockets Unix son un medio de comunicación sin red. Su aplicación puede conectarse a través de un socket Unix localmente
sin exponer nada al mundo exterior.
Lo que necesitamos, sin embargo, es un directorio. De forma predeterminada, PostgreSQL utilizará el directorio /tmp. Sin embargo,
si se ejecuta más de un servidor de base de datos por máquina, cada uno necesitará un directorio de datos separado para vivir.
Aparte de la seguridad, hay varias razones por las que no usar una red puede ser una buena idea. Una de estas razones es el
rendimiento. Usar sockets Unix es mucho más rápido que usar el dispositivo loopback (127.0.0.1). Si eso suena sorprendente, no
se preocupe; es para muchas personas. Sin embargo, la sobrecarga de una conexión de red real no debe subestimarse si solo
ejecuta consultas muy pequeñas.
Para representar lo que esto realmente significa, he incluido un punto de referencia simple.
Crearemos un archivo script.sql. Este es un script simple que crea un número aleatorio y lo selecciona. Es la afirmación más
simplista posible. No hay nada más simple que obtener un número.
Machine Translated by Google
Administrar la seguridad de la red 315
Entonces, ejecutemos este punto de referencia simple en una computadora portátil normal. Para hacerlo, escribiremos una
pequeña cosa llamada script.sql. Será utilizado por el siguiente benchmark:
[hs@linux ~]$ gato /tmp/script.sql
SELECCIONA 1
Luego, podemos simplemente ejecutar pgbench para ejecutar el SQL una y otra vez. La opción f nos permite pasar
el nombre del SQL al script. c 10 significa que queremos que 10 conexiones simultáneas estén activas durante 5
segundos (T 5). El punto de referencia se ejecuta como el usuario de postgres y se supone que debe usar la base
de datos de postgres, que debería estar allí de forma predeterminada. Tenga en cuenta que los siguientes ejemplos
funcionarán en derivados de Red Hat Enterprise Linux (RHEL) . Los sistemas basados en Debian utilizarán diferentes rutas:
[hs@linux ~]$ pgbench f /tmp/script.sql
c 10 T 5
U postgres postgres 2> /dev/null tipo de transacción: /
tmp/script.sql factor de escala: 1 modo de consulta:
simple
número de clientes: 10
número de hilos: 1
duración: 5 s
número de transacciones realmente procesadas: 871407
latencia media = 0,057 ms tps =
174278,158426 (incluido el establecimiento de conexiones)
tps = 174377.935625 (excluyendo el establecimiento de conexiones)
Como podemos ver, no se pasa ningún nombre de host a pgbench, por lo que la herramienta se conecta localmente al
socket de Unix y ejecuta el script lo más rápido posible. En esta caja Intel de 4 núcleos, el sistema pudo lograr alrededor
de 174,000 transacciones por segundo.
¿Qué sucede si se agrega h localhost? El rendimiento cambiará, como puede ver en el siguiente
fragmento de código:
[hs@linux ~]$ pgbench f /tmp/script.sql
h servidor local c 10 T 5
U postgres postgres 2> /dev/null
tipo de transacción: /tmp/script.sql factor de escala:
1 modo de consulta: simple
número de clientes: 10
Machine Translated by Google
316 Administrar la seguridad de PostgreSQL
número de hilos: 1
duración: 5 s
número de transacciones realmente procesadas: 535251
latencia media = 0,093 ms tps = 107000,872598
(incluido el establecimiento de conexiones)
tps = 107046.943632 (excluyendo el establecimiento de conexiones)
El rendimiento caerá como una piedra a alrededor de 107000 transacciones por segundo. La diferencia está claramente
relacionada con la sobrecarga de la red.
Manejo de volcados paralelos
Usando la opción j (el número de subprocesos asignados a pgbench), podemos exprimir algunas transacciones
más de nuestros sistemas. Sin embargo, no cambia el panorama general del punto de referencia en nuestra
situación. En otras pruebas, lo hace porque pgbench puede ser un verdadero cuello de botella si no proporciona
suficiente potencia de CPU.
Como podemos ver, la creación de redes no solo puede ser un problema de seguridad, sino también un problema de
rendimiento. Sin embargo, el rendimiento no es el único aspecto importante. Como estamos hablando principalmente de
seguridad aquí, la siguiente pieza del rompecabezas tiene que ver con pg_hba.conf.
Administrar el archivo pg_hba.conf
Después de configurar las direcciones de enlace, podemos pasar al siguiente nivel. El archivo pg_hba.conf le dirá a
PostgreSQL cómo autenticar a las personas que ingresan a la red. En general, las entradas del archivo pg_hba.conf
tienen el siguiente diseño:
# MÉTODO DE USUARIO DE LA BASE DE DATOS local [OPCIONES]
# host BASE DE DATOS USUARIO DIRECCIÓN MÉTODO [OPCIONES]
# hostssl MÉTODO DE DIRECCIÓN DEL USUARIO DE LA BASE DE DATOS [OPCIONES]
# hostnossl MÉTODO DE DIRECCIÓN DEL USUARIO DE LA BASE DE DATOS [OPCIONES]
# hostgssenc MÉTODO DE DIRECCIÓN DE USUARIO DE BASE DE DATOS [OPCIONES]
# hostnogssenc MÉTODO DE DIRECCIÓN DE USUARIO DE BASE DE DATOS [OPCIONES]
Hay varios tipos de reglas que se pueden poner en el archivo pg_hba.conf:
• local: Esto se puede utilizar para configurar conexiones de socket Unix locales.
• host: se puede utilizar para conexiones de capa de sockets seguros (SSL) y no SSL.
Machine Translated by Google
Administrar la seguridad de la red 317
• hostssl: Sólo es válido para conexiones SSL. Para hacer uso de esta opción, SSL debe estar compilado en el
servidor, que es el caso si estamos usando versiones preempaquetadas de PostgreSQL.
Además de eso, se debe configurar ssl = on en el archivo postgresql.conf. Este archivo se llama cuando se
inicia el servidor.
• hostnossl: Esto funciona para conexiones no SSL.
• hostgssenc: esta regla define que una conexión solo se crea cuando el cifrado GSSAPI
puede hacerse. De lo contrario, fallará.
• hostnogssenc: esta regla es exactamente lo contrario de hostgssenc.
Se puede incorporar una lista de reglas en el archivo pg_hba.conf. Aquí hay un ejemplo:
# TIPO BASE DE DATOS USUARIO DIRECCIÓN MÉTODO
# "local" es solo para conexiones de socket de dominio Unix
local todos todos confianza
# Conexiones locales IPv4:
hospedar a todos todo 127.0.0.1/32 confianza
# Conexiones locales IPv6:
hospedar a todos todo ::1/128 confianza
Puedes ver tres reglas simples:
• El registro local dice que se debe confiar en todos los usuarios de los sockets locales de Unix para todas las bases de datos.
El método de confianza significa que no se debe enviar ninguna contraseña al servidor y las personas pueden iniciar sesión
directamente.
• Las otras dos reglas dicen que lo mismo se aplica a las conexiones desde el host local 127.0.0.1 y ::1/128, que
es una dirección IPv6.
Dado que conectarse sin contraseña ciertamente no es la mejor opción para el acceso remoto, PostgreSQL
proporciona varios métodos de autenticación que se pueden usar para configurar el archivo pg_hba.conf de manera flexible.
Aquí hay una lista de posibles métodos de autenticación:
• confianza: Esto permite la autenticación sin proporcionar una contraseña. El usuario deseado debe ser
disponible en el lado de PostgreSQL.
• rechazar: la conexión será rechazada.
• md5 y contraseña: Las conexiones se pueden crear utilizando una contraseña. md5 significa que la contraseña
se cifra cuando se envía por cable. En el caso de las contraseñas, las credenciales se envían en texto sin
formato, lo que ya no debería hacerse en un sistema moderno. md5 ya no se considera seguro. Debe usar
scramsha256 en lugar de PostgreSQL 10 y versiones posteriores.
Machine Translated by Google
318 Administrar la seguridad de PostgreSQL
• scramsha256: esta configuración es la sucesora de md5 y utiliza un hash mucho más seguro que la versión
anterior.
• gss y sspi: Esto utiliza la interfaz de programa de aplicación de servicio de seguridad genérica (GSSAPI)
o la autenticación de interfaz de proveedor de soporte de seguridad (SSPI) . Esto solo es posible para
conexiones TCP/IP. La idea aquí es permitir el inicio de sesión único.
• ident: obtiene el nombre de usuario del sistema operativo del cliente contactando con el servidor de ident del
cliente y comprobando si coincide con el nombre de usuario de la base de datos solicitado.
• peer: supongamos que iniciamos sesión como abc en Unix. Si peer está habilitado, solo podemos iniciar
sesión en PostgreSQL como abc. Si intentamos cambiar el nombre de usuario, seremos rechazados. La
belleza es que abc no necesitará una contraseña para autenticarse. La idea aquí es que solo el
administrador de la base de datos pueda iniciar sesión en la base de datos en un sistema Unix y no otra
persona que solo tenga la contraseña o una cuenta Unix en la misma máquina. Esto solo funciona para conexiones locales.
• pam: utiliza la biblioteca de módulos de autenticación conectables (PAM) . Esto es especialmente
importante si desea utilizar un medio de autenticación que PostgreSQL no proporciona de
fábrica. Para usar el PAM, cree un archivo llamado /etc/pam.d/postgresql en su sistema Linux y
coloque el PAM deseado que planea usar en el archivo de configuración. Usando la biblioteca
PAM, incluso podemos autenticarnos contra componentes menos comunes. Sin embargo,
también se puede utilizar para conectarse a Active Directory, etc.
• ldap: esta configuración le permite autenticarse mediante el protocolo ligero de acceso a directorios
(LDAP). Tenga en cuenta que PostgreSQL solo solicitará autenticación a LDAP; si un usuario
está presente solo en el lado de LDAP y no en el lado de PostgreSQL, no puede iniciar sesión.
También debe tener en cuenta que PostgreSQL tiene que saber dónde está su servidor LDAP.
Toda esta información debe almacenarse en el archivo pg_hba.conf, como se describe en la
documentación oficial en https://www.postgresql.org/docs/14/authldap.html .
• radio: El servicio de usuario de acceso telefónico de autenticación remota (RADIUS) es un medio para realizar
inicio de sesión único. Nuevamente, los parámetros se pasan usando opciones de configuración.
• cert: este método de autenticación utiliza certificados de cliente SSL para realizar la autenticación y, por lo
tanto, solo es posible si se utiliza SSL. La ventaja aquí es que no es necesario enviar ninguna contraseña.
El atributo CN del certificado se comparará con el nombre de usuario de la base de datos solicitado y, si
coinciden, se permitirá el inicio de sesión. Se puede usar un mapa para permitir el mapeo de usuarios.
Las reglas se pueden enumerar simplemente una tras otra. Lo importante aquí es que el orden sí hace la
diferencia, como se muestra en el siguiente ejemplo:
alojar todos todos 192.168.1.0/24 scramsha256
alojar todos todos 192.168.1.54/32 rechazar
Machine Translated by Google
Administrar la seguridad de la red 319
Cuando PostgreSQL recorra el archivo pg_hba.conf, utilizará la primera regla que coincida. Entonces, si
nuestra solicitud proviene de 192.168.1.54, la primera regla siempre coincidirá antes de que lleguemos a
la segunda. Esto significa que 192.168.1.54 podrá iniciar sesión si la contraseña y el usuario son
correctos; por lo tanto, la segunda regla no tiene sentido.
Si queremos excluir la IP, debemos asegurarnos de que esas dos reglas se intercambien. En la
siguiente sección, veremos SSL y cómo puede usarlo fácilmente.
Manejo de SSL
PostgreSQL permite cifrar la transferencia entre el servidor y el cliente. El cifrado es muy
beneficioso, especialmente si nos comunicamos a largas distancias. SSL ofrece una forma simple
y segura de garantizar que nadie pueda escuchar su comunicación.
En esta sección, aprenderemos cómo configurar SSL:
1. Lo primero que debe hacer es activar el parámetro ssl en el archivo postgresql.conf cuando se inicia el
servidor. En el siguiente paso, podemos colocar certificados SSL en el directorio $PGDATA. Si no
queremos que los certificados estén en algún otro directorio, debemos cambiar los siguientes parámetros:
#ssl_cert_file = 'servidor.crt' reiniciar) # (el cambio requiere
#ssl_key_file = reinicio 'server.key') # (el cambio requiere
''
#ssl_ca_file = reiniciar) # (el cambio requiere
''
#ssl_crl_file = reiniciar) # (el cambio requiere
Si queremos usar certificados autofirmados, necesitamos ejecutar el siguiente comando:
openssl req new text out server.req
2. Responda las preguntas que le hace OpenSSL. Asegúrese de ingresar el nombre de host local como nombre
común. Podemos dejar la contraseña en blanco. Esta llamada generará una clave que está protegida con
una frase de contraseña; no aceptará una frase de contraseña que tenga menos de cuatro caracteres.
3. Para eliminar la frase de contraseña (como debe hacer si desea un inicio automático del servidor),
ejecute el siguiente código:
openssl rsa in privkey.pem out server.key
rm privkey.pem
Machine Translated by Google
320 Administrar la seguridad de PostgreSQL
4. Ingrese la frase de contraseña anterior para desbloquear la clave existente. Ahora, use el siguiente
código para convertir el certificado en un certificado autofirmado y copiar la clave y el certificado
donde el servidor los buscará:
openssl req x509 in server.req texto
clave servidor.clave salida servidor.crt
5. Después de hacer esto, asegúrese de que los archivos tengan el conjunto correcto de permisos:
chmod ogrwx servidor.clave
6. Una vez que se hayan colocado las reglas adecuadas en el archivo pg_hba.conf, puede usar SSL para
conectarse a su servidor. Para verificar que está usando SSL, considere revisar la función pg_stat_ssl.
Le informará sobre cada conexión y si usa SSL, y le proporcionará información importante sobre el
cifrado:
test=# \d pg_stat_ssl Ver
"pg_catalog.pg_stat_ssl"
Columna | Tipo | Intercalación | Anulable | Por defecto
++++
| |
emisor_dn | texto | | |
Si el campo SSL para un proceso contiene verdadero, PostgreSQL hace lo que esperaríamos que hiciera:
postgres=# seleccionar * de pg_stat_ssl;
[ REGISTRO 1 ]
pid | 20075 | t
SSL |
versión TLSv1.2 |
cifrar ECDHERSAAES256GCMSHA384 | 256 |
pedacitos
cliente
Machine Translated by Google
Administrar la seguridad de la red 321
serie_cliente | | emisor_dn
Una vez que haya configurado SSL, es hora de echar un vistazo a la seguridad a nivel de instancia.
Manejo de la seguridad a nivel de instancia
Hasta ahora, hemos configurado direcciones de enlace y le hemos dicho a PostgreSQL qué medio de autenticación
usar para qué rangos de IP. Hasta ahora, la configuración ha estado puramente relacionada con la red.
En esta sección, podemos cambiar nuestra atención a los permisos a nivel de instancia. Lo más importante que
debe saber es si los usuarios de PostgreSQL existen a nivel de instancia. Si creamos un usuario, no solo es visible
dentro de una base de datos; puede ser visto por todas las bases de datos. Un usuario puede tener permiso para
acceder a una sola base de datos, pero los usuarios se crean esencialmente a nivel de instancia.
Para aquellos de ustedes que son nuevos en PostgreSQL, hay una cosa más que deben tener en cuenta: los
usuarios y los roles son lo mismo. Las cláusulas CREATE ROLE y CREATE USER tienen valores predeterminados
diferentes (la única diferencia es que los roles no obtienen el atributo LOGIN de forma predeterminada), pero al
final del día, los usuarios y los roles son los mismos. Por lo tanto, las cláusulas CREATE ROLE y CREATE USER
admiten la misma sintaxis. La siguiente lista contiene la descripción general de la sintaxis de CREAR USUARIO:
test=# \h CREAR USUARIO
Dominio: CREAR USUARIO
Descripción: definir un nuevo rol de base de datos
Sintaxis:
Crear nombre de usuario [ [ CON ] opción [ ... ] ]
donde la opción puede ser:
SUPERUSUARIO | NOSUPERUSUARIO
| CREADODB | NOCREADOB |
CREATEROLA | NOCREATEROLA |
HEREDAR | SIN HERENCIA |
INICIAR SESIÓN | NOLOGIN
| REPLICACIÓN | NOREPLICACIÓN |
ANULACIÓN | NOBAJAR | LÍMITE DE
CONEXIÓN connlimit | [ ENCRIPTADO ]
CONTRASEÑA 'contraseña' | CONTRASEÑA NULA | VÁLIDO HASTA 'marca de
tiempo'
| EN FUNCIÓN nombre_función [, ...]
| EN GRUPO nombre_rol [, ...]
| ROL nombre_rol [, ...]
Machine Translated by Google
322 Administrar la seguridad de PostgreSQL
| ADMIN rol_nombre [, ...]
| USUARIO rol_nombre [, ...]
| URL de uid de
SYSID: https://www.postgresql.org/docs/15/sqlcreateuser.html
Discutamos esos elementos de sintaxis uno por uno. Lo primero que podemos ver es que un usuario puede ser un
superusuario o un usuario normal. Si alguien está marcado como SUPERUSUARIO, ya no hay restricciones que un
usuario normal deba enfrentar. Un SUPERUSUARIO puede soltar objetos (por ejemplo, bases de datos) como desee.
Lo siguiente importante es que se necesitan permisos a nivel de instancia para crear una nueva base de datos.
Nota importante
Tenga en cuenta que cuando alguien crea una base de datos, este usuario será automáticamente el propietario
de la base de datos.
La regla es esta: el creador es siempre automáticamente el propietario de un objeto (a menos que se especifique lo contrario,
como se puede hacer con la cláusula CREATE DATABASE). La belleza de esto es que los propietarios del objeto también
pueden dejar caer un objeto nuevamente.
Nota importante
La cláusula CREATEROLE o NOCREATEROLE define si alguien puede crear nuevos usuarios/roles.
La siguiente cosa importante es la cláusula INHERIT o NOINHERIT. Si se establece la cláusula INHERIT (que
es el valor predeterminado), un usuario puede heredar permisos de algún otro usuario. El uso de permisos
heredados nos permite usar roles, lo cual es una buena manera de abstraer permisos. Por ejemplo, podemos
crear el rol de tenedor de libros y hacer que muchos otros roles hereden del tenedor de libros. La idea es que
solo tenemos que decirle a PostgreSQL una vez lo que se le permite hacer a un rol de contador, incluso si
tenemos muchas personas trabajando en contabilidad.
La cláusula LOGIN o NOLOGIN define si un rol puede iniciar sesión en la instancia.
Nota importante
Tenga en cuenta que la cláusula LOGIN no es suficiente para conectarse realmente a una base de datos. Para
hacer eso, se requieren más permisos.
En este punto, estamos tratando de convertirlo en la instancia, que es la puerta de entrada a todas las bases de datos
dentro de la instancia. Volvamos a nuestro ejemplo: el contador podría estar marcado como NOLOGIN porque queremos
que las personas inicien sesión con sus nombres reales. Todos sus contadores (por ejemplo, Joe y Jane) pueden estar
marcados con la cláusula LOGIN, pero pueden heredar todos los permisos del rol de tenedor de libros. Una estructura
Machine Translated by Google
Administrar la seguridad de la red 323
como esto hace que sea fácil garantizar que todos los tenedores de libros tengan los mismos permisos al tiempo
que garantiza que su actividad individual se opere y registre con sus identidades separadas.
Si planeamos ejecutar PostgreSQL con replicación de transmisión, podemos realizar toda la transmisión del registro de
transacciones como un superusuario. Sin embargo, esto no se recomienda desde el punto de vista de la seguridad. Como
garantía de que no tenemos que ser un superusuario para transmitir xlog, PostgreSQL nos permite otorgar derechos de
replicación a un usuario normal, que luego puede usarse para hacer la transmisión. Es una práctica común crear un usuario
especial solo con el fin de administrar la transmisión.
Como veremos más adelante en este capítulo, PostgreSQL proporciona una característica llamada seguridad de nivel de fila (RLS).
La idea es que podamos excluir filas del alcance de un usuario. Si se supone explícitamente que un usuario debe omitir RLS,
establezca este valor en BYPASSRLS. El valor predeterminado es NOBYPASSRLS.
A veces, tiene sentido restringir la cantidad de conexiones permitidas para un usuario. El LÍMITE DE CONEXIÓN
nos permite hacer exactamente eso. Tenga en cuenta que, en general, nunca puede haber más conexiones que
las definidas en el archivo postgresql.conf (max_connections). Sin embargo, siempre podemos restringir a
ciertos usuarios a un valor más bajo.
De manera predeterminada, PostgreSQL almacenará las contraseñas cifradas en la tabla del sistema, lo cual es un buen
comportamiento predeterminado. Sin embargo, suponga que está realizando un curso de capacitación, asisten 10 estudiantes y
todos están conectados a su caja. Puede estar 100% seguro de que una de esas personas olvidará su contraseña de vez en
cuando. Como su configuración no es crítica para la seguridad, puede decidir almacenar la contraseña en texto sin formato para
que pueda buscarla fácilmente y dársela a un estudiante. Esta función también puede ser útil si está probando software.
A menudo, ya sabemos que alguien dejará la organización bastante pronto. La cláusula VALID UNTIL nos permite bloquear
automáticamente a un usuario específico si su cuenta ha expirado.
La cláusula IN ROLE enumera uno o más roles existentes a los que se agregará inmediatamente el nuevo rol
como nuevo miembro. Esto ayuda a evitar pasos manuales adicionales. Una alternativa a IN ROLE es IN GROUP.
La cláusula ROLE definirá los roles que se agregan automáticamente como miembros del nuevo rol.
La cláusula ADMIN es la misma que la cláusula ROLE, pero agrega CON OPCIÓN DE ADMINISTRADOR.
Finalmente, podemos usar la cláusula SYSID para establecer una identificación específica para el usuario (esto es similar a lo
que hacen algunos administradores de Unix para los nombres de usuario a nivel del sistema operativo). De vez en cuando, un
usuario tiene que ser modificado. La siguiente sección explica cómo se puede hacer esto.
Crear y modificar usuarios
Después de esta introducción teórica, es hora de crear usuarios y ver cómo se pueden usar las cosas en un ejemplo práctico.
Comencemos con la creación de un rol llamado tenedor de libros:
test=# CREATE ROLE contable NOLOGIN;
CREAR ROL
Machine Translated by Google
324 Administrar la seguridad de PostgreSQL
test=# CREAR ROL joe INICIAR SESIÓN;
CREAR ROL
test=# GRANT contador TO joe;
ROL DE SUBVENCIÓN
Lo primero que se ha hecho aquí es que se ha creado un rol llamado tenedor de libros.
Tenga en cuenta que no queremos que las personas inicien sesión como tenedor de libros, por lo que el rol está marcado como NOLOGIN.
CREAR ROL versus CREAR USUARIO
También debe tener en cuenta que NOLOGIN es el atributo predeterminado si utiliza la cláusula CREATE
ROLE. Si prefiere la cláusula CREATE USER, la configuración predeterminada es LOGIN.
Luego, se crea el rol joe y se marca como INICIO DE SESIÓN. Finalmente, el rol de tenedor de libros se
asigna al rol de joe, para que pueda hacer todo lo que el tenedor de libros realmente puede hacer.
Una vez que los usuarios estén en su lugar, podemos probar lo que tenemos hasta ahora:
[hs@linux ~]$ prueba psql U contable
psql: FATAL: el rol "contador" no tiene permiso para iniciar sesión
Como era de esperar, el rol de tenedor de libros no puede iniciar sesión en el sistema. ¿ Qué sucede si el rol de joe
intenta iniciar sesión? Eche un vistazo al siguiente fragmento de código:
[hs@linux ~]$ prueba psql U joe
...
prueba=>
Esto realmente funcionará como se esperaba. Sin embargo, tenga en cuenta que el símbolo del sistema ha cambiado.
Esta es solo una forma en que PostgreSQL le muestra que no ha iniciado sesión como superusuario.
Una vez creado un usuario, puede ser necesario modificarlo. Una cosa que podríamos querer cambiar es
la contraseña. En PostgreSQL, los usuarios pueden cambiar sus propias contraseñas. Así es como funciona:
test=> ALTERAR ROL joe CONTRASEÑA 'abc';
ALTERAR ROL
test=> SELECCIONA usuario_actual;
usuario actual
Joe
(1 fila)
Machine Translated by Google
Administrar la seguridad de la red 325
Manejar las contraseñas con
cuidado Tenga en cuenta el hecho de que ALTER ROLE cambia los atributos de un rol. PASSWORD hará
que la contraseña aparezca en el archivo de registro si se configuró el registro del lenguaje de definición de
datos (DDL) . Esto no es demasiado deseable. Es mejor cambiar la contraseña usando una herramienta
visual. En este caso, existe cierto soporte de protocolo, lo que garantiza que la contraseña nunca se envíe
por cable en texto sin formato. Usar ALTER ROLE directamente para cambiar la contraseña no es una buena idea en absoluto.
La cláusula ALTER ROLE (o ALTER USER) nos permitirá cambiar la mayoría de las configuraciones que se pueden
configurar durante la creación del usuario. Sin embargo, hay aún más en la gestión de usuarios. En muchos casos,
queremos asignar parámetros especiales a un usuario. La cláusula ALTER USER nos da los medios para hacerlo:
ALTERAR USUARIO { role_specification | TODO }
[ EN BASE DE DATOS nombre_base_datos ]
SET configuración_parámetro { PARA | = } { valor | POR DEFECTO }
ALTERAR USUARIO { role_specification | TODO }
[ EN BASE DE DATOS nombre_base_datos ]
ESTABLECER configuración_parámetro DESDE ACTUAL
ALTERAR USUARIO { role_specification | TODO }
[ EN BASE DE DATOS nombre_base_datos ]
RESET configuración_parámetro
ALTERAR USUARIO { role_specification | TODO }
[ EN BASE DE DATOS nombre_base_de_datos ] RESTABLECER TODO
La sintaxis es bastante simple y bastante directa. Para describir por qué esto es realmente útil, he agregado un
ejemplo del mundo real. Supongamos que Joe vive en la isla Mauricio. Cuando inicia sesión , quiere estar en su
propia zona horaria, aunque su servidor de base de datos se encuentra en Europa. Configuremos la zona horaria
por usuario:
test=> ALTER ROLE joe SET TimeZone = 'UTC4';
ALTERAR ROL
prueba=> SELECCIONA ahora();
ahora
20211009 20:36:48.571584+01
(1 fila)
prueba=> \q
[hs@linux ~]$ prueba psql U joe
...
Machine Translated by Google
326 Administrar la seguridad de PostgreSQL
prueba=> SELECCIONA ahora();
ahora
20211009 23:36:53.357845+04
(1 fila)
La cláusula ALTER ROLE modificará el usuario. Tan pronto como joe se vuelva a conectar, la zona horaria ya estará
configurada para él.
Nota importante
La zona horaria no se cambia inmediatamente. Debe volver a conectarse o usar un SET ...
Cláusula TO DEFAULT.
Lo importante aquí es que esto también es posible para algunos parámetros de memoria, como work_mem, que ya
se trataron anteriormente en este libro.
Definición de seguridad a nivel de base de datos
Después de configurar los usuarios a nivel de instancia, es posible profundizar y ver qué se puede hacer a nivel de base
de datos. La primera pregunta importante que surge es la siguiente: permitimos explícitamente que Joe inicie sesión en la
instancia de la base de datos, pero ¿quién o qué permitió que Joe se conectara realmente a una de las bases de datos?
Tal vez no desee que Joe acceda a todas las bases de datos de su sistema. Restringir el acceso a ciertas bases de datos
es exactamente lo que podemos lograr en este nivel.
Para las bases de datos, los siguientes permisos se pueden configurar mediante una cláusula GRANT:
CONCEDER { { CREAR | CONECTAR | TEMPORAL | TEMPERATURA } [, ...] | TODOS LOS [ PRIVILEGIOS ] }
EN BASE DE DATOS nombre_base_datos [, ...]
TO role_specification [, ...] [ CON OPCIÓN DE CONCESIÓN ]
[ OTORGADO POR role_specification ]
Hay dos permisos principales en el nivel de la base de datos que merecen mucha atención:
• CREAR: Esto le permite a alguien crear un esquema dentro de la base de datos. Tenga en cuenta que una
cláusula CREATE no permite la creación de tablas; se trata de esquemas. En PostgreSQL, una tabla reside
dentro de un esquema, por lo que primero debe llegar al nivel del esquema para poder crear una tabla.
• CONECTAR: Esto permite que alguien se conecte a una base de datos.
Machine Translated by Google
Administrar la seguridad de la red 327
La pregunta ahora es esta: nadie ha asignado explícitamente ningún permiso de CONEXIÓN al rol de joe,
entonces, ¿de dónde provienen realmente esos permisos? La respuesta se llama público, que es similar
al mundo de Unix. Si al mundo se le permite hacer algo, también a Joe, que es parte del público en general.
Lo principal es que public no es un rol en el sentido de que se puede descartar y renombrar. Simplemente
podemos verlo como el equivalente de todos en el sistema.
Por lo tanto, para garantizar que no todos puedan conectarse a cualquier base de datos en cualquier momento, es posible que
CONNECT deba ser revocado del público en general. Para ello, podemos conectarnos como superusuario y solucionar el problema:
[hs@linux ~]$ prueba psql U postgres
...
test=# REVOCAR TODO EN LA BASE DE DATOS test FROM public;
REVOCAR
test=# \q
[hs@linux ~]$ psql test U joe psql: FATAL:
permiso denegado para la base de datos "test"
DETALLE: El usuario no tiene el privilegio de CONECTAR.
Como podemos ver, el rol de joe ya no puede conectarse. En este punto, solo los superusuarios tienen acceso
Probar.
En general, es una buena idea revocar los permisos de la base de datos de postgres incluso antes de que se
creen otras bases de datos. La idea detrás de este concepto es que esos permisos ya no estarán en todas esas
bases de datos recién creadas. Si alguien necesita acceso a una determinada base de datos, los derechos
deben otorgarse explícitamente. Los derechos ya no están automáticamente allí.
Si queremos permitir que el rol de joe se conecte a la base de datos de prueba, intente lo siguiente como superusuario:
[hs@linux ~]$ prueba psql U postgres
...
test=# GRANT CONNECT ON DATABASE test TO contable;
CONCEDER
prueba=# \q
[hs@linux ~]$ psql prueba U joe
...
prueba=>
Machine Translated by Google
328 Administrar la seguridad de PostgreSQL
Hay dos opciones aquí:
• Podemos permitir el rol joe directamente para que solo el rol joe pueda conectarse.
• Alternativamente, podemos otorgar permisos al rol de tenedor de libros. Recuerde, el rol de joe heredará todos
los permisos del rol de contador, por lo que si queremos que todos los contadores puedan conectarse a la
base de datos, asignar permisos al rol de contador parece ser una idea atractiva.
No es arriesgado si otorgamos permisos al rol de contador, porque el rol no puede iniciar sesión en la instancia en
primer lugar, por lo que solo sirve como una fuente de permisos.
Ajuste de permisos a nivel de esquema
Una vez que hayamos terminado de configurar el nivel de la base de datos, tiene sentido echar un vistazo al nivel del esquema.
Lo importante aquí es que estamos ante dos tipos de comportamientos:
• PostgreSQL 14 y anteriores
• PostgreSQL 15 y posteriores
Es importante tener en cuenta que el comportamiento predeterminado ha cambiado en PostgreSQL. Veamos qué sucedió
y observemos el comportamiento de PostgreSQL 14 y versiones anteriores.
PostgreSQL 14 y anteriores
Hagamos una pequeña prueba:
prueba=> CREAR BASE DE DATOS prueba;
ERROR: permiso denegado para crear base de datos
test=> CREAR USUARIO xy;
ERROR: permiso denegado para crear rol
test=> CREAR ESQUEMA ventas;
ERROR: permiso denegado para prueba de base de datos
Como podemos ver, joe está teniendo un mal día y no se permite nada más que conectarse a la base de datos.
Sin embargo, hay una pequeña excepción, y sorprende a muchas personas:
test=> CREAR TABLA t_roto (id int);
CREAR MESA
prueba=> \d
Lista de relaciones
Machine Translated by Google
Administrar la seguridad de la red 329
Esquema | Nombre | Tipo | Dueño
+++
publico | t_roto | mesa | José
(1 filas)
De forma predeterminada, public puede trabajar con el esquema público, que siempre está presente. Si está
seriamente interesado en proteger su base de datos, asegúrese de solucionar este problema. De lo contrario, los
usuarios normales potencialmente enviarán spam a su esquema público con todo tipo de tablas, y toda la
configuración podría verse afectada. También debe tener en cuenta que si a alguien se le permite crear un
objeto, esa persona también es su propietario. Propiedad significa que todos los permisos están automáticamente
disponibles para el creador, incluidos los permisos para la destrucción del objeto.
Para quitar esos permisos del público, ejecute la siguiente línea como superusuario:
test=# REVOCAR TODO EN EL ESQUEMA public FROM public;
REVOCAR
Esto es realmente importante en las implementaciones anteriores a los 15 y debe hacerse en todas las
circunstancias. De ahora en adelante, nadie puede poner cosas en su esquema público sin los permisos correctos.
La siguiente lista es prueba de ello:
[hs@linux ~]$ prueba psql U joe
...
test=> CREAR TABLA t_data (id int);
ERROR: no se ha seleccionado ningún esquema para crear en
LÍNEA 1: CREAR TABLA t_data (id int);
Como podemos ver, el comando fallará. Lo importante aquí es el mensaje de error que se
mostrará. PostgreSQL no sabe dónde colocar estas tablas. Por defecto, intentará colocar la
tabla en uno de los siguientes esquemas:
test=> SHOW search_path ; búsqueda_ruta
"$usuario", público (1 fila)
Como no hay un esquema llamado joe, esta no es una opción, por lo que PostgreSQL probará el esquema público.
Como no hay permisos, se quejará de que no sabe dónde poner la mesa.
Machine Translated by Google
330 Administrar la seguridad de PostgreSQL
PostgreSQL 15 y más allá
En PostgreSQL 15 el comportamiento ha cambiado. Ahora es necesario establecer explícitamente los permisos
para el esquema público para evitar el siguiente error:
test=> CREAR TABLA public.t_data (id int); ERROR: permiso denegado para
el esquema public LINE 1: CREATE TABLE public.t_data (id int);
En este caso, obtendremos el mensaje de error que espera. PostgreSQL niega el acceso al esquema público.
La siguiente pregunta lógica ahora es la siguiente: ¿qué permisos se pueden establecer en el nivel de esquema
para dar más poder al rol de joe? Vamos a ver:
CONCEDER { { CREAR | USO } [, ...] | TODOS LOS [ PRIVILEGIOS ] }
EN ESQUEMA nombre_esquema [, ...]
TO role_specification [, ...] [ CON OPCIÓN DE CONCESIÓN ]
[ OTORGADO POR role_specification ]
CREAR significa que alguien puede poner objetos en un esquema. USO significa que alguien puede ingresar al
esquema. Tenga en cuenta que ingresar el esquema no significa que realmente se pueda usar algo dentro del
esquema; esos permisos aún no se han definido. Esto solo significa que el usuario puede ver el catálogo del sistema
para este esquema.
Para permitir que el rol joe acceda a la tabla que creó previamente, será necesaria la siguiente línea (ejecutada como
superusuario):
test=# USO DE CONCESIÓN EN EL ESQUEMA public TO tenedor de libros;
CONCEDER
El rol joe ahora puede leer su tabla como se esperaba:
[hs@linux ~]$ psql test U joe test=> SELECT
count(*) FROM t_broken;
contar
(1 fila)
Machine Translated by Google
Administrar la seguridad de la red 331
El rol joe también puede agregar y modificar filas porque resulta ser el propietario de la tabla.
Sin embargo, aunque ya puede hacer muchas cosas, el papel de joe aún no es todopoderoso. Considere la
siguiente declaración:
test=> ALTER TABLE t_roto RENOMBRAR A t_útil;
ERROR: permiso denegado para el esquema público
Echemos un vistazo más de cerca al mensaje de error real. Como podemos ver, el mensaje se queja
de los permisos del esquema, no de los permisos de la tabla en sí (recuerde, el rol de joe es el
propietario de la tabla). Para solucionar el problema, debe abordarse a nivel de esquema y no a nivel de tabla.
Ejecute la siguiente línea como superusuario:
test=# GRANT CREATE ON SCHEMA public TO contable;
CONCEDER
Los permisos CREATE en el esquema público se asignan al contable.
El rol joe ahora puede cambiar el nombre de su tabla a un nombre más significativo:
[hs@linux ~]$ prueba psql U joe
test=> ALTER TABLE t_roto RENOMBRAR A t_útil;
ALTERAR TABLA
Tenga en cuenta que esto es necesario si se utilizan DDL. En mi trabajo diario como proveedor de servicios de
soporte de PostgreSQL, he visto un par de problemas en los que esto resultó ser un problema.
Trabajar con tablas
Después de ocuparnos de las direcciones de enlace, la autenticación de red, los usuarios, las bases de datos y los esquemas,
finalmente llegamos al nivel de la tabla. El siguiente fragmento de código muestra qué permisos se pueden establecer para
una tabla:
OTORGAR { { SELECCIONAR | INSERTAR | ACTUALIZAR | ELIMINAR | TRUNCADO |
REFERENCIAS | DESENCADENAR }
[, ...] | TODOS LOS [ PRIVILEGIOS ] }
ON { [ TABLA ] nombre_tabla [, ...]
| TODAS LAS TABLAS EN ESQUEMA schema_name [, ...] }
TO role_specification [, ...] [ CON OPCIÓN DE CONCESIÓN ]
[ OTORGADO POR role_specification ]
Machine Translated by Google
332 Administrar la seguridad de PostgreSQL
Permítanme explicar estos permisos uno por uno:
• SELECCIONAR: Esto le permite leer una tabla.
• INSERT: Esto le permite agregar filas a la tabla (esto también incluye copiar, etc.; no se trata solo de la cláusula
INSERT). Tenga en cuenta que si se le permite insertar, no se le permite leer automáticamente. Las cláusulas
SELECT e INSERT son necesarias para poder leer los datos que ha insertado.
• ACTUALIZAR: Esto modifica el contenido de una tabla.
• ELIMINAR: Esto se utiliza para eliminar filas de una tabla.
• TRUNCATE: Esto le permite usar la cláusula TRUNCATE. Tenga en cuenta que las cláusulas DELETE y
TRUNCATE son dos permisos separados porque la cláusula TRUNCATE bloqueará la tabla, lo que no hace la
cláusula DELETE (ni siquiera si no hay una condición WHERE).
• REFERENCIAS: Permite la creación de claves foráneas. Es necesario tener este privilegio tanto en la columna
de referencia como en la referenciada, de lo contrario, la creación de la clave no funcionará.
• TRIGGER: Esto permite la creación de disparadores.
Haciendo uso de GRANT
Lo bueno de la cláusula GRANT es que podemos establecer permisos en todas las tablas en un esquema al
mismo tiempo.
Esto simplifica enormemente el proceso de ajuste de permisos. También es posible utilizar la cláusula WITH GRANT
OPTION. La idea es garantizar que los usuarios normales puedan pasar permisos a otros, lo que tiene la ventaja de
poder reducir la carga de trabajo de los administradores de manera bastante significativa. Imagínese un sistema que
proporcione acceso a cientos de usuarios. Puede comenzar a ser mucho trabajo administrar a todas esas personas y,
por lo tanto, los administradores pueden designar personas para administrar un subconjunto de los datos ellos mismos.
Manejo de la seguridad a nivel de columna
En algunos casos, no todo el mundo puede ver todos los datos. Por ejemplo, imagina un banco. Algunas personas
pueden ver toda la información sobre una cuenta bancaria, mientras que otras pueden estar limitadas a solo un
subconjunto de los datos. En una situación del mundo real, es posible que a alguien no se le permita leer la columna
del saldo, mientras que otra persona puede no ver las tasas de interés de los préstamos de las personas.
Otro ejemplo sería que las personas pueden ver los perfiles de otras personas, pero no sus fotos u otra
información privada. La pregunta ahora es esta: ¿cómo se puede usar la seguridad a nivel de columna?
Para demostrar esto, agregaremos una columna a la tabla existente que pertenece al rol joe:
test=> ALTER TABLE t_useful ADD COLUMN name text;
ALTERAR TABLA
Machine Translated by Google
Administrar la seguridad de la red 333
La tabla ahora consta de dos columnas. El objetivo del ejemplo es garantizar que un usuario pueda ver solo
una de esas columnas:
test=> \d t_útil Tabla "public.t_útil"
Columna | Tipo | modificadores
++
identificación
| entero | nombre |
texto |
Como superusuario, vamos a crear un usuario y darle acceso al esquema que contiene nuestra tabla:
test=# CREAR ROL paul INICIAR SESIÓN;
CREAR ROL
test=# GRANT CONNECT ON DATABASE test TO paul;
CONCEDER
test=# OTORGAR USO EN ESQUEMA public TO paul;
CONCEDER
No olvide otorgar derechos de CONNECT al chico nuevo, paul, porque anteriormente en el capítulo, CONNECT
fue revocado del público. Por lo tanto, la concesión explícita es absolutamente necesaria para garantizar que
podamos llegar a la mesa.
Los permisos SELECT se pueden otorgar al rol paul. Así es como funciona:
test=# GRANT SELECT (id) ON t_useful TO paul;
CONCEDER
Esto ya es suficiente. Ya es posible conectarse a la base de datos como usuario paul y leer la columna:
[hs@linux ~]$ prueba psql U paul
...
prueba => SELECCIONE id DE t_útil;
identificación
(0 filas)
Machine Translated by Google
334 Administrar la seguridad de PostgreSQL
Si estamos usando permisos a nivel de columna, hay algo importante a tener en cuenta. Deberíamos
dejar de usar SELECT * ya que ya no funcionará:
test=> SELECT * FROM t_util;
ERROR: permiso denegado para relación t_útil
* todavía significa todas las columnas, pero como no hay forma de acceder a todas las columnas, las cosas fallarán instantáneamente.
Configuración de privilegios predeterminados
Hasta ahora, ya se han configurado muchas cosas. El problema es, ¿qué sucede si se agregan nuevas tablas
al sistema? Puede ser bastante doloroso y arriesgado procesar estas tablas una por una y establecer los
permisos adecuados. ¿No sería bueno que esas cosas sucedieran automáticamente? Esto es exactamente lo
que hace la cláusula ALTER DEFAULT PRIVILEGES. La idea es dar a los usuarios la opción de hacer que
PostgreSQL establezca automáticamente los permisos deseados tan pronto como un objeto entre en existencia.
Ahora es imposible simplemente olvidar establecer esos derechos.
La siguiente lista muestra la primera parte de la especificación de sintaxis:
prueba=# \h ALTERAR PREDETERMINADO
Dominio: ALTERAR PRIVILEGIOS PREDETERMINADOS
Descripción: define los privilegios de acceso predeterminados
Sintaxis:
ALTERAR PRIVILEGIOS PREDETERMINADOS
[ PARA { PAPEL | USUARIO } target_role [, ...] ]
[ IN SCHEMA schema_name [, ...] ]
concesión_o_revocación_abreviada donde
concesión_o_revocación_abreviada es uno de: GRANT { { SELECCIONAR
| INSERTAR | ACTUALIZAR | ELIMINAR | TRUNCADO |
REFERENCIAS | DESENCADENAR }
[, ...] | TODOS LOS [ PRIVILEGIOS ] }
EN MESAS
PARA { [ GRUPO ] nombre_rol | PÚBLICO } [, ...] [ CON OPCIÓN DE CONCESIÓN ]
...
La sintaxis funciona de manera similar a la cláusula GRANT y, por lo tanto, es fácil e intuitiva de usar.
Para mostrarnos cómo funciona, compilé un ejemplo simple. La idea es que si el rol de joe crea una mesa,
Machine Translated by Google
Profundizando en la seguridad a nivel de fila 335
el rol de paul automáticamente podrá usarlo. La siguiente lista muestra cómo se pueden asignar los privilegios
predeterminados:
test=# ALTERAR PRIVILEGIOS PREDETERMINADOS PARA EL ROL joe
EN ESQUEMA público OTORGAR TODO EN TABLAS A paul;
ALTERAR PRIVILEGIOS PREDETERMINADOS
En mi ejemplo, todas las tablas del esquema público tienen todos los permisos.
Ahora conectémonos como el rol de joe y creemos una tabla:
[hs@linux ~]$ prueba psql U joe
...
prueba => CREAR TABLA t_user (id serial, texto de nombre, texto de contraseña);
CREAR MESA
Como puede ver, la tabla ahora se puede crear con éxito.
Conectarse como el rol de paul demostrará que la tabla se ha asignado al conjunto de permisos adecuado:
[hs@linux ~]$ prueba psql U paul
...
prueba=> SELECCIONAR * DESDE t_usuario;
identificación | nombre | Contraseña
++
(0 filas)
La tabla también se puede leer bien y PostgreSQL no genera errores.
Profundizando en la seguridad a nivel de fila
Hasta este punto, una tabla siempre se ha mostrado como un todo. Cuando la tabla contenía un millón de
filas, era posible recuperar un millón de filas de ella. Si alguien tenía derecho a leer una tabla, se refería a la
tabla entera. En muchos casos, esto no es suficiente. A menudo es deseable que a un usuario no se le
permita ver todas las filas.
Considere el siguiente ejemplo del mundo real, donde un contador está haciendo trabajo de contabilidad para
muchas personas. La tabla que contiene las tasas de impuestos debería ser realmente visible para todos, ya
que todos tienen que pagar las mismas tasas. Sin embargo, cuando se trata de transacciones reales, el contador
puede querer asegurarse de que todos solo puedan ver sus propias transacciones. A la persona A no se le debe
permitir ver los datos de la persona B. Además de eso, también podría tener sentido que el jefe de una división
pueda ver todos los datos en su parte de la empresa.
Machine Translated by Google
336 Administrar la seguridad de PostgreSQL
RLS se ha diseñado para hacer exactamente esto y le permite crear sistemas multiinquilino de forma rápida y
sencilla. La forma de configurar esos permisos es generar políticas. El comando CREAR POLÍTICA está aquí
para proporcionarnos un medio para escribir estas reglas. Veamos un ejemplo del comando CREAR POLÍTICA:
test=# \h CREAR POLÍTICA
Dominio: CREAR POLÍTICA
Descripción: define una nueva política de seguridad de nivel de fila para una tabla
Sintaxis:
CREAR POLÍTICA nombre EN table_name
[ COMO { PERMISIVO | RESTRICTIVO } ]
[ PARA { TODOS | SELECCIONAR | INSERTAR | ACTUALIZAR | BORRAR } ]
[ PARA { nombre_rol | PÚBLICO | ROL_ACTUAL |
USUARIO_ACTUAL | USUARIO_SESIÓN } [, ...] ]
[ USANDO ( usando_expresión ) ]
[ CON CHEQUE ( check_expression ) ]
URL: https://www.postgresql.org/docs/15/sqlcreatepolicy.html
La sintaxis no es difícil de entender. Sin embargo, se proporciona, por supuesto, un ejemplo. Para representar cómo se
puede escribir una política, primero iniciemos sesión como superusuario y creemos una tabla que contenga un par de entradas:
test=# CREATE TABLE t_person (texto de género, texto de nombre);
CREAR MESA
test=# INSERTAR EN t_persona ('masculino',
VALORES 'joe'),
('masculino', 'pablo'),
('femenino', 'sarah'),
(NULO, 'R2D2');
INSERTAR 0 4
Se crea una tabla y los datos se agregan correctamente. Luego se otorga acceso al rol joe, como se puede ver
en el siguiente código:
test=# OTORGAR TODO EN t_person A joe;
CONCEDER
Machine Translated by Google
Profundizando en la seguridad a nivel de fila 337
Hasta ahora, todo es bastante normal y el rol de joe podrá leer toda la tabla, ya que no hay RLS en
su lugar. Pero veamos qué sucede si la SEGURIDAD DE NIVEL DE FILA está habilitada para la tabla:
test=# ALTER TABLE t_person HABILITAR SEGURIDAD A NIVEL DE FILA;
ALTERAR TABLA
Hay una denegación y todas las políticas predeterminadas están en su lugar, por lo que el rol de joe en realidad obtendrá una tabla vacía:
test=> SELECT * FROM t_persona; género |
nombre
+
(0 filas)
La política predeterminada tiene mucho sentido, ya que los usuarios se ven obligados a establecer permisos de forma explícita.
Ahora que la tabla está bajo el control de RLS, las políticas se pueden escribir como superusuario:
test=# CREAR POLÍTICA joe_pol_1
EN t_persona
PARA SELECCIONAR A joe
USO (género = 'masculino');
CREAR POLÍTICA
Iniciar sesión como el rol de joe y seleccionar todos los datos devolverá solo dos filas:
test=> SELECT * FROM t_persona; género | nombre
+
masculino | jose |
masculino Pablo
(2 filas)
Inspeccionemos la política que acabamos de crear de una manera más detallada. Lo primero que podemos ver
es que la política en realidad tiene un nombre. También está conectado a una tabla y permite ciertas operaciones
(en este caso, la cláusula SELECT). Luego viene la cláusula USING. Esto define lo que se le permitirá ver al rol
de joe. La cláusula USING es, por lo tanto, un filtro obligatorio adjunto a cada consulta para seleccionar solo las
filas que se supone que debe ver nuestro usuario.
Machine Translated by Google
338 Administrar la seguridad de PostgreSQL
También hay una nota al margen importante, donde, si hay más de una sola política, PostgreSQL usará una
condición OR. En resumen, más políticas harán que veas más datos de forma predeterminada. En
PostgreSQL 9.6, este siempre fue el caso. Sin embargo, con la introducción de PostgreSQL 10.0, el usuario
puede elegir si las condiciones deben conectarse mediante OR o AND. Están disponibles las siguientes dos opciones:
PERMISIVO | RESTRICTIVO
De forma predeterminada, PostgreSQL es PERMISIVO, por lo que las conexiones OR funcionan. Si decidimos
usar RESTRICTIVO, entonces esas cláusulas se conectarán con AND.
Ahora supongamos que, por alguna razón, se ha decidido que el rol de joe también puede ver robots.
Hay dos opciones para lograr nuestro objetivo. La primera opción es simplemente usar la cláusula ALTER POLICY
para cambiar la política existente:
test=# \h ALTERAR POLÍTICA
Dominio: POLÍTICA DE ALTERACIÓN
Descripción: cambiar la definición de una política de seguridad de nivel de fila
Sintaxis:
ALTERAR POLÍTICA nombre EN nombre_tabla CAMBIAR NOMBRE A nombre_nuevo
ALTERAR POLÍTICA nombre ON table_name
[ A { nombre_rol | PÚBLICO | ROL_ACTUAL |
USUARIO_ACTUAL | USUARIO_SESIÓN } [, ...] ]
[ USANDO ( usando_expresión ) ]
[ CON CHEQUE ( check_expression ) ]
URL: https://www.postgresql.org/docs/15/sqlalterpolicy.html
Este listado muestra la sintaxis de ALTER POLICY. Es bastante similar a CREAR POLÍTICA.
La segunda opción es crear una segunda política, como se muestra en el siguiente ejemplo:
test=# CREAR POLÍTICA joe_pol_2
EN t_persona
PARA SELECCIONAR A joe
USO (género ES NULO);
CREAR POLÍTICA
Machine Translated by Google
Profundizando en la seguridad a nivel de fila 339
La creación de la política funcionó correctamente. La belleza es que esas políticas simplemente se conectan
usando una condición O, como se indicó anteriormente, a menos que se use RESTRICTIVO. Por lo tanto,
PostgreSQL ahora devolverá tres filas en lugar de dos:
test=> SELECT * FROM t_persona; género | nombre
+
masculino | jose |
masculino Pablo
| R2D2
(3 filas)
El rol R2D2 ahora también se incluye en el resultado, ya que coincide con la segunda política.
Para representar cómo PostgreSQL ejecuta la consulta, he decidido incluir un plan de ejecución de la consulta:
prueba=> explicar SELECT * FROM t_person;
PLAN DE CONSULTA
Seq Scan en t_person (costo=0.00...21.00 filas=9 ancho=64)
Filtro: ((género ES NULO) O (género = 'masculino'::texto))
(2 filas)
Como podemos ver, ambas cláusulas USING se han agregado como filtros obligatorios a la consulta. Es
posible que haya notado en la definición de sintaxis que hay dos tipos de cláusulas:
• USING: esta cláusula filtra las filas que ya existen. Esto es relevante para SELECCIONAR y ACTUALIZAR
cláusulas, etc.
• CHECK: esta cláusula filtra las nuevas filas que están a punto de crearse para que sean relevantes para las
cláusulas INSERT y UPDATE, y así sucesivamente.
Esto es lo que sucede si intentamos insertar una fila:
test=> INSERTAR EN t_person VALORES ('masculino', 'kaarel');
ERROR: la nueva fila infringe la política de seguridad de nivel de fila para la tabla "t_person"
Como no existe una política para la cláusula INSERT, la declaración naturalmente generará un error. Aquí está la política
para permitir inserciones:
test=# CREAR POLÍTICA joe_pol_3
EN t_persona
Machine Translated by Google
340 Administrar la seguridad de PostgreSQL
PARA INSERTAR A joe
CON CHEQUE (género IN ('masculino', 'femenino'));
CREAR POLÍTICA
El rol joe puede agregar hombres y mujeres a la tabla, que se muestra en la siguiente lista:
test=> INSERTAR EN t_person VALORES ('mujer', 'maria');
INSERTAR 0 1
Sin embargo, también hay una trampa. Considere el siguiente ejemplo:
test=> INSERTAR EN t_person VALORES ('mujer', 'maria')
REGRESANDO *;
ERROR: la nueva fila infringe la política de seguridad de nivel de fila para la tabla "t_person"
Recuerde, solo existe una política para seleccionar hombres. El problema aquí es que la declaración devolverá una
mujer, lo que no está permitido, ya que el rol de joe está bajo una política de solo hombres.
La cláusula RETURNING * solo funcionará para hombres:
test=> INSERTAR EN t_person VALORES ('male', 'max') REGRESANDO *;
género | nombre
+
masculino | máximo
(1 fila)
INSERTAR 0 1
Si no queremos este comportamiento, tenemos que escribir una política que realmente contenga una cláusula USING adecuada.
RLS es un componente importante en todos los sistemas de seguridad del lado de la base de datos. Una vez que hemos
definido nuestra política de RLS, tiene sentido dar un paso atrás y ver cómo puede inspeccionar los permisos.
Inspección de permisos
Cuando se han establecido todos los permisos, a veces es necesario saber quién tiene qué permisos.
Es fundamental que los administradores averigüen quién puede hacer qué. Desafortunadamente, este proceso no es tan
fácil y requiere un poco de conocimiento. Por lo general, soy un gran admirador del uso de la línea de comandos. Sin
embargo, en el caso del sistema de permisos, realmente puede tener sentido usar una interfaz gráfica de usuario para hacer las cosas.
Machine Translated by Google
Inspección de permisos 341
Antes de mostrarle cómo leer los permisos de PostgreSQL, asignemos derechos al rol llamado joe para que
podamos inspeccionarlos en el siguiente paso:
test=# OTORGAR TODO EN t_person A joe;
CONCEDER
La información sobre los permisos se puede recuperar usando el comando z en psql:
prueba=# \x
La pantalla ampliada está activada.
test=# \z t_person Privilegios
de acceso
[ REGISTRO 1 ]+
Esquema | publico |
Nombre t_persona | mesa
Tipo
Privilegios de acceso | postgres=arwdDxt/postgres
+
| joe=arwdDxt/postgres
Privilegios de columna | |
Políticas joe_pol_1 (r): | (u): (género =
+ 'masculino'::texto) | a: joe | joe_pol_2 (r): | (u): (género
+ ES NULO) | a:
+ joe | joe_pol_3 (a): | (c):
+ (género = ANY (ARRAY['masculino'::texto,
+
+
+
'mujer'::texto]))
+ | a: joe
Esto devolverá todas esas políticas, junto con información sobre los privilegios de acceso a joe. Desafortunadamente,
esos accesos directos son difíciles de leer y tengo la sensación de que los administradores no los entienden en general.
En este ejemplo, el rol joe obtuvo arwdDxt de PostgreSQL. ¿ Qué significan realmente esos
atajos? Vamos a ver:
• a: Esto se agrega a la cláusula INSERT
• r: Esto lee para la cláusula SELECT
Machine Translated by Google
342 Administrar la seguridad de PostgreSQL
• w: Esto escribe para la cláusula UPDATE
• d: Esto elimina para la cláusula DELETE
• D: Esto se usa para la cláusula TRUNCATE (cuando esto se introdujo, t ya estaba en uso)
• x: Esto se usa para referencias
• t: Esto se usa para disparadores
Si no conoce este código, también hay una segunda forma de hacer que las cosas sean más legibles. Considere la
siguiente llamada de función:
test=# SELECT * FROM aclexplode('{joe=arwdDxt/postgres}'); otorgante | beneficiario | tipo_privilegio
| es_concedible
+++
10 | 18481 | INSERTAR | f |
10 | 18481 | SELECCIONAR f | f
10 | 18481 | ACTUALIZAR | f |
10 | 18481 | BORRAR f | f
10 | 18481 | TRUNCAR | F
10 | 18481 | REFERENCIAS
10 | 18481 | DESENCADENAR
(7 filas)
Como podemos ver, el conjunto de permisos se devuelve como una tabla simple, lo que facilita mucho
la vida. Para aquellos de ustedes que todavía consideran que inspeccionar los permisos en PostgreSQL
es algo engorroso, hay una solución adicional (https://www.cybertecpostgresql.com) que hemos
implementado recientemente en Cybertec: pg_permission. La idea es brindarle vistas simples para
inspeccionar el sistema de seguridad con mayor profundidad.
Para descargar pg_permission, visite nuestra página de repositorio de GitHub en https://github.com/cybertec
postgresql/pg_permission .
Puede clonar fácilmente el repositorio, como se muestra en la siguiente lista:
clon de git https://github.com/cybertecpostgresql/pg_permission. git
Clonación en 'pg_permission'... remoto: Enumeración
de objetos: 111, hecho. remoto: Total 111 (delta 0), reutilizado 0
(delta 0), paquetereutilizado 111
Machine Translated by Google
Inspección de permisos 343
Recepción de objetos: 100 % (111/111), 33,08 KiB | 292,00 KiB/s, listo.
Resolviendo deltas: 100% (52/52), hecho.
Una vez hecho esto, ingrese al directorio y simplemente ejecute make install. Esas dos cosas ya son
suficientes para implementar la extensión. Así es como se puede habilitar la extensión:
test=# CREAR EXTENSIÓN pg_permissions;
CREAR EXTENSIÓN
pg_permission desplegará un puñado de vistas, todas ellas estructuradas de forma idéntica. La siguiente vista
muestra cómo se ve la estructura:
prueba=# \d todos_los_permisos
Ver "public.all_permissions"
Columna | Tipo | Intercalación | Anulable | Por defecto
++++
| |
Disparadores:
permisos_trigger EN LUGAR DE ACTUALIZAR EN all_permissions
PARA CADA FILA EJECUTAR FUNCIÓN permisos_
trigger_func()
all_permissions es simplemente una vista que le proporciona una vista general de todos los permisos.
Simplemente puede filtrar por tipo de objeto, etc., para profundizar en los detalles. Lo que también es interesante
es que hay un activador en esta vista, por lo que si desea cambiar los permisos, simplemente puede ejecutar
ACTUALIZAR en todas esas vistas, mientras que pg_permissions cambiará los permisos en el sistema por
usted. En la siguiente sección, aprenderá a reasignar la propiedad.
Machine Translated by Google
344 Administrar la seguridad de PostgreSQL
Reasignación de objetos y eliminación de usuarios
Después de asignar permisos y restringir el acceso en varios niveles, puede suceder que los usuarios se
eliminen del sistema. Como era de esperar, los comandos para hacer esto son los comandos DROP
ROLE y DROP USER. Aquí está la sintaxis de DROP ROLE:
prueba=# \h DROP ROLE
Comando: DROP ROLE
Descripción: eliminar un rol de base de datos
Sintaxis:
DROP ROLE [SI EXISTE] nombre [, ...]
URL: https://www.postgresql.org/docs/14/sqldroprole.html
Una vez que se ha discutido la sintaxis de DROP ROLE, podemos intentarlo. La siguiente lista muestra cómo
funciona esto:
test=# DROP ROLE joe;
ERROR: el rol "joe" no se puede descartar porque algunos objetos dependen de él
DETALLE: destino de la política joe_pol_3 en la tabla t_persona destino de la política
joe_pol_2 en la tabla t_persona destino de la política joe_pol_1 en la tabla
t_persona privilegios para la tabla t_persona propietario de la tabla t_user
propietario de la secuencia t_user_id_seq
propietario de los privilegios
predeterminados en las nuevas relaciones que
pertenecen al rol joe en el esquema público
propietario de la tabla t_useful
PostgreSQL emitirá mensajes de error porque solo se puede eliminar a un usuario si se le ha
quitado todo. Esto tiene sentido por la siguiente razón: supongamos que alguien es dueño de una mesa.
¿Qué debería hacer PostgreSQL con esa tabla? Alguien tiene que poseerlo.
Para reasignar tablas de un usuario al siguiente, considere echar un vistazo a la cláusula REASSIGN:
prueba=# \h REASIGNAR
Comando: REASIGNAR PROPIEDAD
Descripción: cambie la propiedad de los objetos de la base de datos propiedad de un rol de base de datos
Sintaxis:
REASIGNAR PROPIEDAD DE { old_role | ROL_ACTUAL | USUARIO_ACTUAL |
Machine Translated by Google
Resumen 345
USUARIO_SESIÓN } [, ...]
A { nuevo_rol | ROL_ACTUAL | USUARIO_ACTUAL | USUARIO_SESIÓN }
URL: https://www.postgresql.org/docs/15/sqlreasignowned.html
La sintaxis es, nuevamente, bastante simple y ayuda a simplificar el proceso de traspaso. Aquí hay un ejemplo:
test=# REASIGNAR PROPIEDAD DE joe A postgres;
REASIGNAR PROPIEDAD
Entonces, intentemos dejar el papel de Joe nuevamente:
test=# DROP ROLE joe;
ERROR: el rol "joe" no se puede descartar porque algunos objetos dependen de él
DETALLE: destino de la política joe_pol_3 en la tabla t_person destino de la política joe_pol_2 en la tabla
t_person destino de la política joe_pol_1 en la tabla t_person
privilegios para la tabla t_person propietario de los privilegios predeterminados en las nuevas relaciones
que pertenecen al rol
joe en el esquema público
Como podemos ver, la lista de problemas se ha reducido significativamente. Lo que podemos hacer ahora es
resolver todos esos problemas uno tras otro, y luego abandonar el papel. No hay ningún atajo; la única forma de
hacer esto más eficiente es asegurarse de que se asignen la menor cantidad posible de permisos a personas reales
(usuarios con permisos de INICIO DE SESIÓN). Trate de abstraer todo lo que pueda en roles que, a su vez, pueden
ser utilizados por muchas personas. Si no se asignan permisos individuales a personas reales, las cosas tienden a
ser más fáciles en general.
Resumen
La seguridad de la base de datos es un campo amplio y un solo capítulo difícilmente puede cubrir todos los aspectos
de la seguridad de PostgreSQL. Muchas cosas, como SELinux y SECURITY DEFINER/INVOKER, quedaron intactas.
Sin embargo, en este capítulo, aprendimos sobre las cosas más comunes que enfrentaremos como desarrolladores
de PostgreSQL y administradores de bases de datos. También aprendimos cómo evitar las trampas básicas y cómo
hacer que nuestros sistemas sean más seguros. Lo que es importante comprender es que la seguridad sí importa y
que PostgreSQL le brinda todos los medios necesarios para proteger su base de datos.
En el Capítulo 9, Manejo de copias de seguridad y recuperación, aprenderemos sobre la replicación de secuencias de PostgreSQL y las
copias de seguridad incrementales. El capítulo también cubrirá escenarios de conmutación por error.
Machine Translated by Google
346 Administrar la seguridad de PostgreSQL
Preguntas
Aquí hay algunas preguntas para probar su conocimiento:
• ¿Cómo puede configurar el acceso de red a PostgreSQL?
• ¿Qué es un usuario y qué es un rol?
• ¿Cómo se puede cambiar una contraseña?
• ¿Qué es RLS?
Las respuestas a estas preguntas se pueden encontrar en el repositorio de GitHub (https://github.com/PacktPublishing/
MasteringPostgreSQL15 ).
Machine Translated by Google
9
Manejo de copia de seguridad y recuperación
En el Capítulo 8, Gestión de la seguridad de PostgreSQL, echamos un vistazo a todo lo que necesitamos saber sobre cómo
proteger PostgreSQL de la forma más sencilla y beneficiosa posible. Los temas que trataremos en este capítulo son la copia
de seguridad y la recuperación. Hacer copias de seguridad debería ser una tarea regular, y todos los administradores deberían
estar atentos a este ejercicio vital. Afortunadamente, PostgreSQL proporciona medios sencillos para crear copias de seguridad.
Por lo tanto, en este capítulo, cubriremos los siguientes temas:
• Realización de volcados simples • Manejo
de varios formatos
• Reproducción de copias de
seguridad • Manejo de datos globales
Al final de este capítulo, podrá configurar los mecanismos de copia de seguridad adecuados.
Realización de volcados simples
Las copias de seguridad y las exportaciones de datos son importantes porque sin copias de seguridad, está poniendo en riesgo su base de
datos en caso de fallas y problemas relacionados con el almacenamiento. Si está ejecutando una configuración de PostgreSQL, existen
básicamente dos métodos principales para realizar copias de seguridad:
• Volcados lógicos (extracción de un script SQL que representa sus datos)
• Envío de registros de transacciones
La idea detrás del envío del registro de transacciones es archivar los cambios binarios realizados en la base de datos. La mayoría de las
personas afirman que el envío de registros de transacciones es la única forma real de crear copias de seguridad. Sin embargo, en mi opinión,
esto no es necesariamente cierto.
Mucha gente confía en pg_dump para simplemente extraer una representación textual de los datos. Curiosamente,
pg_dump también es el método más antiguo para crear una copia de seguridad y existe desde los primeros días
del proyecto PostgreSQL (el envío de registros de transacciones se agregó mucho más tarde). Cada PostgreSQL
Machine Translated by Google
348 Manejo de copia de seguridad y recuperación
El administrador se familiariza con pg_dump tarde o temprano, por lo que es importante saber cómo
funciona realmente y qué hace.
Ejecutando pg_dump
En la primera parte de esta sección, aprenderá algunas cosas básicas sobre pg_dump. Lo primero que queremos
hacer es crear un volcado de texto simple como se muestra en el siguiente bloque de código:
[hs@linuxpc ~]$ pg_dump prueba > /tmp/dump.sql
Esta es la copia de seguridad más simple que puedas imaginar. Básicamente, pg_dump inicia sesión en la
instancia de la base de datos local, se conecta a una base de datos llamada prueba y comienza a extraer todos
los datos, que luego se enviarán a la salida estándar y se redirigirán al archivo. La belleza aquí es que la salida
estándar le brinda toda la flexibilidad de un sistema Unix. Puede comprimir fácilmente los datos usando una
tubería o hacer lo que quiera con ellos.
En algunos casos, es posible que desee ejecutar pg_dump como un usuario diferente. Todos los programas cliente de PostgreSQL
admiten un conjunto coherente de parámetros de línea de comandos para pasar la información del usuario. Si solo desea configurar
el usuario, use el indicador U, de la siguiente manera:
[hs@linuxpc ~]$ pg_dump U prueba_de_usuario_lo que sea_poderoso > /tmp/ dump.sql
El siguiente conjunto de parámetros se puede encontrar en todos los programas cliente de PostgreSQL:
... Opciones de conexión: d, dbname=DBNAME base de datos para volcar
h, host=HOSTNAME servidor de base de datos host directorio
o socket
p, port=número de puerto del servidor de la base de datos PORT
U, username=NOMBRE conectar como usuario de base de datos especificado w,
nopassword nunca solicitar contraseña
W, password forzar solicitud de contraseña (debe
suceder automáticamente)
role=NOMBRE DEL ROL hacer ESTABLECER ROL antes de volcar
...
Simplemente puede pasar la información que desea a pg_dump, y si tiene el nivel de permiso
adecuado, PostgreSQL obtendrá los datos. Lo importante aquí es ver cómo funciona realmente el programa.
Básicamente, pg_dump se conecta a la base de datos y abre una gran transacción de lectura repetible que
simplemente lee todos los datos. Recuerde, una lectura repetible garantiza que PostgreSQL cree una
instantánea coherente de los datos, que no cambia a lo largo de las transacciones. En otras palabras, un
volcado siempre es coherente: no se violarán claves foráneas. El resultado es una instantánea de los datos tal como eran cuando
Machine Translated by Google
Realización de volcados simples 349
comenzó el vertedero. La consistencia es un factor clave aquí. También implica que los cambios realizados en los
datos cuando se ejecuta el volcado ya no llegarán a la copia de seguridad.
Nota importante
Un volcado simplemente lee todo; por lo tanto, no hay permisos separados para poder volcar algo. Mientras
puedas leerlo, puedes hacer una copia de seguridad.
Además, tenga en cuenta que la copia de seguridad está en formato de texto de forma predeterminada. Esto significa que puede
extraer datos de forma segura , por ejemplo, de Solaris y moverlos a otra arquitectura de CPU. En el caso de copias binarias, esto
claramente no es posible, ya que el formato en disco depende de la arquitectura de su CPU.
Pasar contraseñas e información de conexión
Si observa de cerca los parámetros de conexión que se muestran en la sección anterior, notará que no hay forma
de pasar una contraseña a pg_dump. Puede hacer cumplir una solicitud de contraseña, pero no puede pasar el
parámetro a pg_dump usando una opción de línea de comandos.
La razón de esto es simplemente porque la contraseña puede aparecer en la tabla de procesos y ser visible para
otras personas. La pregunta ahora es: si pg_hba.conf, que está en el servidor, impone una contraseña, ¿ cómo
puede proporcionarla el programa cliente?
Hay varios medios para hacer esto. Aquí hay tres:
• Uso de variables de entorno
• Usando .pgpass
• Uso de archivos de servicio
En esta sección, aprenderemos acerca de los tres métodos.
Uso de variables de entorno
Una forma de pasar todo tipo de parámetros es usar variables de entorno. Si la información no se
pasa explícitamente a pg_dump, buscará la información faltante en las variables de entorno predefinidas.
Puede encontrar una lista de todas las configuraciones posibles en https://www.postgresql.org/docs/15/ static/libpq
envars.html.
La siguiente descripción general muestra algunas de las variables de entorno que comúnmente se necesitan para las copias de seguridad:
• PGHOST: Esto le dice al sistema a qué host conectarse
• PGPORT: Define el puerto TCP a utilizar
• PGUSER: Esto le dice a un programa cliente sobre el usuario deseado
Machine Translated by Google
350 Manejo de copia de seguridad y recuperación
• PGPASSWORD: Contiene la contraseña a utilizar
• PGDATABASE: Este es el nombre de la base de datos para conectarse
La ventaja de estos entornos es que la contraseña no aparecerá en la tabla de procesos. Sin embargo,
hay más.
Considere el siguiente ejemplo:
psql U... h ... p... d...
Dado que usted es un administrador del sistema, ¿realmente desearía escribir un código largo como este
un par de veces al día? Si está trabajando con el mismo host una y otra vez, simplemente configure esas
variables de entorno y conéctese con SQL simple. La siguiente lista muestra cómo conectarse a la base de
datos mediante el uso de variables de entorno para controlar el comportamiento deseado:
[hs@linuxpc ~]$ exportar PGHOST=localhost [hs@linuxpc ~]
$ exportar PGUSER=hs [hs@linuxpc ~]$ exportar
PGPASSWORD=abc [hs@linuxpc ~]$ exportar
PGPORT=5432
[hs@linuxpc ~]$ exportar PGDATABASE=prueba
[hs@linuxpc ~]$ psql psql (15.1)
Escriba "ayuda" para obtener ayuda.
Como puede ver, ya no hay parámetros de línea de comandos. Simplemente escriba psql y ya está.
Nota importante
Todas las aplicaciones basadas en la biblioteca de cliente de lenguaje C estándar de PostgreSQL (libpq)
comprenderán estas variables de entorno, por lo que puede usarlas no solo para psql y pg_dump, sino
también para muchas otras aplicaciones.
Haciendo uso de .pgpass
Una forma muy común de almacenar información de inicio de sesión es mediante el uso de archivos .pgpass. La idea
es simple: coloque un archivo llamado .pgpass en su directorio de inicio y coloque allí su información de inicio de
sesión. El formato es sencillo. La siguiente lista contiene el formato básico:
nombre de host: puerto: base de datos: nombre de usuario: contraseña
Un ejemplo sería el siguiente:
192.168.0.45:5432:mydb:xy:abc
Machine Translated by Google
Realización de volcados simples 351
PostgreSQL ofrece una buena funcionalidad adicional, en la que la mayoría de los campos pueden contener archivos *.
Aquí hay un ejemplo:
*:*:*:xy:abc
El carácter * implica que en cada host, en cada puerto, para cada base de datos, el usuario llamado xy usará
abc como contraseña. Para hacer que PostgreSQL use el archivo .pgpass, asegúrese de tener los permisos
de archivo correctos. Sin las siguientes líneas, las cosas no funcionarán correctamente:
chmod 0600 ~/.pgpass
chmod establece los permisos a nivel de archivo. Estos son necesarios para proteger los archivos.
Además, .pgpass también se puede usar en un sistema Windows. En ese caso, el archivo se puede
encontrar en la ruta %APPDATA%\postgresql\pgpass.conf.
Uso de archivos de servicio
Sin embargo, .pgpass no es el único archivo que puede usar. También puede hacer uso de los archivos de
servicio. Así es como funcionan: si desea conectarse a los mismos servidores una y otra vez, puede crear
un archivo .pg_service.conf. Contendrá toda la información de conexión que necesita.
Aquí hay un ejemplo de un archivo .pg_service.conf:
Mac:~ hs$ cat .pg_service.conf # un servicio de
muestra [hansservice]
anfitrión=localhost
puerto=5432
dbname=prueba
usuario = hs
contraseña=abc
[pabloservicio]
host=192.168.0.45
puerto=5432
nombre_bd=xyz
usuario=paul
contraseña=cde
Machine Translated by Google
352 Manejo de copia de seguridad y recuperación
Para conectarse a uno de los servicios, simplemente configure el entorno y conecte lo siguiente:
iMac:~ hs$ exportar PGSERVICE=hansservice
Ahora se puede establecer una conexión sin pasar parámetros a psql:
iMac:~ hs$ psql psql (15.1)
Escriba "ayuda" para obtener ayuda.
prueba=#
Como puede ver, el inicio de sesión funciona sin parámetros de línea de comandos adicionales. Alternativamente,
puede usar el siguiente comando:
servicio psql = hansservice
Ahora que hemos aprendido cómo pasar contraseñas e información de conexión, pasemos a aprender cómo extraer
subconjuntos de datos.
Extracción de subconjuntos de datos
Hasta ahora, hemos visto cómo volcar una base de datos completa. Sin embargo, esto puede no ser lo que queremos hacer.
En muchos casos, solo queremos extraer un subconjunto de tablas o esquemas. Afortunadamente, pg_dump puede ayudarnos
a hacer eso al mismo tiempo que proporciona varios cambios:
• a: Esto solo volca los datos y no volca la estructura de datos
• s: Esto volca la estructura de datos pero omite los datos
• n: Esto solo volca un determinado esquema
• N: Esto vuelca todo pero excluye ciertos esquemas
• t: Esto solo volca ciertas tablas
• T: Esto volca todo menos ciertas tablas (esto tiene sentido si desea excluir el registro
tablas, etc.)
Los volcados parciales pueden ser muy útiles para acelerar considerablemente las cosas. Ahora que hemos aprendido
cómo realizar volcados simples, aprendamos cómo manejar varios formatos de archivo.
Manejo de varios formatos.
Hasta ahora, hemos visto que pg_dump se puede usar para crear archivos de texto. El problema aquí es que un archivo de texto
solo puede reproducirse por completo, por lo que si hemos guardado una base de datos completa, solo podemos reproducir todo
Machine Translated by Google
Manejo de varios formatos. 353
cosa. En la mayoría de los casos, esto no es lo que queremos hacer. Por lo tanto, PostgreSQL tiene formatos
adicionales que ofrecen más funcionalidad.
En este punto, se admiten cuatro formatos:
F, format=c|d|t|p formato de archivo de salida (personalizado, directorio, tar, texto sin formato (predeterminado))
Ya hemos visto texto sin formato, que es solo texto normal. Además de eso, podemos usar un formato personalizado.
La idea detrás de un formato personalizado es obtener un volcado comprimido, incluida una tabla de contenido. Aquí hay dos
formas de crear un volcado de formato personalizado:
[hs@linuxpc ~]$ pg_dump Fc prueba > /tmp/dump.fc [hs@linuxpc ~]$ pg_dump Fc
prueba f /tmp/dump.fc
Además de la tabla de contenido, el volcado comprimido tiene una ventaja más: es mucho más pequeño.
La regla general es que un volcado de formato personalizado es aproximadamente un 90 % más pequeño que la instancia de la base de datos
de la que está a punto de realizar una copia de seguridad. Por supuesto, esto depende en gran medida de la cantidad de índices, pero para
muchas aplicaciones de bases de datos, esta estimación aproximada se mantendrá.
Una vez que se crea la copia de seguridad, podemos inspeccionar el archivo de copia de seguridad:
[hs@linuxpc ~]$ pg_restore list /tmp/dump.fc
;
; Archivo creado el 20221115 10:26:46 UTC
; nombre de la base de datos: prueba
; Entradas TOC: 25
; Compresión: 1 ; Versión de
volcado: 1.140
; Formato: PERSONALIZADO
; Entero: 4 bytes
; Desplazamiento: 8 bytes
; Volcado de la versión de la base de datos: 15.
; Volcado por pg_dump versión: 15.
;
;
; Entradas TOC seleccionadas:
;
3103; 1262 16384 BASE DE DATOS prueba hs
3; 2615 2200 ESQUEMA horario público
3104; 0 0 COMENTARIO ESQUEMA horas públicas
Machine Translated by Google
354 Manejo de copia de seguridad y recuperación
1; 3079 13350 EXTENSIÓN plpgsql
3105; 0 0 COMENTARIO EXTENSIÓN plpgsql
187; 1259 16391 TABLA público t_test hs
...
Tenga en cuenta que pg_restore list devolverá la tabla de contenido de la copia de seguridad.
Usar un formato personalizado es una buena idea, ya que la copia de seguridad se reducirá de tamaño. Sin embargo, hay
más: el comando Fd creará una copia de seguridad en formato de directorio. En lugar de un solo archivo, ahora obtendrá
un directorio que contiene un par de archivos:
[hs@linuxpc ~]$ mkdir /tmp/backup [hs@linuxpc ~]
$ pg_dump Fd test f /tmp/backup/ [hs@linuxpc ~]$ cd /tmp/backup/
[copia de seguridad hs@linuxpc]$ ls lh
total 86M
rwrwr. 1 h h 85 M 4 de enero 15:54 3095.dat.gz rwrwr. 1 h h 107 4 de
enero 15:54 3096.dat.gz rwrwr. 1 h h 740 K 4 de enero 15:54 3097.dat.gz
rwrwr. 1 h h 39 4 ene 15:54 3098.dat.gz
rwrwr. 1 h h 4.3K 4 de enero 15:54 toc.dat
Una ventaja del formato de directorio es que podemos usar más de un núcleo para realizar la copia de seguridad.
En el caso de un formato simple o personalizado, pg_dump solo utilizará un proceso. El formato del directorio
cambia esa regla. El siguiente ejemplo muestra cómo podemos decirle a pg_dump que use cuatro núcleos (o trabajos):
[copia de seguridad de hs@linuxpc]$ rm rf
* [copia de seguridad de hs@linuxpc]$ pg_dump Fd test f /tmp/backup/ j 4
En esta sección, aprendiste acerca de los volcados de texto básicos. En la siguiente sección, aprenderá sobre la reproducción de
copia de seguridad.
Nota importante
Cuantos más objetos haya en nuestra base de datos, mayor será la posibilidad de una posible aceleración.
Machine Translated by Google
Reproducción de copias de seguridad 355
Reproducción de copias de seguridad
Tener una copia de seguridad no tiene sentido a menos que haya intentado reproducirla. Afortunadamente, esto es fácil de hacer.
Si ha creado una copia de seguridad de texto sin formato, simplemente tome el archivo SQL y ejecútelo. El siguiente ejemplo
muestra cómo se puede hacer eso:
psql su_db < su_archivo.sql
Una copia de seguridad de texto sin formato es simplemente un archivo de texto que contiene todo. Siempre podemos simplemente reproducir un archivo de texto.
Si ha decidido un formato personalizado o un formato de directorio, puede usar pg_restore para reproducir la copia de
seguridad. Además, pg_restore le permite hacer todo tipo de cosas sofisticadas, como reproducir solo una parte de una base
de datos. Sin embargo, en la mayoría de los casos, simplemente reproducirá la base de datos completa. En este ejemplo,
crearemos una base de datos vacía y solo reproduciremos un volcado de formato personalizado:
[copia de seguridad de hs@linuxpc]$ createdb new_db [copia de
seguridad de hs@linuxpc]$ pg_restore d new_db j 4 /tmp/dump.fc
Tenga en cuenta que pg_restore agregará datos a una base de datos existente. Si su base de datos no está vacía,
pg_restore podría fallar pero continuar.
Nuevamente, j se usa para lanzar más de un proceso. En este ejemplo, se utilizan cuatro núcleos para reproducir los datos;
sin embargo, esto solo funciona cuando se está reproduciendo más de una mesa.
Nota importante Si
está utilizando un formato de directorio, simplemente puede pasar el nombre del directorio en lugar del archivo.
En cuanto al rendimiento, los volcados son una buena solución si se trabaja con cantidades de datos pequeñas o medianas.
Hay dos desventajas importantes:
• Obtendremos una instantánea, por lo que todo desde la última instantánea se perderá
• La reconstrucción de un volcado desde cero es comparativamente lenta en comparación con las copias binarias porque todos
de los índices tienen que ser reconstruidos
Echaremos un vistazo a las copias de seguridad binarias en el Capítulo 10, Dar sentido a las copias de seguridad y la
replicación. Reproducir copias de seguridad es fácil, pero hay más de lo que parece. La siguiente sección trata los datos globales.
¿Qué significa eso?
Machine Translated by Google
356 Manejo de copia de seguridad y recuperación
Manejo de datos globales
En las secciones anteriores, aprendimos sobre pg_dump y pg_restore, que son dos programas vitales cuando se
trata de crear copias de seguridad. La cuestión es que pg_dump crea volcados de base de datos: funciona a nivel de
base de datos. Si queremos hacer una copia de seguridad de una instancia completa, debemos usar pg_dumpall o
volcar todas las bases de datos por separado. Antes de profundizar en eso, tiene sentido ver cómo funciona pg_dumpall:
pg_dumpall > /tmp/all.sql
pg_dumpall se conectará a una base de datos tras otra y enviará cosas a la salida estándar, donde podrá procesarlas
con Unix. Tenga en cuenta que pg_dumpall se puede usar como pg_dump. Sin embargo, tiene algunas desventajas.
No es compatible con un formato de directorio o personalizado y, por lo tanto, no ofrece compatibilidad con varios
núcleos. Esto significa que nos quedaremos atascados con un hilo.
Sin embargo, hay más en pg_dumpall. Tenga en cuenta que los usuarios viven en el nivel de instancia. Si crea un
volcado de base de datos normal, obtendrá todos los permisos, pero no obtendrá todas las instrucciones CREATE
USER. los globales no se incluyen en un volcado normal; solo serán extraídos por pg_dumpall.
Si solo queremos globales, podemos ejecutar pg_dumpall usando la opción g:
pg_dumpall g > /tmp/globals.sql
En la mayoría de los casos, es posible que desee ejecutar pg_dumpall g junto con un volcado de formato de directorio o personalizado
para extraer sus instancias. Una secuencia de comandos de copia de seguridad simple podría verse así:
#!/bin/sh
BACKUP_DIR=/tmp/
pg_dumpall g > $BACKUP_DIR/globals.sql
para x en $(psql c "SELECCIONE nombre de datos DESDE pg_database
DONDE el nombre NO ESTÁ EN ('postgres', 'plantilla0', 'plantilla1')" postgres A t)
hacer
pg_dump Fc $x > $BACKUP_DIR/$x.fc hecho
Primero volcará los datos globales y luego recorrerá la lista de bases de datos para extraerlos uno por uno en un
formato personalizado.
Resumamos a continuación.
Machine Translated by Google
Resumen 357
Resumen
En este capítulo, aprendimos sobre la creación de copias de seguridad y volcados en general. Hasta ahora, las copias de
seguridad binarias no se han cubierto, pero ya puede extraer copias de seguridad textuales del servidor para que pueda
guardar y reproducir sus datos de la manera más simple posible. La protección de datos es importante y las copias de
seguridad son vitales para garantizar la seguridad de los datos. Recuerde, sin copias de seguridad corre el riesgo de pérdida total de datos.
En el Capítulo 10, Dar sentido a las copias de seguridad y la replicación, aprenderá sobre el envío de registros de transacciones, la
replicación de transmisión y las copias de seguridad binarias. También aprenderá a usar las herramientas integradas de PostgreSQL
para replicar instancias.
Preguntas
Centrémonos en algunas de las preguntas más importantes que la gente suele hacer:
• ¿ Todo el mundo debería crear vertederos? En realidad no. Los volcados se utilizan a menudo como copias de seguridad y, por lo
tanto, se consideran una tarea administrativa. Por lo tanto, se necesita un sistema de seguridad adecuado para garantizar que
no todos puedan leer todo.
• ¿ Por qué los vertederos son tan pequeños? Una copia binaria es mucho más grande porque contiene los datos "tal como están"
en el disco. En un vertedero, elementos como los índices son meras definiciones, que son, por supuesto, mucho más pequeñas.
• ¿ Tienes que volcar los globales también? Olvidarse de volcar los globales es a menudo un problema central.
Asegúrese de tener siempre una copia de esos globales.
• ¿ Es seguro tener un archivo .pgpass? Es seguro si está 100% seguro de que el directorio
que contienen archivos .pgpass solo pueden ser leídos por el usuario deseado.
Las respuestas a estas preguntas también se pueden encontrar en el repositorio de GitHub (https://github.com/PacktPublishing/
MasteringPostgreSQL15 ).
Machine Translated by Google
Machine Translated by Google
10
Dar sentido a las copias de
seguridad y la replicación
En el Capítulo 9, Manejo de copias de seguridad y recuperación, aprendimos mucho sobre copias de seguridad y recuperación,
que son esenciales para la administración. Hasta ahora, solo se han tratado las copias de seguridad lógicas; eso se solucionará
en este capítulo.
Este capítulo trata sobre el registro de transacciones de PostgreSQL y lo que podemos hacer con él para mejorar nuestra
configuración y hacer las cosas más seguras.
En este capítulo, cubriremos los siguientes temas:
• Comprensión del registro de transacciones •
Archivo y recuperación del registro de transacciones
• Configuración de la replicación asíncrona
• Actualización a replicación síncrona
• Hacer uso de las ranuras de replicación
• Hacer uso de los comandos CREAR PUBLICACIÓN y CREAR SUSCRIPCIÓN
• Configuración de un clúster HA con Patroni
Al final de este capítulo, podrá configurar el archivado y la replicación del registro de transacciones. Además de eso, podrá
comprender los conceptos básicos para manejar la alta disponibilidad y aplicarlos en un entorno del mundo real. Tenga en cuenta
que este capítulo nunca podría ser una guía completa para la replicación; es sólo una breve introducción. Existe mucha más
información, especialmente en el área de alta disponibilidad y en el campo completamente nuevo de PostgreSQL en Kubernetes/
OpenShift, que parece ser uno de los temas de bases de datos más relevantes en la actualidad.
Machine Translated by Google
360 Dar sentido a las copias de seguridad y la replicación
Comprender el registro de transacciones
Cada sistema de base de datos moderno proporciona funcionalidad para garantizar que un sistema pueda sobrevivir a un bloqueo si
algo sale mal o si alguien lo desconecta. Esto es cierto tanto para los sistemas de archivos como para los sistemas de bases de datos.
PostgreSQL también proporciona un medio para garantizar que un bloqueo no pueda dañar la integridad de los
datos o los datos mismos. Garantiza que si se corta la energía, el sistema siempre podrá volver a encenderse y
hacer su trabajo.
Los medios para proporcionar este tipo de seguridad se logran mediante WriteAhead Log (WAL) o xlog. La idea
es no escribir directamente en un archivo de datos, sino escribir primero en el registro. ¿Porque es esto importante?
Imagina que estamos escribiendo algunos datos, de la siguiente manera:
INSERTAR EN datos ... VALORES ('12345678');
Supongamos que estos datos se escribieron directamente en el archivo de datos. Si la operación falla a la mitad, el archivo de
datos se corromperá. Puede contener filas a medio escribir, columnas sin punteros de índice, información de confirmación
faltante, etc. Dado que el hardware realmente no garantiza escrituras atómicas de grandes cantidades de datos, se debe
encontrar una forma de hacerlo más sólido. Al escribir en el registro en lugar de escribir directamente en el archivo, este problema
se puede resolver.
Nota importante En
PostgreSQL, el registro de transacciones consta de registros que están encadenados. Cada registro representa un
"cambio" en una tabla, un índice, un archivo o cualquier otro componente relevante.
Una sola escritura puede constar de varios registros que tienen una suma de verificación y están conectados por el encabezado
WAL. Una sola transacción puede contener un árbol B, un índice, un administrador de almacenamiento, registros de confirmación
y mucho más. Cada tipo de objeto tiene sus propias entradas WAL para garantizar que el objeto pueda sobrevivir a un bloqueo.
Si hay un bloqueo, PostgreSQL se iniciará y reparará los archivos de datos según el registro de transacciones para garantizar
que no se produzcan daños permanentes.
Con esta introducción fuera del camino, obtengamos ahora una comprensión básica del registro de transacciones en general.
Mirando el registro de transacciones
En PostgreSQL, WAL generalmente se puede encontrar en el directorio pg_wal en el directorio de datos, a menos que se
especifique lo contrario en initdb. En versiones anteriores de PostgreSQL, el directorio WAL se llamaba pg_xlog, pero con la
introducción de PostgreSQL 10.0, se cambió el nombre del directorio.
La razón de esto es que, la mayoría de las veces, las personas eliminaban el contenido del directorio pg_xlog, lo que, por
supuesto, generaba problemas graves y una posible corrupción de la base de datos. Por lo tanto, la comunidad ha dado el paso
sin precedentes de cambiar el nombre de un directorio dentro de una instancia de PostgreSQL.
La esperanza es hacer que el nombre sea lo suficientemente aterrador como para que nadie se atreva a eliminar el contenido nuevamente.
Machine Translated by Google
Comprender el registro de transacciones 361
La siguiente lista muestra cómo se ve el directorio pg_wal:
[postgres@linux pg_wal]$ pwd /var/lib/pgsql/
15/data/pg_wal [postgres@linux pg_wal]$ ls l
totales 688132
rw. 1 postgres postgres 16777216 19 de enero 07:58 0000000100000000000000CD
rw. 1 postgres postgres 16777216 13 de enero 17:04 0000000100000000000000CE
rw. 1 postgres postgres 16777216 13 de enero 17:04 0000000100000000000000CF
rw. 1 postgres postgres 16777216 13 de enero 17:04
0000000100000000000000D0
rw. 1 postgres postgres 16777216 13 de enero 17:04 0000000100000000000000D1
rw. 1 postgres postgres 16777216 13 de enero 17:04 0000000100000000000000D2
Lo que podemos ver es que el registro de transacciones es un archivo de 16 MB que consta de 24 dígitos. La numeración
es hexadecimal. Como podemos ver, a CF le sigue D0. Los archivos son siempre de un tamaño fijo.
Nota importante Una
cosa a tener en cuenta es que, en PostgreSQL, la cantidad de archivos de registro de transacciones no está relacionada con
el tamaño de una transacción. Puede tener un conjunto muy pequeño de archivos de registro de transacciones y aun así
ejecutar fácilmente una transacción de varios terabytes (TB) .
Tradicionalmente, el directorio WAL normalmente consta de archivos de 16 MB. Sin embargo, desde la introducción de
PostgreSQL, el tamaño de un segmento WAL ahora se puede configurar con initdb. En algunos casos, esto puede
acelerar las cosas. Así es como funciona. El siguiente ejemplo nos muestra cómo se puede cambiar el tamaño del
archivo WAL a 32 MB:
initdb D /pgdata walsegsize=32
Comprender los puntos de control
Como mencioné anteriormente, cada cambio se escribe en WAL en formato binario (no contiene SQL).
El problema es este: el servidor de la base de datos no puede seguir escribiendo en WAL para siempre, ya que consumirá
más y más espacio con el tiempo. Entonces, en algún momento, el registro de transacciones debe reciclarse. Esto se hace
mediante un punto de control, que sucede automáticamente en segundo plano.
Machine Translated by Google
362 Dar sentido a las copias de seguridad y la replicación
La idea es que, cuando se escriben los datos, primero van al registro de transacciones y luego se coloca un búfer
sucio en búferes compartidos. Esos búferes sucios tienen que ir al disco y el escritor en segundo plano los escribe
en los archivos de datos o durante un punto de control. Tan pronto como se hayan escrito todos los búfer sucios
hasta ese momento, se puede eliminar el registro de transacciones.
Nota importante
Por favor, nunca borre manualmente los archivos de registro de transacciones. En el caso de un bloqueo, el
servidor de la base de datos no podrá volver a iniciarse y la cantidad de espacio en disco necesario se recuperará
de todos modos a medida que ingresen nuevas transacciones. Nunca toque el registro de transacciones
manualmente. PostgreSQL se encarga de las cosas por sí solo, y hacer las cosas allí manualmente es realmente dañino.
Optimización del registro de transacciones
Los puntos de control ocurren automáticamente y son activados por el servidor. Sin embargo, hay opciones de configuración
que deciden cuándo se inicia un punto de control. Los siguientes parámetros en postgresql. conf están a cargo de manejar
los puntos de control:
#checkpoint_timeout = 5min #max_wal_size = # rango 30s1d
1GB #min_wal_size = 80MB
Hay dos razones para iniciar un punto de control:
• Podríamos quedarnos sin tiempo o espacio
• El tiempo máximo entre dos puntos de control está definido por las variables checkpoint_timeout
La cantidad de espacio proporcionado para almacenar los registros de transacciones variará entre las variables
min_wal_size y max_wal_size. PostgreSQL activará automáticamente los puntos de control de tal manera que la cantidad
de espacio realmente necesaria estará entre esos dos números.
Nota importante La
variable max_wal_size es un límite suave y PostgreSQL puede (bajo una carga pesada) necesitar temporalmente
un poco más de espacio. En otras palabras, si nuestro registro de transacciones está en un disco separado, tiene
sentido asegurarse de que haya un poco más de espacio disponible para almacenar WAL. A menudo, la gente
pregunta cuánto WAL podría sobrepasar el límite. Esto es difícil de determinar. Sin embargo, hemos visto
directorios WAL sustancialmente más grandes.
¿Cómo puede ajustar el registro de transacciones en PostgreSQL 9.6 y 15.x? En 9.6, se realizaron algunos cambios
en el escritor de fondo y la maquinaria de puntos de control. En versiones anteriores, hubo algunos casos de uso en
los que las distancias de puntos de control más pequeñas tenían sentido desde el punto de vista del rendimiento. En 9.6 y
Machine Translated by Google
Archivo y recuperación de registros de transacciones 363
más allá, esto ha cambiado bastante, y las distancias de punto de control más amplias son básicamente siempre muy
favorables porque se pueden aplicar muchas optimizaciones en los niveles de base de datos y sistema operativo
para acelerar las cosas . La optimización más notable es que los bloques se ordenan antes de escribirlos, lo que
reduce en gran medida la E/S aleatoria en los discos mecánicos.
Pero hay más Las grandes distancias de los puntos de control en realidad disminuirán la cantidad de WAL creada. Sí, así es:
las distancias más grandes del punto de control conducirán a menos WAL.
La razón de esto es simple. Cada vez que se toca un bloque después de un punto de control por primera vez, debe enviarse a
WAL por completo. Si el bloque se cambia con más frecuencia, solo los cambios llegan al registro.
Las distancias más grandes básicamente provocan menos escrituras de página completa, lo que a su vez reduce la cantidad de
WAL creada en primer lugar. La diferencia puede ser bastante sustancial, como se puede ver en una de mis publicaciones de
blog en https://www.postgresqlsupport.com/checkpointdistanceandmountofwal/ .
PostgreSQL también nos permite configurar si los puntos de control deben ser cortos e intensos, o si deben distribuirse en un
período más largo. El valor predeterminado es 0.9, lo que significa que el punto de control debe realizarse de tal manera que el
proceso haya finalizado en un 90 % entre el punto de control actual y el siguiente. La siguiente lista muestra
checkpoint_completion_target:
#checkpoint_completion_target = 0.9
Aumentar este valor básicamente significa que el punto de control se extiende y es menos intensivo. En muchos casos,
un valor más alto ha demostrado ser beneficioso para aplanar los picos de E/S causados por puntos de control intensos.
El antiguo valor predeterminado solía ser 0,5. Sin embargo, un valor más alto es básicamente siempre mejor y, por lo tanto, se
ha cambiado el valor predeterminado.
Como ya hemos dicho en este capítulo, WAL es manejado por el servidor de la base de datos automáticamente. En la
configuración predeterminada, los archivos WAL antiguos se renombran y reciclan. Si bien esto es bueno para los sistemas
de archivos tradicionales, puede que no sea la mejor opción posible si utiliza un sistema de archivos de copia en escritura
(COW) como btrfs o similar.
Por lo tanto, se ha introducido un nuevo parámetro:
wal_recycle = encendido
Si está en un sistema de archivos tradicional, está bien; si de hecho está utilizando un sistema de archivos COW, considere dar
una oportunidad para obtener un mejor rendimiento.
Sin embargo, el rendimiento no es el único tema importante. Echemos también un vistazo al archivado y la recuperación de registros.
Archivo y recuperación de registros de transacciones
Después de nuestra breve introducción al registro de transacciones en general, es hora de centrarse en el proceso de
archivado del registro de transacciones. Como ya hemos visto, el registro de transacciones contiene una secuencia de binarios
Machine Translated by Google
364 Dar sentido a las copias de seguridad y la replicación
los cambios que se realizan en el sistema de almacenamiento. Entonces, ¿por qué no usarlo para replicar instancias de bases de
datos y hacer muchas otras cosas geniales, como archivar?
Configuración para archivar
Lo primero que queremos hacer en este capítulo es crear una configuración para realizar la recuperación de un punto
en el tiempo (PITR) estándar. Hay un par de ventajas de usar PITR sobre volcados ordinarios:
• Perderemos menos datos porque podemos restaurarlos a un cierto punto en el tiempo y no solo al punto de copia
de seguridad fijo.
• La restauración será más rápida porque los índices no tienen que crearse desde cero. son solo
copiados y están listos para usar.
La configuración de PITR es fácil. Solo se deben realizar algunos cambios en el archivo postgresql. conf archivo, como
se muestra aquí:
wal_level = versiones de réplica # solía ser "hot_standby" en versiones anteriores
max_wal_senders = 10 # al menos 2, mejor al menos 2
La variable wal_level dice que se supone que el servidor debe producir suficientes registros de transacciones para
permitir PITR. Si la variable wal_level se establece en mínimo (que es el valor predeterminado hasta PostgreSQL 9.6),
el registro de transacciones solo contendrá suficiente información para recuperar una configuración de un solo nodo; no
es lo suficientemente rico como para manejar la replicación. En PostgreSQL 10.0 y posteriores, el valor predeterminado
ya es correcto y ya no es necesario cambiar la mayoría de las configuraciones.
La variable max_wal_senders nos permitirá transmitir WAL desde el servidor. Nos permitirá usar pg_basebackup para
crear una copia de seguridad inicial en lugar de la copia tradicional basada en archivos. La ventaja aquí es que
pg_basebackup es mucho más fácil de usar. Nuevamente, el valor predeterminado en 10.0 se ha cambiado de manera
que, para el 90 % de todas las configuraciones, no se necesitan cambios.
La idea detrás de la transmisión WAL es que el registro de transacciones que se crea se copia en un lugar seguro para
su almacenamiento. Básicamente, hay dos medios para transportar WAL:
• Usando pg_receivewal (hasta 9.6, esto se conoce como pg_receivexlog)
• Usar el sistema de archivos como un medio para archivarlo
En esta sección, veremos cómo configurar la segunda opción. Durante las operaciones normales, PostgreSQL sigue
escribiendo en esos archivos WAL. Cuando tenemos archive_mode = activado en postgresql. conf, PostgreSQL llamará
a la variable archive_command para cada archivo.
Machine Translated by Google
Archivo y recuperación de registros de transacciones 365
Una configuración podría tener el siguiente aspecto. Primero, se puede crear un directorio que almacene esos archivos de registro de transacciones:
mkdir /archivo
archivo chown postgres.postgres
Las siguientes entradas se pueden cambiar en el archivo postgresql.conf:
archive_mode = en
archive_command = 'cp %p /archive/%f'
Un reinicio habilitará el archivado, pero primero configuremos el archivo pg_hba.conf para reducir el tiempo de
inactividad al mínimo absoluto.
Nota importante
Tenga en cuenta que podemos poner cualquier comando en la variable archive_command.
Mucha gente usa rsync, scp y otros para transportar sus archivos WAL a una ubicación segura. Si nuestro script
devuelve 0, PostgreSQL asumirá que el archivo ha sido archivado. Si se devuelve algo más, PostgreSQL intentará
archivar el archivo nuevamente. Esto es necesario porque el motor de la base de datos debe asegurarse de que no
se pierda ningún archivo. Para realizar el proceso de recuperación, tenemos que tener todos los archivos disponibles;
no se permite que se pierda un solo archivo. En el siguiente paso, ajustaremos la configuración en el archivo pg_hba.conf.
Uso de bibliotecas de archivo
La introducción de PostgreSQL 15 trajo consigo una nueva función que esperamos que se utilice mucho en el futuro: el
archivo de bibliotecas. Para integrar las herramientas de copia de seguridad de forma más estrecha en un sistema, es
más fácil ejecutarlas como una biblioteca que ejecutar un ejecutable por cada segmento de 16 MB que deba archivarse:
#archive_library = segmento '' # biblioteca a usar para archivar un archivo de registro
# (la cadena vacía indica
# archive_command debería
# ser usado)
La idea es realmente permitir que las personas que escriben software de respaldo se conviertan en parte del servidor. En el futuro,
esperamos que muchos proveedores de software de copia de seguridad hagan uso de esta función.
Configuración del archivo pg_hba.conf
Ahora que el archivo postgresql.conf se ha configurado correctamente, es necesario configurar el archivo
pg_hba.conf para la transmisión. Tenga en cuenta que esto solo es necesario si planeamos usar pg_basebackup,
que es una herramienta de última generación para crear copias de seguridad base.
Machine Translated by Google
366 Dar sentido a las copias de seguridad y la replicación
Básicamente, las opciones que tenemos en el archivo pg_hba.conf son las mismas que ya vimos en el
Capítulo 8, Administrar la seguridad de PostgreSQL. Solo hay un problema importante a tener en cuenta,
que se puede entender con la ayuda del siguiente código:
# Permitir conexiones de replicación desde localhost, por un usuario con la
# privilegio de replicación. replicación local
postgres replicación postgres 127.0.0.1/32 replicación de confianza
anfitrión confianza postgres ::1/128
anfitrión confianza
Podemos definir las reglas estándar del archivo pg_hba.conf. Lo importante es que la segunda columna dice replicación. Las
reglas normales no son suficientes: es muy importante agregar permisos de replicación explícitos. Además, tenga en cuenta que
no tenemos que hacer esto como superusuario. Podemos crear un usuario específico al que solo se le permite realizar el inicio de
sesión y la replicación.
Una vez más, PostgreSQL 10 y las versiones posteriores ya están configuradas de la forma que hemos descrito en
esta sección. La replicación local funciona cuando se deben agregar direcciones IP remotas listas para usar a pg_hba.conf.
Ahora que el archivo pg_hba.conf se ha configurado correctamente, se puede reiniciar PostgreSQL.
Creación de copias de seguridad básicas
Después de enseñarle a PostgreSQL cómo archivar esos archivos WAL, es hora de crear la primera copia de seguridad. La idea es
tener una copia de seguridad y reproducir los archivos WAL basados en ella para llegar a cualquier punto en el tiempo.
Para crear una copia de seguridad inicial, podemos recurrir a pg_basebackup, que es una herramienta de línea de comandos
utilizada para realizar copias de seguridad. Llamemos a pg_basebackup y veamos cómo funciona:
pg_basebackup D /algún_directorio_objetivo
h servidor local
punto de control=rápido
walmétodo=flujo
Como podemos ver, aquí usaremos cuatro parámetros:
• D: ¿Dónde queremos que viva la copia de seguridad base? PostgreSQL requiere un directorio vacío. Al final de la copia de
seguridad, veremos una copia del directorio de datos del servidor (el destino).
• h: Esto indica la dirección IP o el nombre del maestro (la fuente). Este es el servidor que usted
quiere hacer una copia de seguridad.
Machine Translated by Google
Archivo y recuperación de registros de transacciones 367
• checkpoint=fast: por lo general, pg_basebackup espera a que el maestro cree un punto de control.
La razón de esto es que el proceso de reproducción tiene que empezar en alguna parte. Un punto de control
garantiza que los datos se hayan escrito hasta cierto punto, por lo que PostgreSQL puede saltar allí de manera
segura e iniciar el proceso de reproducción. Básicamente, también se puede hacer sin el parámetro checkpoint=fast.
Sin embargo, puede pasar un tiempo antes de que pg_basebackup comience a copiar datos en este caso. Los
puntos de control pueden tener una diferencia de hasta 1 hora, lo que puede retrasar nuestras copias de seguridad innecesariamente.
• walmethod=stream: de forma predeterminada, pg_basebackup se conecta al servidor maestro y comienza a copiar
archivos. Ahora, tenga en cuenta que esos archivos se modifican mientras se copian.
Por lo tanto, los datos que llegan a la copia de seguridad son inconsistentes. Esta inconsistencia se puede reparar
durante el proceso de recuperación mediante WAL. Sin embargo, la copia de seguridad en sí no es coherente. Al
agregar el parámetro wal method=stream, es posible crear una copia de seguridad independiente; se puede
iniciar directamente sin reproducir el registro de transacciones. Este es un buen método si solo queremos clonar
una instancia y no usar PITR. Afortunadamente, walmethod=stream ya es el predeterminado en PostgreSQL
10.0 o superior. Sin embargo, en 9.6 o anterior, se recomienda usar el predecesor, denominado xlog
method=stream. En resumen, ya no es necesario configurar walmethod=stream explícitamente en PostgreSQL
15.0.
Echemos un vistazo a la gestión del ancho de banda ahora.
Reducir el ancho de banda de las copias de seguridad
Cuando se inicia pg_basebackup, intenta terminar su trabajo lo más rápido posible. Si tenemos una buena conexión de
red, pg_basebackup definitivamente puede obtener cientos de megabytes por segundo desde el servidor remoto. Si
nuestro servidor tiene un sistema de E/S débil, podría significar que pg_basebackup podría absorber todos los recursos
fácilmente y los usuarios finales podrían experimentar un mal rendimiento porque sus solicitudes de E/S son simplemente
demasiado lentas.
Para controlar la tasa de transferencia máxima, pg_basebackup ofrece lo siguiente:
r, tasamax=tasa
tasa de transferencia máxima para transferir el directorio de datos
(en kB/s, o utilice el sufijo "k" o "M")
Cuando creamos una copia de seguridad base, debemos asegurarnos de que el sistema de disco en el maestro realmente
pueda soportar la carga. Ajustar nuestra tasa de transferencia puede, por lo tanto, tener mucho sentido.
Machine Translated by Google
368 Dar sentido a las copias de seguridad y la replicación
Asignación de espacios de tabla
Por lo general, pg_basebackup se puede llamar directamente si estamos usando un diseño de sistema de archivos
idéntico en el sistema de destino. Si este no es el caso, pg_basebackup le permite asignar el diseño del sistema
de archivos maestro al diseño deseado. La opción T nos permite hacer los mapeos:
T, tablespacemapping=OLDDIR=NUEVODIR
reubicar el espacio de tabla en OLDDIR a NEWDIR
Nota importante
Si su sistema es pequeño, podría ser una buena idea mantener todo en un espacio de tabla. Los tablespaces
solo tienen sentido si pueden ayudarlo a agregar más discos físicos a un servidor. No tiene sentido cuando
está limitado por el ancho de banda de la red o si los datos en su SAN terminan en los mismos discos de
todos modos.
Esto es válido si la E/S no es el problema (tal vez porque solo administra unos pocos gigabytes de datos).
Uso de diferentes formatos
La herramienta de línea de comandos pg_basebackup puede crear varios formatos. Por defecto, pondrá los datos
en un directorio vacío. Esencialmente, se conectará al servidor de origen, creará un archivo .tar a través de una
conexión de red y colocará los datos en el directorio deseado.
El problema con este enfoque es que pg_basebackup creará muchos archivos, lo que no es adecuado si queremos
mover la copia de seguridad a una solución de copia de seguridad externa como Tivoli Storage Manager. La
siguiente lista muestra los formatos de salida válidos compatibles con pg_basebackup:
F, formato=p|t tar) formato de salida (simple (predeterminado),
Para crear un solo archivo, podemos usar la opción F=t. De forma predeterminada, creará un archivo llamado
base.tar, que luego se puede administrar más fácilmente. La desventaja, por supuesto, es que tenemos que volver
a inflar el archivo antes de realizar PITR.
Definición de objetivos de copia de seguridad
Con la introducción de PostgreSQL, ahora es posible definir un destino de copia de seguridad. De forma
predeterminada, PostgreSQL enviará la copia de seguridad al cliente. Se almacenará en la máquina que ejecuta
pg_ basebackup. Sin embargo, este podría no ser el comportamiento deseado:
t, objetivo=OBJETIVO[:DETALLE]
destino de la copia de seguridad (si no es el cliente)
Machine Translated by Google
Archivo y recuperación de registros de transacciones 369
Por el momento, hay tres objetivos de respaldo:
• cliente: Comportamientos estándar
• servidor: almacene la copia de seguridad en el servidor (por ejemplo, servidor:/alguna/ruta)
• backhole: envía la copia de seguridad a /dev/null
Esto agrega mucha flexibilidad al comando porque la nueva función desacopla el destino de la copia de seguridad de la propia
herramienta de copia de seguridad.
Seguimiento de pg_basebackup
Si está guardando una base de datos enorme, pg_basebackup puede tardar un poco. Tenga en cuenta el hecho de
que si está guardando una base de datos de 1 TB utilizando una conexión de red de 1 GB por segundo, todavía
estamos hablando de unos 20 minutos. Esto es mucho tiempo. Por lo tanto, puede ser útil realizar un seguimiento del
progreso de pg_basebackup y ver lo que realmente está haciendo. Afortunadamente, PostgreSQL proporciona una
vista del sistema que le brinda toda la información que necesitará para monitorear las cosas:
prueba=# \d pg_stat_progress_basebackup
Ver "pg_catalog.pg_stat_progress_basebackup"
Columna | Tipo | Intercalación | Anulable |
Por defecto
+++ +
tablespaces_streamed |grande
| tablespaces_total | |
grande | | |
Puede ver cuántos datos ya se han enviado, lo que da una buena indicación de cuándo podría terminar
realmente la copia de seguridad base.
Probar el archivado del registro de transacciones
Antes de sumergirnos en el proceso de reproducción real, tiene sentido verificar el archivo para asegurarse de que
funciona perfectamente y como se esperaba mediante un comando ls simple, como se muestra en el siguiente código:
[archivo hs@linux]$ ls l
totales 229384
rw 1 h personal 16777216 2 de octubre 12:38
0000000100000000000000007
Machine Translated by Google
370 Dar sentido a las copias de seguridad y la replicación
rw 1 hora personal 339 2 de octubre 12:38 00000001000000000000
0007.00000188.backup
rw 1 h personal 16777216 2 de octubre 12:38
000000010000000000000008
rw 1 h personal 16777216 2 de octubre 12:31
000000010000000000000009
rw 1 h personal 16777216 2 de octubre 12:31
00000001000000000000000A
rw 1 h personal 16777216 2 de octubre 12:31
00000001000000000000000B
rw 1 h personal 16777216 2 de octubre 12:38
00000001000000000000000C
rw 1 h personal 16777216 2 de octubre 12:38
00000001000000000000000D
drwx 4 h personal 136 2 de octubre 12:38 archive_status
...
Tan pronto como haya una actividad seria en una base de datos, los archivos WAL deben enviarse al archivo.
Además de solo buscar archivos, la siguiente vista puede ser útil:
test=# \d pg_stat_archiver Ver
"pg_catalog.pg_stat_archiver"
Columna | modificadores | Tipo de
++
| last_archived_time | | grande | archived_count last_archived_wal | texto
marca de tiempo con zona horaria | recuento_fallido | grande | | texto | last_failed_wal
last_failed_time | marca de tiempo con zona horaria | | marca de tiempo con zona
horaria |
stats_reset
La vista del sistema pg_stat_archiver es muy útil para averiguar si el archivado se ha detenido por
cualquier motivo y cuándo. Nos informará sobre la cantidad de archivos ya archivados
(archived_count ). También podemos ver cuál fue el archivo final y cuándo ocurrió el evento.
Finalmente, la vista del sistema pg_stat_ archiver puede decirnos cuándo el archivo ha fallado, lo cual es información
Desafortunadamente, el código o mensaje de error no se muestra en la tabla, pero dado que archive_command
puede ser un comando arbitrario, es fácil de registrar.
Machine Translated by Google
Archivo y recuperación de registros de transacciones 371
Hay una cosa más que ver en el archivo. Como describimos anteriormente, es importante verificar que los archivos
estén realmente archivados. Pero hay más Cuando se llama a la herramienta de línea de comandos pg_basebackup ,
veremos un archivo .backup en la secuencia de archivos WAL. Es pequeño y contiene solo cierta información sobre la
copia de seguridad base en sí; es puramente informativo y no es necesario para el proceso de reproducción. Sin
embargo, nos da algunas pistas vitales. Cuando comencemos a reproducir el registro de transacciones más adelante,
podemos eliminar todos los archivos WAL que sean más antiguos que el archivo .backup. En este caso, nuestro archivo
de respaldo se llama 000000010000000000000007.00000188.backup. Esto significa que el proceso de reproducción
comienza en algún lugar dentro del archivo ...0007 (en la posición ...188). También significa que podemos eliminar
todos los archivos anteriores a ...0007. Los archivos WAL más antiguos ya no serán necesarios para la recuperación.
Tenga en cuenta que podemos mantener más de una copia de seguridad, por lo que solo me refiero a la copia de seguridad actual.
Ahora que el archivo funciona, podemos centrar nuestra atención en el proceso de reproducción.
Reproducción del registro de transacciones
Resumamos el proceso hasta ahora. Hemos ajustado el archivo postgresql.conf (wal_level, max_wal_senders, archive_mode y
archive_command) y hemos permitido el comando pg_basebackup en el archivo pg_hba.conf. Luego, la base de datos se reinició
y se produjo correctamente una copia de seguridad base.
Tenga en cuenta que las copias de seguridad base solo pueden realizarse mientras la base de datos está completamente
operativa; solo se requiere un breve reinicio para cambiar las variables max_wal_senders y wal_level.
Ahora que el sistema funciona correctamente, es posible que nos enfrentemos a un bloqueo del que querremos recuperarnos.
Por lo tanto, podemos realizar PITR para restaurar la mayor cantidad de datos posible. Lo primero que tenemos que hacer es
tomar la copia de seguridad base y colocarla en la ubicación deseada.
Nota importante
Puede ser una buena idea guardar el clúster de la base de datos anterior. Incluso si está roto, nuestra compañía de
soporte de PostgreSQL podría necesitarlo para rastrear el motivo del bloqueo. Todavía puede eliminarlo más tarde, una
vez que tenga todo en funcionamiento nuevamente.
Dado el diseño del sistema de archivos anterior, podríamos querer hacer algo como lo siguiente:
cd /algún_dir_objetivo cp Rv * /datos
Suponemos que el nuevo servidor de base de datos estará ubicado en el directorio /data. Asegúrese de que el directorio esté
vacío antes de copiar la copia de seguridad base.
En PostgreSQL 14, algunas cosas han cambiado: en versiones anteriores, teníamos que configurar la recuperación. conf para
controlar el comportamiento de una réplica o PITR en general. Todos los ajustes de configuración para controlar esos
Machine Translated by Google
372 Dar sentido a las copias de seguridad y la replicación
las cosas se han movido al archivo de configuración principal, postgresql.conf. Si está ejecutando configuraciones
antiguas, es hora de cambiar a las nuevas interfaces para asegurarse de que su automatización no se rompa.
Entonces, veamos cómo configurar el proceso de reproducción. Intente poner restore_command y
recovery_ target_time en postgresql.conf:
restore_command = 'cp /archive/%f %p'
recovery_target_time = '20221002 12:42:00'
Después de arreglar el archivo postgresql.conf, simplemente podemos iniciar nuestro servidor. La salida podría tener el
siguiente aspecto:
esperando que se inicie el servidor...
20221214 09:53:16.688 CET [60000] REGISTRO: iniciando PostgreSQL 15.1 en aarch64
appledarwin21.6.0, compilado por Apple clang versión 13.1.6 (clang1316.0.21.2.5), 64
bits
20221214 09:53:16.690 CET [60000] REGISTRO: escucha en la dirección IPv6 "::1",
puerto 7000
20221214 09:53:16.690 CET [60000] REGISTRO: escucha en la dirección IPv4
"127.0.0.1", puerto 7000
20221214 09:53:16.691 CET [60000] REGISTRO: escucha en el socket de Unix "/
tmp/.s.PGSQL.7000"
20221214 09:53:16.694 CET [60003] REGISTRO: se interrumpió el sistema de base de
datos; último conocido en 20221214 09:52:24 CET 20221214 09:53:16.721
CET [60003] REGISTRO: rehacer comienza en 0/2000028
20221214 09:53:16.721 CET [60003] REGISTRO: estado de recuperación consistente
alcanzado en 0/2000100
20221214 09:53:16.721 CET [60003] REGISTRO: rehacer hecho en 0/2000100
uso del sistema: CPU: usuario: 0,00 s, sistema: 0,00 s, transcurrido: 0,00 s
20221214 09:53:16.728 CET [60001] REGISTRO: inicio del punto de control: espera
inmediata al final de la recuperación
20221214 09:53:16.730 CET [60001] REGISTRO: punto de control completo: escribió 3
búferes (0,0 %); 0 archivos WAL agregados, 0 eliminados, 1 reciclado; escritura=0,001
s, sincronización=0,001 s, total=0,002 s; sincronizar archivos=2, más largo=0,001 s,
promedio=0,001 s; distancia=16384 kB, estimación=16384 kB
20221214 09:53:16.730 CET [60000] REGISTRO: el sistema de base de datos está listo
para aceptar conexiones
Machine Translated by Google
Archivo y recuperación de registros de transacciones 373
Cuando se inicia el servidor, hay un par de mensajes que debe buscar para asegurarse de que nuestra
recuperación funcione perfectamente. el estado de recuperación consistente alcanzado es el más importante a buscar.
Una vez que haya llegado a este punto, puede estar seguro de que su base de datos es consistente y no está dañada.
Dependiendo de la marca de tiempo que haya elegido, es posible que haya perdido algunas transacciones
al final (si lo desea), pero en general, su base de datos será consistente (no hay infracciones clave, etc.).
Si ha utilizado una marca de tiempo que indica un punto en el futuro, PostgreSQL se quejará de que no pudo
encontrar el siguiente archivo WAL y finalizará el proceso de reproducción. Si está utilizando una marca de tiempo
entre el final de la copia de seguridad base y algún punto antes del bloqueo, por supuesto, no verá este tipo de mensaje.
Una vez finalizado el proceso, el servidor se iniciará correctamente.
Encontrar la marca de tiempo correcta
Hasta ahora, hemos avanzado bajo el supuesto de que conocemos la marca de tiempo que queremos recuperar, o que
simplemente queremos reproducir un registro de transacciones completo para reducir la pérdida de datos. Sin embargo,
¿qué pasa si no queremos reproducirlo todo? ¿Qué pasa si no sabemos a qué punto en el tiempo recuperarnos? En la
vida cotidiana, este es en realidad un escenario muy común. Uno de nuestros desarrolladores podría perder algunos
datos por la mañana y se supone que debemos hacer las cosas bien nuevamente. El problema es este: ¿a qué hora de la mañana?
Una vez finalizada la recuperación, no se puede reiniciar fácilmente. Una vez que se complete la recuperación, se
promoverá el sistema y, una vez que se haya promovido, no podremos continuar reproduciendo WAL.
Sin embargo, lo que podemos hacer es pausar la recuperación sin promoción, verificar qué hay dentro de la base de
datos y continuar.
Hacer eso es fácil. Lo primero que debemos asegurarnos es que la variable hot_standby esté activada en el archivo
postgresql.conf. Esto asegurará que la base de datos sea legible mientras aún está en modo de recuperación. Luego,
configure la siguiente variable en postgresql.conf:
recovery_target_action = 'pausa'
Hay varias configuraciones de recovery_target_action. Si usamos pausa, PostgreSQL se detendrá en el momento
deseado y nos permitirá verificar lo que ya se ha reproducido. Podemos ajustar el tiempo que queramos, reiniciar e
intentarlo de nuevo. Alternativamente, podemos establecer el valor para promover o cerrar.
También hay una segunda forma de pausar la reproducción del registro de transacciones. Básicamente, también se puede hacer
al realizar PITR. Sin embargo, en la mayoría de los casos, se realiza con replicación de transmisión. Esto es lo que se puede hacer
durante la reproducción de WAL:
prueba=# \x
La pantalla ampliada está activada.
prueba=# \df *pausa*
Lista de funciones
Machine Translated by Google
374 Dar sentido a las copias de seguridad y la replicación
[ REGISTRO 1 ]+
Esquema | pg_catálogo |
Nombre pg_get_wal_replay_pause_state | texto
Tipo de datos de resultado
Tipos de datos de argumentos | |
Tipo función
[ REGISTRO 2 ]+
Esquema | pg_catálogo |
Nombre pg_is_wal_replay_paused | booleano
Tipo de datos de resultado
Tipos de datos de argumentos | |
Tipo función
[ REGISTRO 3 ]+
Esquema | pg_catálogo |
Nombre pg_wal_replay_pause | vacío
Tipo de datos de resultado
Tipos de datos de argumentos | |
Tipo función
Podemos llamar a SELECT pg_wal_replay_pause(); comando para detener la reproducción de WAL hasta que
llamemos a SELECT pg_wal_replay_resume(); dominio.
La idea es averiguar cuánto WAL ya se ha reproducido y continuar según sea necesario.
Sin embargo, tenga esto en cuenta: una vez que se ha promocionado un servidor, no podemos continuar
reproduciendo WAL sin más precauciones.
Se ha agregado una función más en PostgreSQL 14: pg_get_wal_replay_pause_state.
Le permite regresar ya sea que esté haciendo una pausa o no. Tenga en cuenta que solo se puede llamar a la función si
se encuentra en modo de reproducción. De lo contrario, se producirá un error, como se muestra aquí:
prueba=# SELECCIONA pg_get_wal_replay_pause_state();
ERROR: la recuperación no está en curso
SUGERENCIA: Las funciones de control de recuperación solo se pueden ejecutar durante
recuperación.
La función puede devolver tres valores de texto diferentes:
• no pausado
• pausa solicitada
• en pausa
Machine Translated by Google
Archivo y recuperación de registros de transacciones 375
Como ya hemos visto, puede ser bastante complicado determinar qué tan atrás necesitamos recuperarnos. Por lo tanto,
PostgreSQL nos proporciona algo de ayuda. Considere el siguiente ejemplo del mundo real: a medianoche, ejecutamos
un proceso nocturno que termina en un punto que generalmente se desconoce. El objetivo es recuperar exactamente
hasta el punto final del proceso nocturno. El problema es este: ¿cómo sabemos cuándo ha terminado el proceso? En la
mayoría de los casos, esto es difícil de entender. Entonces, ¿por qué no agregar un marcador al registro de transacciones?
El código para esto es el siguiente:
postgres=# SELECCIONE pg_create_restore_point('mi_proceso_diario_finalizó');
pg_create_restore_point
1F/E574A7B8
(1 fila)
Si nuestro proceso llama a esta instrucción SQL tan pronto como finaliza, será posible usar esta etiqueta en el
registro de transacciones para recuperar exactamente un punto en el tiempo agregando la siguiente directiva al
archivo postgresql.conf:
recovery_target_name = 'mi_proceso_diario_terminado'
Al usar esta configuración en lugar de recovery_target_time, el proceso de reproducción nos transmitirá
exactamente hasta el final del proceso nocturno.
Por supuesto, también podemos reproducir hasta un determinado ID de transacción. Sin embargo, en la vida real, esto ha
resultado ser difícil, ya que un administrador rara vez conoce la identificación exacta de la transacción y, por lo tanto, no
tiene mucho valor práctico. Tenga en cuenta que la configuración de marcadores debe realizarse antes de la recuperación.
Eso es importante.
Limpieza del archivo de registro de transacciones
Hasta ahora, los datos se escriben en el archivo todo el tiempo y no se ha prestado atención a limpiar el archivo
nuevamente para liberar espacio en el sistema de archivos. PostgreSQL no puede hacer este trabajo por nosotros porque
no tiene idea de si queremos usar el archivo nuevamente. Por lo tanto, estamos a cargo de limpiar el registro de
transacciones. Por supuesto, también podemos usar una herramienta de copia de seguridad; sin embargo, es importante
saber que PostgreSQL no tiene ninguna posibilidad de hacer la limpieza por nosotros.
Supongamos que queremos limpiar un registro de transacciones antiguo que ya no se necesita. Tal vez queramos
mantener varias copias de seguridad base y limpiar todos los registros de transacciones que ya no serán necesarios para
restaurar una de esas copias de seguridad.
En este caso, la herramienta de línea de comandos pg_archivecleanup es exactamente lo que necesitamos. Simplemente
podemos pasar el directorio del archivo y el nombre del archivo de copia de seguridad al comando pg_archivecleanup,
Machine Translated by Google
376 Dar sentido a las copias de seguridad y la replicación
y se asegurará de que los archivos se eliminen del disco. El uso de esta herramienta nos facilita la vida porque no
tenemos que averiguar por nuestra cuenta qué archivos de registro de transacciones conservar. Así es como funciona:
pg_archivecleanup elimina los archivos WAL más antiguos de los archivos de PostgreSQL.
Uso:
pg_archivecleanup [OPCIÓN]... UBICACIÓN DE ARCHIVO
ARCHIVO MÁS ANTIGUO
Opciones:
d generar salida de depuración (modo detallado) ejecución en seco,
norte mostrar los nombres de los archivos que
sería eliminado
V, version información de versión de salida, luego salir
x EXT limpiar archivos si tienen esta extensión mostrar esta ayuda, luego salir
?, ayuda
Para usar como archive_cleanup_command en postgresql.conf:
archive_cleanup_command = 'pg_archivecleanup [OPCIÓN]...
UBICACIÓN DE ARCHIVO %r'
p.ej
archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %r'
O para usar como un limpiador de archivos independiente:
por
ejemplo, pg_archivecleanup /mnt/server/archiverdir 0000000100000000000
00010.00000020.copia de seguridad
Esta herramienta se puede utilizar con facilidad y está disponible en todas las plataformas.
Ahora que hemos echado un vistazo al archivado de registros de transacciones y PITR, podemos centrar nuestra atención en
una de las características más utilizadas en el mundo de PostgreSQL hoy en día: la replicación de transmisión.
Configuración de la replicación asíncrona
La idea detrás de la replicación de transmisión es simple. Después de una copia de seguridad base inicial, la copia de
seguridad secundaria puede conectarse al maestro, obtener un registro de transacciones en tiempo real y aplicarlo. La
reproducción del registro de transacciones ya no es una sola operación, sino un proceso continuo que se supone que debe
seguir ejecutándose mientras exista un clúster.
Machine Translated by Google
Configuración de la replicación asíncrona 377
Realización de una configuración básica
En esta sección, aprenderemos cómo configurar la replicación asincrónica de forma rápida y sencilla. El objetivo
es configurar un sistema que consta de dos nodos.
Básicamente, la mayor parte del trabajo ya se ha realizado para el archivo WAL. Sin embargo, para que sea más fácil
de entender, veremos todo el proceso de configuración de la transmisión porque no podemos suponer que el envío
WAL realmente ya está configurado según sea necesario.
Lo primero que debe hacer es ir al archivo postgresql.conf y ajustar los siguientes parámetros:
wal_level = réplica
max_wal_senders = 10 # o cualquier valor >= 2
# este ya es el valor por defecto
# en versiones más recientes
hot_standby = encendido # ya en una configuración predeterminada
Nota importante
A partir de PostgreSQL 10.0, algunos de estos parámetros ya son las opciones predeterminadas.
Tal como lo hicimos anteriormente, la variable wal_level debe ajustarse para garantizar que PostgreSQL produzca
suficientes registros de transacciones para mantener un esclavo. Luego, tenemos que configurar la variable
max_wal_senders. Cuando un esclavo está en funcionamiento o cuando se crea una copia de seguridad base, un proceso
emisor de WAL se comunicará con un proceso receptor de WAL en el lado del cliente. La configuración max_wal_senders
permite que PostgreSQL cree suficientes procesos para servir a esos clientes.
Nota importante
En teoría, es suficiente tener un solo proceso de remitente WAL. Sin embargo, es bastante inconveniente.
Una copia de seguridad base que usa el parámetro walmethod=stream ya necesitará dos procesos de
remitente WAL. Si desea ejecutar un esclavo y realizar una copia de seguridad base al mismo tiempo,
ya hay tres procesos en uso. Por lo tanto, asegúrese de permitir que PostgreSQL cree suficientes
procesos para evitar reinicios sin sentido.
Luego, está la variable hot_standby. Básicamente, un maestro ignora la variable hot_standby y no la toma en
consideración. Todo lo que hace es hacer que el esclavo sea legible durante la reproducción de WAL. Entonces,
¿ por qué nos importa? Tenga en cuenta que el comando pg_basebackup clonará todo el servidor, incluida su
configuración. Esto significa que si ya hemos establecido el valor en el maestro, los esclavos lo recibirán
automáticamente cuando se clone el directorio de datos.
Machine Translated by Google
378 Dar sentido a las copias de seguridad y la replicación
Después de configurar el archivo postgresql.conf, podemos centrar nuestra atención en el archivo pg_hba.conf ;
solo permita que el esclavo realice la replicación agregando reglas. Básicamente, esas reglas son las mismas
que vimos para PITR.
Luego, reinicie el servidor de la base de datos, tal como lo hizo con PITR.
Ahora, se puede llamar al comando pg_basebackup en el esclavo. Antes de hacerlo, asegúrese
de que el directorio /target esté vacío. Si está utilizando paquetes RPM, asegúrese de cerrar
una instancia potencialmente en ejecución y vaciar el directorio (por ejemplo, /var/lib/pgsql/data).
El siguiente código muestra cómo se puede usar pg_basebackup:
pg_basebackup D /objetivo
h principal.ejemplo.com
punto de control=rápido
walmétodo=flujo R
Simplemente reemplace el directorio /target con el directorio de destino deseado y reemplace master.
example.com con la IP o nombre DNS de tu maestro. El parámetro checkpoint=fast activará un punto de
control instantáneo. Luego, está el parámetro walmethod=stream; abrirá dos flujos. Uno copiará los datos,
mientras que el otro obtendrá los datos WAL creados mientras se ejecuta la copia de seguridad.
Finalmente, está el indicador R:
R, writerecoveryconf replicación # escribir configuración para
La bandera R es una muy buena característica. El comando pg_basebackup puede crear automáticamente
la configuración del esclavo. En versiones anteriores, agregará varias entradas al archivo recovery.conf. En
PostgreSQL 12 y superior, realizará los cambios automáticamente en postgresql.conf:
standby_mode = en primario_conninfo = '...'
La primera configuración dice que PostgreSQL debe seguir reproduciendo WAL todo el tiempo: si se ha reproducido
todo el registro de transacciones, debe esperar a que llegue un nuevo directorio WAL. La segunda configuración le
dirá a PostgreSQL dónde está el maestro. Es una conexión de base de datos normal.
Los esclavos también pueden conectarse a otros esclavos para transmitir registros de transacciones. Es posible replicar en cascada
simplemente creando copias de seguridad base desde un esclavo. Entonces, maestro realmente significa servidor de origen en este contexto.
Machine Translated by Google
Configuración de la replicación asíncrona 379
Después de ejecutar el comando pg_basebackup, se pueden iniciar los servicios. Lo primero que
debemos verificar es si el maestro muestra el proceso del remitente wal:
[hs@zenbook ~]$ ps hacha | grep remitente 17873? Ss 0:00
postgres: proceso de remitente wal
ah ::1(57596) transmisión 1F/E9000060
Si lo hace, el esclavo también mostrará el proceso del receptor wal:
17872 ? Ss 0:00 postgres: proceso del receptor wal
transmisión 1F/E9000060
Si esos procesos están ahí, ya estamos en el camino correcto y la replicación está funcionando como se esperaba.
Ambos lados ahora están hablando entre sí, y WAL fluye del maestro al esclavo.
Mejorando la seguridad
Hasta ahora, hemos visto que los datos se transmiten como superusuario. Sin embargo, no es una buena idea permitir
el acceso de superusuario desde un sitio remoto. Afortunadamente, PostgreSQL nos permite crear un usuario al que
solo se le permite consumir el flujo de registro de transacciones y nada más.
Crear un usuario solo para transmisión es fácil. Así es como funciona:
test=# CREAR USUARIO repl REPLICACIÓN DE INICIO DE SESIÓN;
CREAR ROL
Al asignar la replicación al usuario, es posible usarla solo para transmisión; todo lo demás está prohibido.
Se recomienda encarecidamente no utilizar su cuenta de superusuario para configurar la transmisión. Simplemente
cambie el archivo de configuración al usuario recién creado. No exponer las cuentas de superusuario mejorará
drásticamente la seguridad, al igual que dar una contraseña al usuario de replicación.
Detención y reanudación de la replicación
Una vez que se ha configurado la replicación de transmisión, funciona perfectamente sin demasiada intervención del
administrador. Sin embargo, en algunos casos, podría tener sentido detener la replicación y reanudarla en un
momento posterior. Pero, ¿por qué alguien querría hacer eso?
Considere el siguiente caso de uso: usted está a cargo de una configuración maestro/esclavo, que ejecuta un
sistema de administración de contenido (CMS) inepto o algún software de foro dudoso. Supongamos que
desea actualizar su aplicación del terrible CMS 1.0 al terrible CMS 2.0. Se ejecutarán algunos cambios en su
base de datos, que se replicarán instantáneamente en la base de datos esclava. ¿Qué pasa si el proceso de
actualización hace algo mal? El error se replicará instantáneamente en ambos nodos debido a la transmisión.
Machine Translated by Google
380 Dar sentido a las copias de seguridad y la replicación
Para evitar la replicación instantánea, podemos detener la replicación y reanudarla según sea necesario. En el caso de nuestra
actualización de CMS, simplemente podríamos hacer lo siguiente:
1. Detener la replicación.
2. Realice la actualización de la aplicación en el maestro.
3. Comprobar que nuestra aplicación sigue funcionando. En caso afirmativo, reanudamos la replicación. Si no, fallamos al
réplica, que todavía tiene los datos antiguos.
Con este mecanismo, podemos proteger nuestros datos porque podemos volver a los datos como estaban antes del problema.
Más adelante en este capítulo, aprenderemos cómo promocionar un esclavo para que se convierta en el nuevo servidor maestro.
La pregunta principal ahora es esta: ¿cómo podemos detener la replicación? Así es como funciona. Ejecute la siguiente línea
en el standby:
prueba=# SELECCIONA pg_wal_replay_pause();
Esta línea detendrá la replicación. Tenga en cuenta que el registro de transacciones seguirá fluyendo del maestro al esclavo ;
solo se detiene el proceso de reproducción. Sus datos aún están protegidos, ya que se conservan en el esclavo. En el caso
de un bloqueo del servidor, no se perderán datos.
Tenga en cuenta que el proceso de reproducción debe detenerse en el esclavo. De lo contrario, PostgreSQL
arrojará un error :
ERROR: la recuperación no está en curso
SUGERENCIA: Las funciones de control de recuperación solo se pueden ejecutar durante
recuperación.
Una vez que se reanude la replicación, se necesitará la siguiente línea en el esclavo:
SELECCIONE pg_wal_replay_resume();
PostgreSQL comenzará a reproducir WAL nuevamente.
Comprobación de la replicación para garantizar la disponibilidad
Uno de los trabajos principales de cada administrador es garantizar que la replicación se mantenga en funcionamiento en
todo momento. Si la replicación no funciona, es posible que los datos se pierdan si el maestro falla. Por lo tanto, vigilar la
replicación es absolutamente necesario.
Afortunadamente, PostgreSQL proporciona vistas del sistema que nos permiten analizar en profundidad lo que está sucediendo.
Una de esas vistas es pg_stat_replication:
prueba=# \d pg_stat_replication
Ver "pg_catalog.pg_stat_replication"
Machine Translated by Google
Configuración de la replicación asíncrona 381
Columna | Tipo ...
+...
pid | entero | oid |
usesysid nombre
usa el nombre
nombre_aplicación | texto | inet client_addr
client_hostname |
texto | puerto_cliente entero | marca de
zona horaria tiempo con
pg_lsn | pg_lsn
backend_start
| backend_xmin | xido | texto | pg_lsn | pg_lsn |
intervalo | intervalo | intervalo | entero |
estado texto |
sent_lsn marca de
write_lsn tiempo con
flush_lsn zona horaria
replay_lsn
write_lag
flush_lag
replay_lag
sync_priority
sincronización_estado
tiempo_respuesta
La vista pg_stat_replication contendrá información sobre el remitente. No quiero usar la palabra maestro
aquí porque los esclavos se pueden conectar a algún otro esclavo. Es posible construir un árbol de
servidores. En el caso de un árbol de servidores, el maestro solo tendrá información de los esclavos a los
que está conectado directamente.
Lo primero que veremos en esta vista es el ID del proceso del remitente WAL. Nos ayuda a identificar el
proceso si algo sale mal. Usualmente este no es el caso. Luego, veremos el nombre de usuario utilizado
por el esclavo para conectarse a su servidor de envío. Los campos client_* indicarán dónde están los esclavos.
Podremos extraer información de red de esos campos. El campo backend_start muestra cuándo los
esclavos comenzaron a transmitir desde nuestro servidor.
Luego, está el campo mágico backend_xmin. Suponga que está ejecutando una configuración maestro/esclavo.
Es posible decirle al esclavo que informe su ID de transacción al maestro. La idea detrás de esto es retrasar la
limpieza en el maestro para que los datos no se tomen de una transacción que se ejecuta en el esclavo.
El campo de estado nos informa sobre el estado del servidor. Si nuestro sistema está bien, el campo contendrá
transmisión. De lo contrario, se necesita una inspección más cercana.
Machine Translated by Google
382 Dar sentido a las copias de seguridad y la replicación
Los siguientes cuatro campos son realmente importantes. El campo sent_lsn, anteriormente el campo sent_location,
indica cuánto WAL ya ha llegado al otro lado, lo que implica que los campos han sido aceptados por el receptor de
WAL. Podemos usarlo para averiguar cuántos datos ya han llegado al esclavo. Luego, está el campo write_lsn,
anteriormente el campo write_location. Una vez que se ha aceptado WAL , se pasa al sistema operativo. El campo
write_lsn nos dirá que la posición WAL ya ha llegado al sistema operativo de forma segura. El campo flush_lsn,
anteriormente el campo flush_location, sabrá cuánto WAL la base de datos ya ha vaciado en el disco.
Finalmente, está replay_lsn, anteriormente el campo de ubicación de reproducción. El hecho de que WAL haya llegado
al disco en modo de espera no significa que PostgreSQL ya se haya reproducido o se haya hecho visible para el usuario
final. Supongamos que la replicación está en pausa. Los datos seguirán fluyendo al disco de reserva. Sin embargo, se
aplicará más adelante. El campo replay_lsn nos dirá cuántos datos ya están visibles.
En PostgreSQL 10.0, se agregaron más campos a pg_stat_replication; los campos *_lag indican el retraso del esclavo
y ofrecen una forma conveniente de ver qué tan atrás está un esclavo.
Nota importante
Los campos están a diferentes intervalos para que podamos ver la diferencia horaria con mayor claridad.
Finalmente, PostgreSQL nos dice si la replicación es síncrona o asíncrona.
Si todavía estamos en PostgreSQL 9.6, puede que nos resulte útil calcular la diferencia entre los servidores de envío y
recepción en bytes. Los campos *_lag todavía no hacen esto para 9.6, por lo que tener la diferencia en bytes puede ser
muy beneficioso. Así es como funciona:
SELECCIONE client_addr, pg_current_wal_location() sent_location
como diferencia
DESDE pg_stat_replication;
Al ejecutar esto en el maestro, la función pg_current_wal_location() devuelve la posición actual del registro de
transacciones. PostgreSQL 9.6 tiene un tipo de datos especial para las posiciones del registro de transacciones,
denominado pg_lsn. Cuenta con un par de operadores, que se utilizan aquí para restar la posición WAL del esclavo de
la posición WAL del maestro. La vista descrita aquí, por lo tanto, devuelve la diferencia entre los dos servidores en bytes
(el retraso de replicación).
Nota importante
Tenga en cuenta que esta declaración solo funciona en PostgreSQL 10. La función solía llamarse pg_
current_xlog_location() en versiones anteriores.
Machine Translated by Google
Configuración de la replicación asíncrona 383
Mientras que la vista del sistema pg_stat_replication contiene información sobre el lado emisor, la
vista del sistema pg_stat_wal_receiver nos proporcionará información similar sobre el lado receptor:
prueba=# \d pg_stat_wal_receiver
Ver "pg_catalog.pg_stat_wal_receiver"
Columna | Tipo ...
+ …
pid | entero | texto
estado | pg_lsn
recibir_inicio_lsn | entero
escrito_lsn | pg_lsn recibir_inicio_tli | pg_lsn
recibido_tli | marca descargado_lsn | entero
last_msg_send_time
de tiempo con zona horaria
de tiempo con zona last_msg_receipt_time | marca
horaria last_end_time
horaria
| texto
| pg_lsn last_end_lsn | marca de tiempo con zona
slot_name | texto | entero | texto
host_remitente
puerto_remitente
conninfo
Después de la identificación del proceso del receptor WAL, PostgreSQL le proporcionará el estado del
proceso. Luego, el campo receive_start_lsn le informará sobre la posición del registro de transacciones en
la que comenzó el receptor WAL, mientras que el campo receive_start_tli le informará sobre la línea de
tiempo utilizada cuando se inició el receptor WAL.
Los campos writing_lsn y flusged_lsn contienen información sobre la posición de WAL, que ya se escribió y vació
en el disco, respectivamente. Luego, tenemos información sobre la hora, así como sobre las franjas horarias y las
conexiones.
En general, a muchas personas les resulta más fácil leer la vista del sistema pg_stat_replication que
la vista pg_stat_wal_receiver, y la mayoría de las herramientas se basan en la vista pg_stat_replication.
Realización de failovers y comprensión de los plazos
Una vez que se ha creado una configuración maestro/esclavo, generalmente funciona sin problemas durante mucho
tiempo. Sin embargo, todo puede fallar y, por lo tanto, es importante comprender cómo se puede reemplazar un servidor
fallido con un sistema de respaldo.
Machine Translated by Google
384 Dar sentido a las copias de seguridad y la replicación
PostgreSQL facilita las conmutaciones por error y la promoción. Básicamente, todo lo que tenemos que hacer es usar el
parámetro pg_ctl para decirle a una réplica que se promueva:
pg_ctl D data_dir promover
El servidor se desconectará del maestro y realizará la promoción al instante. Recuerde, es posible que el
esclavo ya admita miles de conexiones de solo lectura mientras se promociona. Una buena característica
de PostgreSQL es que todas las conexiones abiertas se convertirán en conexiones de lectura/escritura
durante la promoción; ni siquiera es necesario volver a conectarse.
Tenga en cuenta que PostgreSQL 12 y versiones posteriores también pueden promover la base de datos de esclava
a maestra utilizando SQL simple. Simplemente use SELECT pg_promote();. Incluso es posible retrasar la promoción
un par de segundos:
postgres=# \df *promover*
Lista de funciones
[ REGISTRO 1 ]+
Esquema | pg_catálogo |
Nombre pg_promocionar |
Tipo de datos de resultado booleano
Tipos de datos de argumentos | espera booleano DEFAULT verdadero,
wait_seconds entero DEFAULT 60 | función
Tipo
Al promocionar un servidor, PostgreSQL incrementará la línea de tiempo; si configura un servidor nuevo, estará
en la línea de tiempo 1. Si se clona un esclavo de ese servidor, estará en la misma línea de tiempo que su maestro.
Entonces, ambas casillas estarán en la línea de tiempo 1. Si el esclavo es ascendido a maestro independiente, pasará a
la línea de tiempo 2.
Los plazos son especialmente importantes para PITR. Supongamos que creamos una copia de seguridad base
alrededor de la medianoche. A las 00:00 se asciende al esclavo. A las 3:00 p. m., algo falla y queremos recuperar
a las 2:00 p . 12:00
El cambio de línea de tiempo también será visible en el nombre de los archivos de registro de transacciones. Aquí hay un ejemplo de un
archivo WAL en la línea de tiempo 1:
0000000100000000000000F5
Machine Translated by Google
Configuración de la replicación asíncrona 385
Si la línea de tiempo cambia a 2, el nuevo nombre de archivo será el siguiente:
0000000200000000000000F5
Como puede ver, los archivos WAL de diferentes líneas de tiempo teóricamente podrían existir en el mismo directorio de archivo.
Manejo de conflictos
Hasta ahora, hemos aprendido mucho sobre la replicación. Sin embargo, es importante echar un vistazo a los
conflictos de replicación. La pregunta principal que surge es esta: ¿cómo puede ocurrir un conflicto en primer lugar?
Considere el siguiente ejemplo:
Maestro Esclavo
Pestaña DROP TABLE; COMENZAR;
SELECCIONE... DESDE la pestaña DONDE ...
... correr ...
... el conflicto sucede ...
... se permite que la transacción continúe durante 30
segundos ...
... el conflicto se resuelve o termina antes del tiempo de espera
...
Figura 10.1: descripción de los conflictos de replicación
El problema aquí es que el maestro no sabe que está ocurriendo una transacción en el esclavo.
Por lo tanto, el comando DROP TABLE no se bloquea hasta que desaparece la transacción de lectura. Si esas
dos transacciones ocurrieron en el mismo nodo, este sería, por supuesto, el caso. Sin embargo, estamos viendo
dos servidores aquí. El comando DROP TABLE se ejecutará normalmente y una solicitud para eliminar esos
archivos de datos en el disco llegará al esclavo a través del registro de transacciones. El esclavo no tiene
problemas: si la tabla se elimina del disco, la cláusula SELECT tiene que morir; si el esclavo espera a que se
complete la cláusula SELECT antes de aplicar WAL, podría quedarse atrás irremediablemente.
La solución ideal es un compromiso que se puede controlar mediante una variable de configuración:
max_standby_streaming_delay = 30 s
# retraso máximo antes de cancelar las consultas # al leer
el flujo de WAL;
Machine Translated by Google
386 Dar sentido a las copias de seguridad y la replicación
La idea es esperar 30 segundos antes de resolver el conflicto matando la consulta en el esclavo.
Dependiendo de nuestra aplicación, podríamos querer cambiar esta variable a una configuración más o menos
agresiva. Tenga en cuenta que 30 segundos son para todo el flujo de replicación y no para una sola consulta.
Puede ser que una sola consulta se elimine mucho antes porque otra consulta ya ha esperado durante algún tiempo.
Si bien el comando DROP TABLE es claramente un conflicto, hay algunas operaciones que son menos obvias.
Aquí hay un ejemplo:
COMENZAR;
...
ELIMINAR DESDE la pestaña DONDE id < 10000;
COMPROMETERSE;
...
pestaña VACÍO;
Una vez más, supongamos que hay una cláusula SELECT de ejecución prolongada en el esclavo. La cláusula
DELETE claramente no es el problema aquí, ya que solo marca la fila como eliminada, en realidad no la elimina.
El compromiso tampoco es un problema porque simplemente marca la transacción como realizada. Físicamente,
la fila sigue ahí.
El problema comienza cuando se inicia una operación como VACUUM. Destruirá la fila en el disco. Por
supuesto, estos cambios llegarán a WAL y finalmente llegarán al esclavo, que luego tendrá problemas.
Para evitar los problemas típicos causados por las cargas de trabajo de OLTP estándar, el equipo de desarrollo de PostgreSQL
ha introducido una variable de configuración:
hot_standby_feedback = desactivado
# enviar información desde el modo de espera para evitar
# conflictos de consultas
Si esta configuración está activada, el esclavo enviará periódicamente el ID de transacción más antiguo al
maestro. VACUUM sabrá entonces que hay una transacción más antigua en algún lugar del sistema y aplazará
la edad de limpieza hasta un punto posterior, cuando sea seguro limpiar las filas. De hecho, el parámetro
hot_standby_feedback provoca el mismo efecto que una transacción larga en el maestro.
Como podemos ver, el parámetro hot_standby_feedback está desactivado por defecto. ¿Por qué es ese el caso?
Bueno, hay una buena razón para esto: si está apagado, un esclavo no tiene un impacto real en el maestro.
La transmisión de registros de transacciones no consume mucha potencia de la CPU, lo que hace que la replicación de transmisión sea
económica y eficiente. Sin embargo, si un esclavo (que puede que ni siquiera esté bajo nuestro control) mantiene las transacciones abiertas
durante demasiado tiempo, nuestro maestro podría sufrir una sobrecarga en la mesa debido a una limpieza tardía. En una configuración
predeterminada, esto es menos deseable que la reducción de conflictos.
Tener hot_standby_feedback = activado normalmente evitará el 99 % de todos los conflictos relacionados con
OLTP, lo que es especialmente importante si sus transacciones tardan más de un par de milisegundos.
Machine Translated by Google
Configuración de la replicación asíncrona 387
Hacer que la replicación sea más confiable
En este capítulo, ya hemos visto que configurar la replicación es fácil y no requiere mucho esfuerzo. Sin embargo,
siempre hay algunos casos de esquina que pueden causar desafíos operativos. Uno de esos casos de esquina se
refiere a la retención del registro de transacciones.
Considere el siguiente escenario:
1. Se recupera una copia de seguridad base.
2. Después de la copia de seguridad, no sucede nada durante 1 hora.
3. Se inicia el esclavo.
Tenga en cuenta que al amo no le importa demasiado la existencia del esclavo. Por lo tanto, es posible que el registro de
transacciones necesario para que el esclavo se inicie ya no exista en el maestro, ya que es posible que ya lo hayan
eliminado los puntos de control. El problema es que se necesita una resincronización para poder encender el esclavo. En el
caso de una base de datos de múltiples TB, esto es claramente un problema.
Una posible solución a este problema es usar la configuración wal_keep_segments:
wal_keep_segmentos = 0 # en segmentos de archivo de registro, 16 MB cada uno; #0
deshabilita
De forma predeterminada, PostgreSQL mantiene suficientes registros de transacciones para sobrevivir a un bloqueo
inesperado, pero no mucho más. Con la configuración wal_keep_segments, podemos decirle al servidor que conserve más
datos para que un esclavo pueda ponerse al día, incluso si se queda atrás.
Es importante tener en cuenta que los servidores no solo se atrasan porque son demasiado lentos o están demasiado
ocupados ; en muchos casos, se produce un retraso porque la red es demasiado lenta. Supongamos que está creando
un índice en una tabla de 1 TB: PostgreSQL clasificará los datos y, cuando el índice esté realmente construido,
también se enviará al registro de transacciones. Solo imagine lo que sucede cuando se envían cientos de megabytes
de WAL a través de un cable que quizás solo pueda manejar 1 GB más o menos. La pérdida de muchos gigabytes de
datos podría ser la consecuencia de esto y sucederá en cuestión de segundos. Por lo tanto, ajustar la configuración
de wal_keep_segments no debe centrarse en el retraso típico sino en el retraso más alto tolerable por un administrador
(quizás con cierto margen de seguridad).
Invertir en una configuración razonablemente alta para la configuración de wal_keep_segments tiene mucho sentido, y
recomiendo asegurarse de que siempre haya suficientes datos.
Una solución alternativa al problema de quedarse sin registros de transacciones son las ranuras de replicación, que se
tratarán más adelante en este capítulo.
Machine Translated by Google
388 Dar sentido a las copias de seguridad y la replicación
Actualización a la replicación síncrona
Hasta ahora, la replicación asincrónica se ha cubierto con un detalle razonable. Sin embargo, la replicación
asíncrona significa que se permite que se produzca una confirmación en un esclavo después de la confirmación
en un maestro. Si el maestro falla, los datos que no llegaron al esclavo podrían perderse incluso si se está replicando.
La replicación síncrona está aquí para resolver el problema: si PostgreSQL se replica de forma síncrona, al menos
una réplica debe vaciar una confirmación en el disco para pasar por el maestro. Por lo tanto, la replicación síncrona
básicamente reduce sustancialmente las probabilidades de pérdida de datos.
En PostgreSQL, configurar la replicación síncrona es fácil. Solo hay que hacer dos cosas (en cualquier
orden):
• Ajuste la configuración synchronous_standby_names en el archivo postgresql.conf en
el maestro
• Agregue una configuración de application_name al parámetro primary_conninfo en el archivo de
configuración en la réplica
Comencemos con el archivo postgresql.conf en el maestro:
''
synchronous_standby_names =
# servidores en espera que brindan un representante de sincronización
# número de esperas de sincronización y separados por comas
# lista de application_name # desde
standby(s); '*' = todos
Si añadimos '*', todos los nodos se considerarán candidatos sincrónicos. Sin embargo, en escenarios de la
vida real, es más probable que solo se enumeren un par de nodos. Aquí hay un ejemplo:
synchronous_standby_names = 'esclavo1, esclavo2, esclavo3'
En el caso anterior, obtuvimos tres candidatos sincrónicos. Ahora, tenemos que cambiar el archivo de
configuración y agregar application_name:
primario_conninfo = '... application_name=esclavo2'
La réplica ahora se conectará al maestro como esclavo2. El maestro verificará su configuración y se dará
cuenta de que el esclavo2 es el primero en la lista que constituye un esclavo viable. PostgreSQL, por lo
tanto, se asegurará de que una confirmación en el maestro solo sea exitosa si el esclavo confirma que la
transacción está allí.
Ahora, supongamos que Slave2 se cae por algún motivo: PostgreSQL intentará convertir uno de los otros
dos nodos en un modo de espera síncrono. El problema es este: ¿qué pasa si no hay otro servidor?
Machine Translated by Google
Actualización a la replicación síncrona 389
En este caso, PostgreSQL esperará la confirmación para siempre si se supone que una transacción es síncrona.
PostgreSQL no seguirá comprometiéndose a menos que haya al menos dos nodos viables disponibles.
Recuerde, le hemos pedido a PostgreSQL que almacene datos en al menos dos nodos; si no podemos
proporcionar suficientes hosts en un momento dado, es culpa nuestra. En realidad, esto significa que la
replicación síncrona se logra mejor con al menos tres nodos, un maestro y dos esclavos, ya que siempre existe
la posibilidad de que se pierda un host.
Hablando de fallas de host, hay una cosa importante a tener en cuenta en este punto: si un socio síncrono muere
mientras se realiza una confirmación, PostgreSQL esperará a que regrese. Alternativamente, la confirmación
sincrónica puede ocurrir con algún otro socio sincrónico potencial. Es posible que el usuario final ni siquiera se
dé cuenta de que los socios síncronos han cambiado.
En algunos casos, almacenar datos en solo dos nodos puede no ser suficiente; tal vez queramos mejorar aún
más la seguridad y almacenar los datos en aún más nodos. Para lograrlo, podemos hacer uso de la siguiente
sintaxis en PostgreSQL 9.6 o superior:
synchronous_standby_names =
'4(esclavo1, esclavo2, esclavo3, esclavo4, esclavo5, esclavo6)'
Por supuesto, esto tiene un precio: tenga en cuenta que la velocidad disminuirá si agregamos más y más réplicas
sincrónicas. No existe tal cosa como un almuerzo gratis. PostgreSQL proporciona un par de formas de mantener
bajo control la sobrecarga de rendimiento, que analizaremos en la siguiente sección.
En PostgreSQL 10.0, se han agregado aún más funciones. Echemos un vistazo a las partes relevantes del
protocolo:
[PRIMERO] num_sync ( standby_name [, ...] )
ANY num_sync ( nombre_en espera [, ...] ) nombre_en espera
[, ...]
Se han introducido las palabras clave ANY y FIRST. FIRST le permite establecer las prioridades de sus
servidores, mientras que ANY le da a PostgreSQL un poco más de flexibilidad cuando realiza una transacción síncrona.
Ajuste de la durabilidad
En este capítulo, hemos visto que los datos se replican de forma sincrónica o asincrónica. Sin embargo, esto no
es algo global. Para garantizar un buen rendimiento, PostgreSQL nos permite configurar las cosas de una forma
muy flexible. Es posible replicar todo de forma síncrona o asíncrona, pero en muchos casos, es posible que
queramos hacer las cosas de forma más detallada. Esto es exactamente cuando se necesita la configuración
synchronous_commit.
Machine Translated by Google
390 Dar sentido a las copias de seguridad y la replicación
Suponiendo que se hayan configurado la replicación síncrona, la configuración application_name y
la configuración synchronous_ standby_names en el archivo postgresql.conf, la configuración
synchronous_ commit ofrecerá las siguientes opciones:
• off: esto es básicamente una replicación asíncrona. WAL no se vaciará al disco en el maestro al instante, y el maestro no
esperará a que el esclavo escriba todo en el disco. Si el maestro falla, es posible que se pierdan algunos datos (hasta
tres veces: wal_writer_delay).
• local: el registro de transacciones se vacía en el disco cuando el maestro realiza una confirmación. Sin embargo, el maestro
no espera al esclavo (replicación asíncrona).
• remote_write: la configuración de remote_write ya hace que PostgreSQL se replique sincrónicamente.
Sin embargo, solo el maestro guarda datos en el disco. Para el esclavo, basta con enviar los datos al sistema operativo.
La idea es no esperar a que se vacíe el segundo disco para acelerar las cosas. Es muy poco probable que ambos
sistemas de almacenamiento se bloqueen exactamente al mismo tiempo. Por lo tanto, el riesgo de pérdida de datos es
cercano a cero.
• on: en este caso, una transacción está bien si el maestro y los esclavos han vaciado correctamente la transacción en el
disco. La aplicación no recibirá una confirmación a menos que los datos se almacenen de forma segura en dos servidores
(o más, según la configuración).
• aplicación_remota: Si bien está activado garantiza que los datos se almacenen de manera segura en dos nodos, no
garantiza que podamos simplemente equilibrar la carga de inmediato. El hecho de que los datos se vacíen en el disco
no garantiza que el usuario ya pueda verlos. Por ejemplo, si hay un conflicto, un esclavo detendrá la reproducción de la
transacción; sin embargo, aún se envía un registro de transacciones al esclavo durante un conflicto y se descarga en el
disco. En resumen, los datos pueden descargarse en el esclavo, incluso si aún no son visibles para el usuario final . La
opción remote_apply soluciona este problema. Garantiza que los datos deben estar visibles en la réplica para que la
siguiente solicitud de lectura se pueda ejecutar de forma segura en el esclavo, que ya puede ver los cambios realizados
en el maestro y exponerlos al usuario final. La opción remote_apply es, por supuesto, la forma más lenta de replicar
datos porque requiere que el esclavo ya exponga los datos al usuario final.
En PostgreSQL, el parámetro synchronous_commit no es un valor global. Se puede ajustar en varios niveles, al igual que muchas
otras configuraciones. Podríamos querer hacer algo como lo siguiente:
test=# ALTER DATABASE test SET synchronous_commit TO off;
ALTERAR BASE DE DATOS
A veces, solo una única base de datos debe replicarse de cierta manera. También es posible replicar sincrónicamente si estamos
conectados como un usuario específico. Por último, pero no menos importante, también es posible decirle a una sola transacción
cómo comprometerse. Al ajustar el parámetro synchronous_commit sobre la marcha, incluso es posible controlar las cosas a nivel
de transacción.
Machine Translated by Google
Hacer uso de las ranuras de replicación 391
Por ejemplo, considere los siguientes dos escenarios:
• Escribir en una tabla de registro, donde podríamos querer usar una confirmación asíncrona porque queremos
ser rápido
• Almacenar un pago con tarjeta de crédito donde queremos estar seguros, por lo que una transacción síncrona podría ser
lo deseado
Como podemos ver, la misma base de datos puede tener diferentes requisitos, dependiendo de qué datos
se modifican. Por lo tanto, cambiar datos a nivel de transacción es muy útil y ayuda a mejorar la velocidad.
Hacer uso de las ranuras de replicación
Después de esa introducción a la replicación síncrona y la durabilidad dinámicamente ajustable, quiero centrarme en una
característica llamada ranuras de replicación.
¿Cuál es el propósito de una ranura de replicación? Consideremos el siguiente ejemplo: hay un maestro y un esclavo. En el
maestro, se ejecuta una transacción grande y la conexión de red no es lo suficientemente rápida para enviar todos los datos a
tiempo. En algún momento, el maestro elimina su registro de transacciones (punto de control). Si el esclavo está demasiado
atrasado, se necesita una resincronización. Como ya hemos visto, la configuración wal_keep_segments se puede usar para
reducir el riesgo de que falle la replicación. La pregunta es esta: ¿cuál es el mejor valor para la configuración de
wal_keep_segments? Claro, más es mejor, pero ¿cuánto es mejor?
Las ranuras de replicación resolverán este problema por nosotros: si usamos una ranura de replicación, un maestro solo puede
reciclar el registro de transacciones una vez que todas las réplicas lo hayan consumido. La ventaja aquí es que un esclavo
nunca puede retrasarse tanto como para necesitar una resincronización.
El problema es, ¿qué pasa si apagamos una réplica sin decírselo al maestro? El maestro mantendría un registro de
transacciones para siempre y el disco en el servidor primario eventualmente se llenaría, causando un tiempo de inactividad
innecesario.
Hay dos cosas a tener en cuenta para reducir el riesgo. En primer lugar, se necesita un seguimiento adecuado para asegurarse
de que nada falle. La segunda opción es controlar una ranura de replicación a través de una variable de tiempo de ejecución:
#max_slot_wal_keep_size = 1 # en megabytes; 1 deshabilita
Normalmente, una ranura de replicación conserva los datos hasta que se consumen. Sin embargo, esto ha causado innumerables apagones.
Las cosas simplemente fallaron debido a que los discos se llenaron, etc. A partir de PostgreSQL 14, ahora podemos limitar la
cantidad de WAL que se permite acumular en una ranura. De forma predeterminada, sigue siendo "ilimitado". Sin embargo,
recomendamos limitar esta cantidad a un número razonable. Idealmente, establezca esto en un valor que asegure que el sistema
de archivos no se puede llenar, sino en un número lo suficientemente grande como para hacer un uso completo de las ranuras de replicación.
Para reducir este riesgo para el maestro, las ranuras de replicación solo se deben usar junto con la supervisión y las
alertas adecuadas (independientemente de las limitaciones de almacenamiento impuestas por max_slot_wal_keep_size).
Simplemente es necesario vigilar las ranuras de replicación abiertas que podrían causar problemas o que podrían no estar
más en uso.
Machine Translated by Google
392 Dar sentido a las copias de seguridad y la replicación
En PostgreSQL, hay dos tipos de ranuras de replicación:
• Ranuras de replicación física
• Ranuras de replicación lógica
Las ranuras de replicación física se pueden usar para la replicación de transmisión estándar. Se asegurarán de que los datos no
se reciclen demasiado pronto. Las ranuras de replicación lógica hacen lo mismo. Sin embargo, se utilizan para la decodificación
lógica. La idea detrás de la decodificación lógica es dar a los usuarios la oportunidad de adjuntar el registro de transacciones y
decodificarlo con un complemento. Una ranura de transacción lógica es, por lo tanto, una especie de cola f para las instancias
de la base de datos. Permite al usuario extraer los cambios realizados en la base de datos y, por lo tanto, en el registro de
transacciones, en cualquier formato y para cualquier propósito.
En muchos casos, se utiliza una ranura de replicación lógica para la replicación lógica.
Manejo de ranuras de replicación física
Para hacer uso de las ranuras de replicación, se deben realizar cambios en el archivo postgresql.conf, de la siguiente manera:
wal_level = max_replication_slots
lógicos = 5 # o el número que sea necesario
Con las ranuras físicas, la lógica no es necesaria: una réplica es suficiente. Sin embargo, para las ranuras lógicas,
necesitamos una configuración wal_level más alta. Luego, la configuración de max_replication_slots debe cambiarse
si estamos usando PostgreSQL 9.6 o inferior. PostgreSQL 10.0 ya tiene una configuración predeterminada mejorada.
Básicamente, simplemente ingresamos un número que sirve para nuestro propósito. Mi recomendación es agregar algunas
ranuras de repuesto para que podamos conectar fácilmente más consumidores sin reiniciar el servidor en el camino.
Después de un reinicio, se puede crear la ranura:
prueba=# \x
La pantalla ampliada está activada.
test=# \df *crear*espacio*físico
Lista de funciones
[ REGISTRO 1 ]+
Esquema | pg_catálogo |
Nombre pg_create_physical_replication_slot | registro
Tipo de datos de resultado
Tipos de datos de argumentos | nombre_nombre_ranura, reserva_inmediata
valor predeterminado booleano falso,
booleano temporal DEFAULT falso,
Machine Translated by Google
Hacer uso de las ranuras de replicación 393
SALIDA slot_name nombre, SALIDA lsn pg_lsn | función
Tipo
La función pg_create_physical_replication_slot está aquí para ayudarnos a crear la ranura.
Se puede llamar con uno de dos parámetros. Si solo se pasa un nombre de ranura, la ranura estará activa cuando
se use por primera vez. Si se pasa verdadero como segundo parámetro, la ranura comenzará inmediatamente a
conservar el registro de transacciones:
test=# SELECT * FROM pg_create_physical_replication_slot('some_ slot_name', true); | lsn
nombre_ranura
+
alguna_ranura_nombre | 0/EF8AD1D8
(1 fila)
Para ver qué ranuras están activas en el maestro, considere ejecutar la siguiente instrucción SQL:
test=# \x La
visualización ampliada está activada.
test=# SELECT * FROM pg_replication_slots; [ REGISTRO 1 ]
+ | alguna_ranura_nombre | | físico | | | F
complemento
nombre_ranura
datoide de tipo
de ranura
base de datos
temporario
...
La vista nos dice mucho sobre la tragamonedas. Contiene información sobre el tipo de ranura en uso, las posiciones del
registro de transacciones y más.
Para hacer uso de la ranura, todo lo que tenemos que hacer es agregarlo al archivo de configuración de la siguiente manera:
nombre_ranura_principal = 'alguna_ranura_nombre'
Una vez que se reinicia la transmisión, la ranura se usará directamente y protegerá la replicación. Si ya no queremos
nuestro espacio, podemos dejarlo fácilmente:
test=# \df *soltar*ranura*
Lista de funciones
Machine Translated by Google
394 Dar sentido a las copias de seguridad y la replicación
[ REGISTRO 1 ]+
Esquema | pg_catálogo |
Nombre pg_drop_replication_slot | vacío
Tipo de datos de resultado
Tipos de datos de argumentos | nombre |
Tipo normal
Cuando se elimina una ranura, ya no hay distinción entre una ranura lógica y una física. Simplemente pase el
nombre de la ranura a la función y ejecútela.
Nota importante
Nadie puede usar la ranura cuando se cae. De lo contrario, PostgreSQL generará un error por una buena
razón.
Manejo de ranuras de replicación lógica
Las ranuras de replicación lógica son esenciales para la replicación lógica. Debido a limitaciones de espacio en este
capítulo, lamentablemente no es posible cubrir todos los aspectos de la replicación lógica. Sin embargo, quiero esbozar
algunos de los conceptos básicos que son esenciales para la decodificación lógica y, por lo tanto, también para la replicación lógica.
Si queremos crear una ranura de replicación, así es como funciona. La función que se necesita aquí toma dos
parámetros: el primero definirá el nombre de la ranura de replicación, mientras que el segundo llevará el
complemento que se usará para decodificar el registro de transacciones. Determinará el formato que PostgreSQL
utilizará para devolver los datos. Echemos un vistazo a cómo se puede hacer una ranura de replicación:
prueba=# SELECCIONAR *
DESDE pg_create_logical_replication_slot('logical_slot', 'test_decoding'); | lsn
nombre_ranura
+
ranura_lógica | 0/EF8AD4B0 (1 fila)
Podemos verificar la existencia de la ranura usando el mismo comando que usamos anteriormente. Para verificar lo que
realmente hace una ranura, se puede crear una pequeña prueba:
test=# CREAR TABLA t_demo (id int, texto de nombre, texto de carga útil);
CREAR MESA
prueba=#COMENZAR;
COMENZAR
Machine Translated by Google
Hacer uso de las ranuras de replicación 395
test=# INSERTAR EN t_demo
VALORES (1, 'hans', 'algunos datos');
INSERTAR 0 1
test=# INSERTAR EN t_demo VALORES (2, 'paul', 'algunos datos más');
INSERTAR 0 1
prueba=# COMPROMISO;
COMPROMETERSE
test=# INSERTAR EN t_demo VALORES (3, 'joe', 'less data');
INSERTAR 0 1
Tenga en cuenta que se ejecutaron dos transacciones. Los cambios realizados en esas transacciones ahora se
pueden extraer de la ranura:
test=# SELECT pg_logical_slot_get_changes('logical_slot', NULL,
NULO);
pg_logical_slot_get_changes
(0/EF8AF5B0,606546,"COMENZAR 606546")
(0/EF8CCCA0,606546,"CONFIRMACIÓN 606546")
(0/EF8CCCD8,606547,"COMENZAR 606547")
(0/EF8CCCD8,606547,"tabla public.t_demo: INSERTAR: id[entero]:1
nombre[texto]:'hans' payload[texto]:'algunos datos'")
(0/EF8CCD60,606547,"table public.t_demo: INSERT: id[integer]:2 name[text]:'paul' payload[text]:'some more
data'")
(0/EF8CCDE0,606547,"CONFIRMACIÓN 606547")
(0/EF8CCE18,606548,"COMENZAR 606548")
(0/EF8CCE18,606548,"tabla public.t_demo: INSERTAR: id[entero]:3
nombre[texto]:'joe' carga útil[texto]:'menos datos'")
(0/EF8CCE98,606548,"CONFIGURACIÓN 606548")
(9 filas)
El formato que se usa aquí depende del complemento de salida que elegimos anteriormente. Hay varios
complementos de salida para PostgreSQL, como wal2json.
Nota importante
Si se utilizan valores predeterminados, el flujo lógico contendrá valores reales y no solo funciones.
El flujo lógico tiene los datos que terminaron en las tablas subyacentes.
Machine Translated by Google
396 Dar sentido a las copias de seguridad y la replicación
Además, tenga en cuenta que la ranura ya no devuelve datos una vez que se consume:
test=# SELECT pg_logical_slot_get_changes('logical_slot', NULL,
NULO);
pg_logical_slot_get_changes
(0 filas)
El conjunto de resultados en la segunda llamada está, por lo tanto, vacío. Si queremos obtener datos
repetidamente, PostgreSQL ofrece la función pg_logical_slot_peek_changes. Funciona igual que la función
pg_logical_ slot_get_changes pero garantiza que los datos seguirán estando disponibles en la ranura.
Por supuesto, el uso de SQL simple no es la única forma de consumir un registro de transacciones. También hay una
herramienta de línea de comandos llamada pg_recvlogical. Se puede comparar con el uso de la cola f en una instancia de
base de datos completa y recibe un flujo de datos en tiempo real.
Iniciemos la herramienta pg_recvlogical con el siguiente comando:
[hs@zenbook ~]$ pg_recvlogical S logical_slot P test_decoding
d prueba U postgres inicio f
En este caso, la herramienta se conecta a la base de datos de prueba y consume datos de logical_slot. La cola f
significa que la transmisión se enviará a la salida estándar. Eliminemos algunos datos:
test=# ELIMINAR DE t_demo DONDE id < random()*10;
ELIMINAR 3
Los cambios se incluirán en el registro de transacciones. Sin embargo, de forma predeterminada, a la base de
datos solo le importa cómo se verá la tabla después de la eliminación. Sabe qué bloques hay que tocar y así
sucesivamente, pero no sabe cuál era la tabla anteriormente:
COMENZAR 606549
table public.t_demo: DELETE: (notupledata)
table public.t_demo: DELETE: (sin datos de tupla) table public.t_demo:
DELETE: (sin datos de tupla)
COMPROMETER 606549
Por lo tanto, la salida es bastante inútil. Para arreglar eso, la siguiente línea viene al rescate:
test=# ALTER TABLE t_demo IDENTIDAD DE LA RÉPLICA COMPLETA;
ALTERAR TABLA
Machine Translated by Google
Haciendo uso de los comandos CREATE PUBLICATION y CREATE SUBSCRIPTION 397
Si la tabla se vuelve a llenar con datos y se elimina nuevamente, el flujo del registro de transacciones tendrá el siguiente aspecto:
COMENZAR 606558
table public.t_demo: DELETE: id[entero]:1 nombre[texto]:'hans'
payload[texto]:'algunos datos'
tabla public.t_demo: ELIMINAR: id[entero]:2 nombre[texto]:'paul'
carga útil[texto]: 'algunos datos más'
tabla public.t_demo: ELIMINAR: id[entero]:3 nombre[texto]:'joe'
carga útil[texto]: 'menos datos'
COMPROMETER 606558
Ahora, todos los cambios están incluidos. Echemos un vistazo a las ranuras de replicación lógica a continuación.
Casos de uso para ranuras de replicación lógica
Hay varios casos de uso para las ranuras de replicación lógica. El caso de uso más simple es el que se muestra en la
siguiente sección. Los datos pueden obtenerse del servidor en el formato deseado y usarse para auditar, depurar o
simplemente monitorear una instancia de base de datos.
El siguiente paso lógico es tomar esta secuencia de cambios y utilizarla para la replicación. Las soluciones
como la replicación bidireccional (BDR) se basan totalmente en la decodificación lógica porque los cambios a
nivel binario no funcionarían con la replicación multimaestro.
Finalmente, a veces es necesario actualizar sin tiempo de inactividad. Recuerde, el flujo de registro de transacciones
binarias no se puede usar para replicar entre diferentes versiones de PostgreSQL. Por lo tanto, las futuras versiones
de PostgreSQL admitirán una herramienta llamada pglogical, que ayuda a actualizar sin tiempo de inactividad.
En esta sección, aprendiste sobre la decodificación lógica y algunas otras técnicas básicas. Echemos un vistazo a los
comandos CREAR PUBLICACIÓN y CREAR SUSCRIPCIÓN ahora.
Haciendo uso de CREAR PUBLICACIÓN y CREAR
Comandos de SUSCRIPCIÓN
A partir de la versión 10.0, la comunidad de PostgreSQL creó dos nuevos comandos: CREAR PUBLICACIÓN y
CREAR SUSCRIPCIÓN. Estos se pueden usar para la replicación lógica, lo que significa que ahora puede replicar
datos de forma selectiva y lograr actualizaciones con tiempo de inactividad cercano a cero. Hasta ahora, la replicación
binaria y la replicación del registro de transacciones se han cubierto por completo. Sin embargo, a veces, es posible
que no queramos replicar una instancia de base de datos completa; replicar una tabla o dos podría ser suficiente.
Esto es exactamente cuando la replicación lógica es lo correcto para usar.
Machine Translated by Google
398 Dar sentido a las copias de seguridad y la replicación
Antes de comenzar, lo primero que debe hacer es cambiar wal_level a lógico en postgresql. conf de la siguiente
manera, y luego reinicie:
wal_level = lógico
Entonces, podemos crear una tabla simple:
test=# CREAR TABLA t_test (a int, b int);
CREAR MESA
El mismo diseño de tabla también debe existir en la segunda base de datos para que esto funcione. PostgreSQL no
creará automáticamente esas tablas para nosotros:
test=# CREAR BASE DE DATOS repl;
CREAR BASE DE DATOS
Después de crear la base de datos, se puede agregar la tabla idéntica:
repl=# CREAR TABLA t_test (a int, b int);
CREAR MESA
El objetivo aquí es publicar el contenido de la tabla t_test en la base de datos de prueba en otro lugar. En este
caso, simplemente se replicarán en una base de datos en la misma instancia. Para publicar esos cambios,
PostgreSQL ofrece el comando CREAR PUBLICACIÓN:
test=# \h CREAR PUBLICACIÓN
Dominio: CREAR PUBLICACIÓN
Descripción: definir una nueva publicación
Sintaxis:
CREAR PUBLICACION nombre
[ PARA TODAS LAS MESAS
| FOR objeto_publicación [, ... ] ]
[ CON ( parámetro_publicación [= valor] [, ... ] ) ]
donde objeto_publicación es uno de: TABLE [ ONLY ]
table_name [ * ] ] [, ... ] [ ( nombre_columna [, ... ] )
] [ DONDE ( expresión )
TABLAS EN ESQUEMA { schema_name | ESQUEMA_ACTUAL } [, ... ]
URL: https://www.postgresql.org/docs/15/sqlcreatepublication. html
Machine Translated by Google
Haciendo uso de los comandos CREATE PUBLICATION y CREATE SUBSCRIPTION 399
La sintaxis es bastante fácil. Todo lo que necesitamos es un nombre y una lista de todas las tablas que
el sistema debe replicar:
test=# CREAR PUBLICACION pub1 PARA TABLA t_test;
CREAR PUBLICACIÓN
Ahora, se puede crear la suscripción. La sintaxis es, nuevamente, bastante simple y directa:
test=# \h CREAR SUSCRIPCIÓN
Comando: CREAR SUSCRIPCIÓN
Descripción: definir una nueva suscripción
Sintaxis:
CREAR SUSCRIPCIÓN nombre_suscripción
CONEXIÓN 'conninfo'
PUBLICACIÓN nombre_publicación [, ...]
[ CON ( parámetro_suscripción [= valor] [, ... ] ) ]
URL: https://www.postgresql.org/docs/15/sql createsubscription.html
Básicamente, crear una suscripción directamente no es ningún problema. Sin embargo, si jugamos este juego
dentro de la misma instancia desde la base de datos de prueba a la base de datos de réplica, es necesario
crear la ranura de replicación en uso manualmente. De lo contrario, CREAR SUSCRIPCIÓN nunca terminará.
Esto es realmente importante: muchas personas informaron problemas al no cumplir con este requisito. Sin
embargo, en general, las publicaciones y las suscripciones no se utilizan dentro de la misma instancia. La
función no está diseñada para usarse de esa manera, sino en múltiples hosts e instancias. Aquí hay un
ejemplo de cómo se puede crear la ranura:
test=# SELECT pg_create_logical_replication_slot('sub1', 'pgoutput');
pg_create_logical_replication_slot
(sub1,0/27E2B2D0)
(1 fila)
En este caso, el nombre de la ranura que se crea en la base de datos maestra es sub1. Luego, necesitamos
conectarnos a la base de datos de destino y ejecutar el siguiente comando:
repl=# CREAR SUSCRIPCIÓN sub1
CONEXIÓN 'host=localhost dbname=usuario de prueba=postgres'
PUBLICACIÓN pub1
Machine Translated by Google
400 Dar sentido a las copias de seguridad y la replicación
CON (create_slot = falso);
CREAR SUSCRIPCIÓN
Por supuesto, tenemos que ajustar los parámetros de CONEXIÓN de nuestra base de datos. Luego, PostgreSQL
sincronizará los datos y listo.
Nota importante
Tenga en cuenta que create_slot = false solo se usa porque la prueba se ejecuta dentro de la misma
instancia del servidor de la base de datos. Si usamos diferentes bases de datos, no hay necesidad de crear
manualmente la ranura y no es necesario crear_ranura = falso.
Configuración de un clúster HA con Patroni
Después de esta introducción a la replicación de PostgreSQL y algunas técnicas básicas, es hora de ponerlo todo
junto y resolver un problema importante: PostgreSQL HA. Un solo servidor o una sola pieza de hardware siempre es
propenso a errores. Si un servidor falla, queremos evitar que toda la infraestructura se caiga. Lo que queremos es
algún tipo de servidor de respaldo que se haga cargo en caso de falla.
En el mundo de PostgreSQL, Patroni se ha convertido en el estándar de facto para lograr HA. Por lo tanto, esta sección trata
sobre Patroni y cómo puede usarlo para hacer que las configuraciones de la base de datos PostgreSQL sean más confiables.
Comprender cómo opera Patroni
Antes de que podamos comenzar, es importante explicar la lógica subyacente de Patroni en general.
Tradicionalmente, una configuración de alta disponibilidad constaba de dos servidores que estaban conectados mediante dos interfaces de red.
Una interfaz de red serviría para mover los datos dentro de un clúster. La segunda conexión servía
como testigo para distinguir entre fallo de red y de nodo. ¿Por qué es eso importante? Si el servidor A envía un
mensaje al servidor B, hay tres resultados posibles: a) la transferencia simplemente funciona (el caso ideal), b) la
transferencia falla porque el cable está roto, o c) la transferencia falla porque el segundo nodo está abajo. Un
segundo cable ayuda a averiguar qué ha sucedido (una falla de nodo o de red). Si bien esta arquitectura ha
funcionado bien durante muchos años, tiene sus limitaciones en el mundo moderno: los clústeres de alta
disponibilidad estaban fuertemente vinculados al hardware subyacente. Dependía del cableado, generalmente
servidores físicos, etc.
El nuevo concepto es, por tanto, diferente. Se basa en algoritmos de "consenso" para hacer las cosas
(específicamente, el protocolo RAFT). Esto nos lleva a la arquitectura general que forma un grupo Patroni moderno:
• Consenso (etcd, ZooKeeper, etc.)
• Patronos
• PostgreSQL
• haproxy, yaim o vipmanager (todos opcionales)
Machine Translated by Google
Configuración de un clúster HA con Patroni 401
¿Cuál es el propósito de esos componentes y cómo interactúan? Centrémonos en la primera parte, el
consenso. Las herramientas como etcd son "almacenes de valores clave distribuidos", lo que significa que
puede hablar con esta herramienta mediante una interfaz REST. Lo que hace Patroni es poner una clave en
etcd que caducará después de un tiempo. Si la clave no se puede renovar, un nodo se considerará muerto.
El trato es, por lo tanto, que se elegirá un primario, y se supone que debe actualizar su clave dentro del
almacén de clavevalor una y otra vez . Si no es posible, se debe elegir un nuevo líder, y Patroni hará todo el
cableado para que esto suceda. Esto significa encontrar un candidato adecuado, promocionarlo, reconfigurar la replicación, etc.
Otra forma de decirlo es que etcd almacena información en la que todos están de acuerdo y Patroni controla
PostgreSQL de una manera que garantiza que el consenso se implemente realmente. La forma en que Patroni
interactúa con PostgreSQL es como lo haría un administrador: enviar una señal, detener, iniciar, reconfigurar, etc.
Patroni es, por lo tanto, simplemente automatización para que PostgreSQL haga cumplir el estado de consenso del clúster.
La experiencia práctica ha demostrado que este enfoque es muy fiable y mucho más fácil de configurar que las
arquitecturas anteriores. También es increíblemente autocurativo y necesita muy poca interacción humana.
Instalación de Patroni
Patroni viene con la mayoría de las distribuciones de Linux. Está disponible como paquete para Debian, así
como para cosas como RHEL y CentOS. Para facilitarle la instalación de Patroni, hemos compilado un pequeño
script que muestra todos los pasos relevantes desde la perspectiva de RedHat. Veámoslo paso a paso:
#!/bin/bash
ROJO='\033[0;31m'
VERDE='\033[0;32m'
BLANCO='\033[1;37m'
claro
Bueno, todos queremos color, ¿no? Hacer que el guión sea agradable no es estrictamente necesario, pero si optamos por
una solución interior, también podríamos hacerlo más hermoso. Lo que hace esta solución es establecer un código de color.
Luego, verificamos si el script, de hecho, se ejecutó como usuario root. Es necesario ejecutar como root
porque tenemos que instalar muchos paquetes de software diferentes:
si [[ $EUID ne 0 ]]; entonces
sleep 1.8 echo
e "${RED}Error: este script debe ejecutarse como root" echo e "${WHITE}" sleep 1.8
salida 1
fi
Machine Translated by Google
402 Dar sentido a las copias de seguridad y la replicación
¿Qué software necesitamos para ejecutar el clúster? Se instalarán los siguientes paquetes:
echo "++"
Luego le preguntamos al usuario qué quiere hacer:
# obtener información del usuario si desea instalar el clúster etcd/patroni
read p "¿Desea instalar etcd & patroni? (s/n) " input_etcd # Verifique si la entrada del usuario es usuario_
válida y si lo es
(s)
# iniciar instalador
if [ "$user_input_etcd" != "y" ]; entonces echo e "${RED}Cancelando
instalación.......!" echo e "${BLANCO}" dormir 1.8
salida
demás
eco ""
Una vez que hayamos validado la entrada del usuario, podemos instalar etcd, que se encarga del consenso:
# establecer la versión de etcd a la última
ETCD_VER=v3.5.1 echo
e "${BLANCO}################################### #" echo "¡Descargando la última
compilación de etcd!..."
# descargar etcd de los servidores de google
GOOGLE_URL=https://storage.googleapis.com/etcd # eliminar si hay una versión
anterior o actual
rm f /tmp/etcd*linuxamd64.tar.gz rm rf /tmp/etcddescarga
prueba && mkdir p /tmp/etcd
# descarga, etc.
curl s L ${GOOGLE_URL}/${ETCD_VER}/etcd${ETCD_VER}
Machine Translated by Google
Configuración de un clúster HA con Patroni 403
linuxamd64.tar.gz o /tmp/etcd.tar > /dev/null
# navegue a /tmp/ y descomprima etcd tar y copie los ejecutables de etcd
cd /tmp/ tar
xf /tmp/etcd.tar > /dev/null rm f /user/local/bin/etcd* mv /
tmp/etcd${ETCD_VER}linuxamd64/etcd* /usr/
local/papelera/
Este código descarga etcd y lo coloca en /usr/local/bin/. Tenga en cuenta que para muchos sistemas
operativos, hay paquetes listos para usar disponibles. Sin embargo, no todas las versiones de etcd son
iguales. Sugerimos usar uno muy reciente y apegarse a él. Además, intente asegurarse de no mezclar
versiones en diferentes nodos.
Así es como podemos instalar etcd. En las versiones recientes de RHEL/CentOS, ya no había paquetes etcd
realmente útiles que tuvieran sentido, por lo que usamos los tarballs directamente para la implementación. etcd se
ha escrito en Go, por lo que se presenta como un binario único vinculado estáticamente que hace que sea realmente
fácil de implementar y ejecutar:
# bonita pantalla de carga
dormir 5.8 y PID=$!
echo "Instalando etcd v3.5.1 ....." printf "${GREEN}[" while kill
0 $PID 2> /dev/null; imprime "$
{VERDE}▓"
dormir 0.13 printf
"${GREEN}▓" dormir 0.02
printf "${VERDE}▓"
dormir 0.2
printf "${VERDE}▓" dormir 0.8
hecho
printf "${GREEN}] hecho!"
eco ""
# crear carpetas ETCD
mkdir p /etc/etcd /var/lib/etcd # crear grupo y usuario
groupadd f system etcd useradd s /sbin/
nologin system g etcd etcd 1> /dev/null
Machine Translated by Google
404 Dar sentido a las copias de seguridad y la replicación
2>> /desarrollo/null
# cambiar propietario de /var/lib/etcd a etcd usuario chown R etcd:etcd /
var/lib/etcd/
fi
En el siguiente paso, podemos instalar los repositorios comunitarios de PostgreSQL. La mayoría de las distribuciones de
Linux tienen una versión de PostgreSQL. No obstante, recomendamos utilizar las versiones comunitarias para disfrutar de
toda la potencia de todas las extensiones disponibles:
echo e "${BLANCO}###################################"
eco ""
echo e "${WHITE}####################################" echo "Agregando repositorio
de PostgreSQL..."
# eliminar postgresql repo si está instalado dnf remove qy
https://download.postgresql.org/pub/repos/yum/reporpms/EL8x86_64/pgdgredhatrepo
latest.noarch.rpm 1>/dev/ nulo 2>> /dev/null
# recién instalado postgresql repo dnf install qy https://
download.postgresql.org/pub/repos/yum/reporpms/EL8x86_64/pgdgredhatrepolatest.noarch.rpm 1> /
dev/null 2>> /desarrollo/null
# Deshabilite el módulo PostgreSQL incorporado:
módulo dnf qy deshabilitar postgresql
# clean postgresql packages echo "Eliminando
los paquetes PostgreSQL antiguos si están instalados..." dnf remove qy postgresql* 1> /dev/
null 2>> /dev/null
Lo importante aquí es que deshabilitemos las cosas integradas. Solo queremos usar las versiones
comunitarias. Luego, instalamos la versión deseada de PostgreSQL:
# instalar postgresqlXXservidor
# función para comprobar si la versión de PSQL es válida isTrue() { if
[ "$psql_version" !
= "12" ] || [ "$psql_version" != "13"
] || [ "$psql_version" != "14" ] || [ "$psql_version" != "15" ]; entonces
volver 1
demás
volver 0
Machine Translated by Google
Configuración de un clúster HA con Patroni 405
fi
Por supuesto, queremos preguntarle al usuario qué versión de PostgreSQL prefiere antes de instalarla:
read p "¿Qué versión de PostgreSQL desea instalar? (12/13/14/15)" psql_version
mientras que es verdadero
hacer
si (( $! == 1 ))
entonces
read p "No es una versión válida de PostgreSQL, elija una de las 3: (12/13/14/15)"
psql_version
fi
hecho
# instalar paquetes psql echo
"Instalando PostgreSQL ${psql_version} ...." dnf install qy postgresql${psql_version}
server postgresql${psql_version}contrib 1> /dev/null 2>> /dev/null
En el siguiente paso, nos encargaremos de instalar los paquetes Patroni y nos aseguraremos de que todo se vea bien y
brillante. Nuevamente, mostraremos algunas barras de progreso y así sucesivamente para que las cosas se vean mejor
(no dude en omitir estas partes):
# bonita pantalla de carga
dormir 5.8 y PID=$! printf "$
{GREEN}[" while kill 0 $PID
2> /dev/null; hacer printf "${GREEN}▓" dormir 0.13 printf "$
{GREEN}▓" dormir 0.02 printf "$
{GREEN}▓"
dormir 0.2 printf "${GREEN}▓"
dormir 0.8
hecho
printf "${GREEN}] hecho!"
eco ""
Machine Translated by Google
406 Dar sentido a las copias de seguridad y la replicación
echo e "${BLANCO}###################################"
eco ""
# eliminar paquetes antiguos de patroni si hay algunos yum remove qy epel
release 1> /dev/null 2>> /dev/null yum remove qy patronietcd.x86_64 1> /dev/null 2>> /
dev/ nulo # instalar patroni echo e "${BLANCO}################################### " echo
"Instalando Patroni ...." yum
install qy epelrelease 1> /dev/null 2>> /dev/null yum install qy patronietcd.x86_64 1> /
dev/null 2>> /dev /null # buena pantalla de carga
sleep 5.8 & PID=$! printf "${GREEN}[" while kill 0 $PID 2> /dev/null; hacer printf "${GREEN}
▓" dormir 0.13 printf "${GREEN}▓" dormir 0.02 printf "${GREEN}▓" dormir 0.2 printf "${GREEN}▓"
dormir 0.8
hecho
printf "${GREEN}] hecho!"
eco ""
echo e "${BLANCO}###################################"
eco ""
Ahora que todo se ha instalado, podemos avanzar y configurar nuestro primer clúster. Asegúrese de que
el proceso se repita en los tres nodos de la base de datos que necesitará para el clúster. Una máquina
no es suficiente. Necesita tres cajas idénticas o dos servidores de base de datos (con etcd) y un tercer
nodo solo para etcd. Para estar seguro, use un número impar para etcd (3, 5, 7, etc.).
Creación de plantillas Patroni
En el siguiente paso, queremos configurar nuestro clúster. Para hacerlo, tenemos que entender cómo podemos
implementar un clúster. La forma en que Patroni hace esto es mediante el uso de plantillas. Cuando inicias Patroni, puedes pasar un
Machine Translated by Google
Configuración de un clúster HA con Patroni 407
plantilla (por ejemplo, patroni0.yml) que luego se usa para configurar PostgreSQL. Tenga en cuenta que la plantilla
solo se usa durante la instalación; nunca se vuelve a tocar más tarde.
La plantilla sabe todo sobre la configuración deseada y convertirá esta información en un nodo de clúster utilizable.
Aquí está el contenido del archivo postgres0.yml (que se puede encontrar en los paquetes binarios como ejemplo).
Discutiremos esas secciones en detalle:
alcance: batman
#espacio de nombres: /servicio/
nombre: postgresql0
La primera sección define el alcance y el nombre del nodo. La pregunta natural que surge aquí es, ¿qué entendemos
por alcance? En general, cientos de clústeres de PostgreSQL comparten un clúster etcd. Todos ellos pueden
descargar su información de estado en etcd. Esos nodos de alguna manera necesitan saber a qué grupo pertenecen.
El alcance responde a esa pregunta. Lo que vemos aquí es que el nodo postgresql0 pertenece a un clúster llamado
batman, que puede contener uno o más servidores.
Echemos un vistazo a la siguiente sección:
restapi:
escucha: 127.0.0.1:8008
connect_address: 127.0.0.1:8008 # certfile: /etc/ssl/
certs/sslcertsnakeoil.pem # keyfile: /etc/ssl/private/sslcertsnakeoil.key # autenticación:
# nombre de usuario: nombre de usuario
# contraseña: contraseña
Cada proceso Patroni se expone a sí mismo como una interfaz REST (lo mismo ocurre con etcd). Esto tiene un par
de ventajas. Si desea realizar un cambio en un clúster, puede hacerlo convenientemente a través de REST : no es
necesario iniciar sesión en la línea de comandos porque no es necesario acceder al archivo de configuración. Pero
hay más: si desea cambiar una variable (work_mem) en un clúster completo, simplemente puede hablar con un solo
proceso Patroni y se asegurará de que el cambio se envíe a todos los demás miembros del clúster, lo que reduce en
gran medida el esfuerzo de mantenimiento para administrar una configuración. Usar REST es simplemente más fácil
que hablar con cada nodo a través de SSH uno por uno.
Finalmente, queremos seguridad adicional al integrar SSL:
# control:
# inseguro: falso # Permitir conexiones a sitios SSL sin
certificados
Machine Translated by Google
408 Dar sentido a las copias de seguridad y la replicación
# certfile: /etc/ssl/certs/sslcertsnakeoil.pem # cacert: /etc/ssl/certs/sslcacert
snakeoil.pem
En esta sección, ya ha visto que los estados de los clústeres se sincronizan mediante etcd o una herramienta similar.
Por lo tanto, tenemos que decirle a Patroni dónde se puede encontrar etcd:
etc.:
#Proporcionar host para realizar el descubrimiento inicial de la topología del clúster:
servidor: 127.0.0.1:2379
#O use "hosts" para proporcionar múltiples puntos finales
#Podría ser una cadena separada por comas: #hosts:
host1:puerto1,host2:puerto2
#o una lista yaml real:
#Hospedadores:
# host1:puerto1 #
host2:puerto2
#Una vez que se completa el descubrimiento, Patroni utilizará la lista de URL de cliente
anunciadas
#Es posible cambiar este comportamiento configurando:
#use_proxies: verdadero
Hay dos formas de hablar con etcd. Una opción es enumerar un solo host. En este caso, Patroni dejará de funcionar
(degradando a PostgreSQL) cuando no se pueda acceder a etcd. Sin embargo, también es posible enumerar
muchos etcd aquí. Siempre que se pueda acceder a un etcd que tenga una mayoría, su nodo de base de datos
estará absolutamente bien. Se recomienda apuntar a más de un etcd por razones de confiabilidad.
Sin embargo, etcd no es lo único que ofrece soporte para el protocolo RAFT. También hay una
implementación RAFT incorporada escrita en Python que ofrece una alternativa a RAFT. Configurar esta
implementación es bastante similar a lo que acabas de ver. Sin embargo, la mayoría de la gente
simplemente usa etcd, ya que está más extendido:
#balsa:
#dir_datos: . #
self_addr: 127.0.0.1:2222 # partner_addrs:
# 127.0.0.1:2223
# 127.0.0.1:2224
Machine Translated by Google
Configuración de un clúster HA con Patroni 409
Las siguientes secciones tratan sobre el aprovisionamiento de PostgreSQL. Recuerde que la plantilla solo se usa para
instalar PostgreSQL y configurar la instalación. No se usa más adelante, lo que significa que tenemos que empaquetar
todos los parámetros de configuración en este archivo. Esta primera sección trata sobre DCS (el consenso distribuido):
oreja:
# esta sección se escribirá en
Etcd:/<namespace>/<scope>/config después de inicializar el nuevo clúster # y todos los demás
miembros del clúster lo usarán como ̀global
configuración`
CC:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
# master_start_timeout: 300
Centrémonos en las partes relevantes aquí: ttl significa "hora de irse", que es el tiempo que establecemos antes de que un
usuario inactivo sea expulsado del grupo por no mostrar ningún signo de vida. Las llaves tienen que ser renovadas cada 10
segundos. Lo que también es importante es la cantidad de datos que se permite perder durante una conmutación por error
no controlada. La posición principal del registro de transacciones no se muestrea en tiempo real; en realidad, la cantidad de
datos perdidos en la conmutación por error está, en el peor de los casos, limitada por los bytes de
máxima_retraso_en_la_conmutación por error del registro de transacciones, más la cantidad que se escribe en los últimos
ttl segundos (loop_wait/2 segundos en un caso promedio) . Sin embargo, un retraso de replicación de estado estable típico
es muy inferior a un segundo, por lo que no es un gran problema en muchos casos.
De forma predeterminada, PostgreSQL establecerá el modo de replicación en asíncrono:
# modo_sincrónico: falso
Si desea que se permita la replicación síncrona (o que se habilite de forma predeterminada), puede configurar el anterior
a la verdad
La siguiente sección trata sobre los clústeres en espera:
#clúster_en_espera:
#anfitrión: 127.0.0.1
#puerto: 1111
#primary_slot_name: patroni
La idea aquí es que si tiene más de un centro de cómputo, es posible que desee que todas las operaciones se
realicen en un centro de datos principal. Si falla un nodo, realizará la conmutación por error en el centro de datos
principal y querrá seguir atendiendo a sus clientes desde el mismo lugar. Sin embargo, es posible que aún desee tener repuestos
Machine Translated by Google
410 Dar sentido a las copias de seguridad y la replicación
capacidad en un segundo centro de datos, básicamente, un "clúster esclavo" de una infraestructura operativa existente.
Por lo tanto, si hay un problema, puede conmutar por error al segundo clúster y declararlo como el centro de datos
principal. Por lo general, este tipo de configuración solo tiene sentido si tiene más de una ubicación para alojar
tus servidores
Finalmente, podemos decirle a nuestra implementación de PostgreSQL qué parámetros de configuración se supone que debe usar:
postgresql:
use_pg_rewind: verdadero
# use_slots: verdadero
parámetros:
# wal_level: hot_standby hot_standby:
# "on" max_connections: 100
#
# max_worker_processes: 8
# wal_keep_segments: 8
# max_wal_senders: 10
# max_replication_slots: 10
# max_prepared_transactions: 0
# max_locks_per_transaction: 64 wal_log_hints: "on"
# track_commit_timestamp: "off"
# archive_mode: "on" archive_timeout: 1800s
# archive_command: mkdir p ../
# wal_archive && prueba ! f # ../
archivo_wal/%f && cp %p ../archivo_wal/%f
# recuperación_conf:
# comando_restauración: cp ../wal_archive/%f %p
La primera sección le dirá a Patroni que use pg_rewind para resincronizar un nodo y usar ranuras de replicación
si lo desea. Se recomienda establecer ambos parámetros en verdadero. Sin embargo, la parte realmente
importante aquí es la parte del parámetro. Con esta información, Patroni generará la configuración de PostgreSQL.
Entonces, en lugar de ajustar postgresql.conf manualmente, use esas plantillas.
La siguiente sección trata sobre initdb:
# algunas opciones deseadas para 'initdb' initdb: # Nota:
debe ser una lista (algunas opciones # necesitan valores, otras son interruptores)
Machine Translated by Google
Configuración de un clúster HA con Patroni 411
codificación: UTF8
sumas de verificación de datos
Puede decirle a Patroni qué conjunto de caracteres desea y si desea o no sumas de verificación. En general, las
sumas de comprobación son una buena idea.
pg_hba.conf también se rige por la plantilla:
pg_hba: # Agregue las siguientes líneas a pg_hba.conf después de ejecutar
'initdb'
# Para conectividad basada en kerberos gss (descartar @.*$) # replicador de replicación
de host 127.0.0.1/32 gss include_ realm=0
# host all all 0.0.0.0/0 gss include_realm=0 host replicator replicator
127.0.0.1/32 scramsha256 host all all 0.0.0.0/0 scramsha256 # hostssl all all 0.0.0.0 /0 scram
sha256 # Script adicional que se lanzará después del clúster
inicial
creación (se pasará la URL de conexión como parámetro)
Simplemente coloque sus reglas pg_hba.conf allí y la configuración se generará para usted:
# post_init: /usr/local/bin/setup_cluster.sh
Patroni ofrece varios ganchos que le permiten inyectar código personalizado, como el gancho post_init. Le
permite ejecutar cualquier código que necesite después de la implementación.
Finalmente, es posible que desee crear algunos usuarios con algunos atributos directamente después de ejecutar
initdb y encender:
# Algunos usuarios adicionales que deben crearse
# después de inicializar un nuevo clúster
usuarios:
administración:
contraseña: opciones de
administrador:
crear rol
creadob
En el siguiente paso, debe definir el directorio de datos y los puertos en los que PostgreSQL debe escuchar.
También queremos decirle a Patroni dónde almacenar los archivos .pgpass. Lo importante aquí es que hay un
Machine Translated by Google
412 Dar sentido a las copias de seguridad y la replicación
distinción entre el puerto de escucha y el puerto de conexión. listen define cómo se puede acceder
a los nodos dentro del clúster, mientras que connect_address maneja la visibilidad fuera del clúster:
postgresql:
escucha: 127.0.0.1:5432
dirección_conexión: 127.0.0.1:5432 dir_datos: data/
postgresql0 # bin_dir: # config_dir: pgpass: /
tmp/pgpass0
autenticación:
replicación: nombre
de usuario: replicador
contraseña: reppass
superusuario:
nombre de usuario: postgres
contraseña: zalando
rebobinar: # No tiene efecto en postgres 10 e inferior nombre de usuario:
rewind_user contraseña:
rewind_password
# Kerberos spn del lado del servidor
# krbsrvnombre: postgres
parámetros:
# Archivo de ticket de kerberos completamente calificado para el usuario que ejecuta
# igual que KRB5CCNAME utilizado por GSS #
krb_server_keyfile: /var/spool/keytabs/postgres
unix_socket_directorios: '.'
# Script de esgrima adicional ejecutado después de adquirir el bloqueo de líder pero antes
de promocionar la réplica #pre_promote: /path/to/pre_promote.sh
Finalmente, hay etiquetas que se pueden usar para enseñar comportamientos adicionales a Patroni:
etiquetas:
nofailover: falso
equilibrio sin carga: falso
clonar de: falso
sin sincronización: falso
Machine Translated by Google
Configuración de un clúster HA con Patroni 413
La configuración nofailover le indicará a Patroni que este nodo no es un candidato adecuado para la conmutación por error.
Esta podría ser una configuración válida si no todos sus nodos son igualmente poderosos.
clonefrom es igualmente importante. Supongamos que no todos los nodos dentro del clúster están en la misma
ubicación. Si Patroni tiene que volver a sincronizar, es mejor copiar los datos "localmente" que copiarlos a través
de conexiones de red largas y lentas.
A menudo, desea que algunos servidores sean candidatos sincrónicos, mientras que otros siempre deben permanecer
asincrónicos porque están fuera del sitio. nosync es el método para controlar este comportamiento.
Configurando etcd
Una vez que las plantillas de Patroni están completas, es hora de echar un vistazo a la configuración de etcd. Aquí
hay un ejemplo:
_ETCD_NAME="<nombre_nodo>"
_ETCD_DATA_DIR="/var/lib/etcd/<nombre_del_clúster>"
_ETCD_LISTEN_PEER_URLS="http://<node_ip>:2380"
_ETCD_LISTEN_CLIENT_URLS="http://<node_ip>:2379"
_ETCD_INITIAL_ADVERTISE_PEER_URLS="http://<node_ip>:2380"
_ETCD_ADVERTISE_CLIENT_URLS="http://<node_ip>:2379"
_ETCD_INITIAL_CLUSTER_TOKEN="etcd<nombre>"
_ETCD_INITIAL_CLUSTER_STATE="nuevo"
_ETCD_INITIAL_CLUSTER="<node1_hostname>=http://<node1_
ip>:2380,<node2_hostname>=http://<node2_ip>:2380,<node3_ hostname>=http://
<node3_ip>:2380"
_ETCD_HEARTBEAT_INTERVAL=250
_ETCD_ELECTION_TIMEOUT=1250
Tenga en cuenta que esta plantilla etcd es para un clúster etcd de tres nodos. Se necesitan dos puertos TCP:
2379 y 2380. Debe asegurarse de que estos puertos estén abiertos y se puedan usar para comunicarse dentro
del clúster etcd. Este tipo de plantilla tiene que estar en los tres nodos. Por lo tanto, debe asegurarse de que
nodeX_ip esté configurado en la IP correcta (la máquina etcd en la que está trabajando).
Si ha instalado etcd desde los archivos TAR, como se muestra anteriormente, también necesitará un script
para manejar systemd. El siguiente listado contiene un ejemplo:
[Unidad]
Descripción=servicio etcd
After=red.objetivo
After=networkonline.target Wants=red
online.target
Machine Translated by Google
414 Dar sentido a las copias de seguridad y la replicación
[Servicio]
Type=notify
EnvironmentFile=/etc/etcd/etcd.conf ExecStart=/usr/local/
bin/etcd \ name=${_ETCD_NAME} \ datadir=$
{_ETCD_DATA_DIR} \ initial
advertise peerurls=$
{_ETCD_INITIAL_ADVERTISE_PEER_ URLS} \ listenpeerurls=${_ETCD_LISTEN_PEER_URLS} \
listen
clienturls=${_ETCD_LISTEN_CLIENT_URLS} \ advertiseclienturls=$
{_ETCD_ADVERTISE_CLIENT_URLS} \ initialclustertoken=$
{_ETCD_INITIAL_CLUSTER_TOKEN} \ initialcluster=${_ETCD_INITIAL_CLUSTER} \
initialclusterstate=${_ETCD_INITIAL_CLUSTER_STATE} \ heartbeatinterval=$
{_ETCD_HEARTBEAT_INTERVAL} \ eleccióntiempo de espera=$
{_ETCD_ELECTION_TIMEOUT}
Reiniciar = en caso de falla
Reiniciar segundo = 5
[Instalar]
WantedBy=multiusuario.objetivo
Muchas distribuciones de Linux en Patroni vienen con scripts systemd listos para usar. Sin embargo, si no está
usando una de esas distribuciones o prefiere hacer este tipo de configuración manualmente, la siguiente lista
contiene un script de inicio systemd listo para usar para Patroni:
After=redonline.objetivo
[Servicio]
Tipo=sencillo
Usuario = postgres
Grupo = postgres
#DirectorioDeTrabajo=~
# Ubicación de la configuración de Patroni
Environment=PATRONI_CONFIGURATION=/etc/patroni/patroni.yml # Deshabilitar OOM kill en el
postmaster OOMScoreAdjust=1000 #Ejecutar con
PATRONI_CONFIG_LOCATION
Variable configurada anteriormente en Environment
ExecStart=/usr/bin/patroni ${PATRONI_CONFIGURACIÓN}
Machine Translated by Google
Configuración de un clúster HA con Patroni 415
ExecReload=/bin/kill HUP $MAINPID
# Dé una cantidad de tiempo razonable para que el servidor se inicie/apague
Tiempo de espera por segundo = 30
TimeoutStopSec=120s # solo
elimina el proceso patroni, no sus hijos, por lo que detendrá correctamente postgres
KillSignal=ENTRAR
KillMode=proceso
[Instalar]
WantedBy=multiusuario.objetivo
En algunas distribuciones de Linux, es posible que etcd no quiera iniciarse. Si es así, verifique la configuración de
SELinux y ajústela (o apague SELinux por completo) para evitar este problema. Este problema ha sido informado por
muchos usuarios que intentan ejecutar etcd.
Encendiendo Patroni
Una vez que todo esté en su lugar, finalmente podemos iniciar etcd y Patroni (systemctl start etcd y systemctl
start patroni). Verificar el estado del servicio Patroni debería apuntar a una instalación en funcionamiento:
[root@node1 ~]# systemctl status patroni • patroni.service Cargado:
cargado (/usr/lib/systemd/
system/patroni.service; habilitado; proveedor predeterminado: deshabilitado)
Activo: activo (en ejecución) desde el lun 20221025 22:53:10 CEST; Hace 14 minutos PID principal:
5680 (patroni)
Tareas: 18 (límite: 4772)
Memoria: 167,5 M
CGroup: /system.slice/patroni.service ├─5680 /usr/bin/python3 /
usr/bin/patroni /etc/
patroni/patroni .yml
├─6097 /usr/pgsql14/bin/postgres D / var/lib/postgresql/14/
batman configfile=/var/ lib/postgresql/15/batman/postgresql.conf listen_
direcciones= 172.16.15.128,172.16.15.129,172.16.15.130 puerto>
├─6099 postgres: batman: registrador ├─6101
postgres: batman: puntero de control
Machine Translated by Google
416 Dar sentido a las copias de seguridad y la replicación
├─6102 postgres: batman: editor de antecedentes ├─6103 postgres:
batman: recopilador de estadísticas ├─6110 postgres: batman:
postgres postgres [local]
inactivo
├─6158 postgres: batman: walwriter ├─6159 postgres:
batman: autovacuum launcher ├─6160 postgres: batman: archiver ├─6161
postgres: batman: replicación lógica
lanzacohetes
├─6184 postgres: batman: replicador walsender
172.16.15.129(57628) transmisión 0/9000060
└─6192 postgres: batman: replicador walsender
172.16.15.130(47642) transmisión 0/9000060
Okt 25 23:06:20 nodo1 patroni[5680]: 20221025 23:06:20,038 INFO: ninguna acción. Soy (postgresql0)
el líder con el candado Okt 25 23:06:30 node1 patroni[5680]: 20221025 23:06:30,039 INFO: sin
acción. Soy (postgresql0) el líder con el candado Okt 25 23:06:40 node1 patroni[5680]: 20221025
23:06:40,039 INFO: sin acción. Soy (postgresql0) el líder con el candado
...
Cada par de segundos, el líder debería decirnos que, de hecho, es el líder. Una réplica nos dirá
que está siguiendo al líder. Una vez que vea esos mensajes, sabrá que el clúster está funcionando.
Por supuesto, puedes comprobar que todo funciona:
[raíz@nodo1 ~]# patronictl c /etc/patroni/patroni .yml lista
+ Grupo: batman (7023108408162231721) ++
+
| postgresql0 | 172.16.15.128 | Líder | corriendo | 6 | | postgresql1 | 172.16.15.129 | Réplica |
|
corriendo | 6 | | postgresql2 | 172.16.15.130 | Réplica | corriendo | 6 |
0 |
0 |
++++ ++
+
Machine Translated by Google
Configuración de un clúster HA con Patroni 417
patronictl es una excelente herramienta para ver lo que sucede dentro de su clúster. En nuestro ejemplo, todos los
nodos están en funcionamiento.
Sin embargo, patronictl puede hacer mucho más:
[raíz@nodo1 ~]# patronictl ayuda
Uso: patronictl [OPCIONES] COMANDO [ARGS]...
Opciones: c,
configfile TEXTO Archivo de configuración d, dcs TEXTO
Utilice este DCS
k, insecure sin Permitir conexiones a sitios SSL
certificados
ayuda Muestra este mensaje y sal.
Comandos:
configurar Crear archivo de configuración Generar un dsn
dsn para el miembro proporcionado,...
editconfig Editar la configuración del clúster
conmutación por error Conmutación por error a una réplica
enjuagar Descartar eventos programados
historia Mostrar el historial de conmutación por error/conmutación
lista Enumere los miembros de Patroni para un Patroni dado
pausa Deshabilitar la conmutación por error automática
consulta Consultar un miembro Patroni PostgreSQL
reiniciar Reinicializar miembro del clúster
recargar Recargar la configuración de miembros del clúster
eliminar Eliminar clúster de DCS
Reanudar Reiniciar miembro del clúster
reanudar Reanudar conmutación por error automática
andamio Crear una estructura para el clúster en DCS
showconfig Muestra la configuración del clúster
Conmutación Conmutación a una réplica
topología Imprime la topología ASCII para un clúster dado
versión Versión de salida del comando patronictl o ...
patronictl puede realizar todo tipo de operaciones, como conmutación por error. Sin embargo, un
comando especialmente importante es patronictl editconfig. Si usa este comando, Patroni le
permitirá editar la configuración de PostgreSQL y distribuirá automáticamente el cambio dentro del clúster.
Simplemente edite el archivo de configuración y guárdelo. patronictl publicará los datos en la interfaz REST y se
asegurará de que se produzca el cambio.
Machine Translated by Google
418 Dar sentido a las copias de seguridad y la replicación
Uso de IP de servicio
Ahora que implementó con éxito su primer clúster Patroni, es hora de responder una pregunta
realmente importante: ¿cómo sabe el cliente qué nodo está activo? Hay un par de soluciones
a este problema:
• Cadenas de conexión estilo C
• haproxy
• gerente vip
La primera opción permite a las personas enumerar más de un nombre de host dentro de una cadena de conexión.
Muchos idiomas admiten este tipo de cadena de conexión. JDBC y todos los lenguajes basados en C (libpq)
admiten esta función. El cliente debe conocer todos los hosts, y luego la biblioteca del cliente se asegurará de que
obtenga el host correcto.
Sin embargo, el clúster a menudo tiene que ser totalmente transparente. Una forma de asegurarse de eso es usar haproxy.
Esto funciona mediante la conexión haproxy a etcd, obteniendo toda la información y enrutando su conexión a
cualquier nodo que se necesite.
Finalmente, existen soluciones como vipmanager (https://github.com/cybertec postgresql/vipmanager), que
también es de código abierto. La idea aquí es, nuevamente, conectarse a etcd. Sin embargo, en el caso de vip
manager, se moverá una IP de servicio dentro del clúster para que el cliente siempre pueda conectarse al mismo
sistema.
Resumen
En este capítulo, aprendimos sobre las funciones más importantes de la replicación de PostgreSQL, como la
replicación de transmisión y los conflictos de replicación. Luego aprendimos sobre PITR, así como sobre las
ranuras de replicación. Un libro sobre replicación nunca está completo a menos que abarque alrededor de 400
páginas, pero hemos aprendido los puntos clave que todo administrador debe conocer. Ha aprendido a configurar
la replicación y ahora comprende los aspectos más importantes.
El siguiente capítulo, Capítulo 11, Decidir sobre extensiones útiles, trata sobre extensiones útiles para PostgreSQL.
Aprenderemos sobre las extensiones que brindan aún más funcionalidad y han sido ampliamente adoptadas por
la industria.
Machine Translated by Google
Preguntas 419
Preguntas
• ¿Cuál es el propósito de la replicación lógica?
• ¿Cuál es el impacto en el rendimiento de la replicación síncrona?
• ¿Por qué no utilizaría siempre la replicación síncrona?
Las respuestas a estas preguntas se pueden encontrar en el repositorio de GitHub (https://github.com/
PacktPublishing/MasteringPostgreSQL15 ).
Machine Translated by Google
Machine Translated by Google
11
Decidir sobre extensiones útiles
En el Capítulo 10, Dar sentido a las copias de seguridad y la replicación, nuestro enfoque se centró en la replicación, el envío de
registros de transacciones y la decodificación lógica. Después de analizar principalmente temas relacionados con la administración,
el objetivo ahora es apuntar a un tema más amplio. En el mundo de PostgreSQL, muchas cosas se hacen usando extensiones.
La ventaja de las extensiones es que se puede agregar funcionalidad sin sobrecargar el núcleo de PostgreSQL.
Los usuarios pueden elegir entre extensiones de la competencia y encontrar la que más les convenga. La filosofía
es mantener el núcleo delgado, relativamente fácil de mantener y listo para el futuro. La comunidad de desarrollo
de PostgreSQL pone un gran énfasis en la extensibilidad para asegurarse de que el núcleo no esté inflado debido
a las funciones que solo unas pocas personas necesitarán a largo plazo.
En este capítulo, discutiremos algunas de las extensiones más utilizadas para PostgreSQL. Sin embargo, antes de
profundizar en este tema, quiero señalar que es totalmente imposible explicar todas las extensiones. Incluso explicar
todas las extensiones ampliamente utilizadas está mucho más allá del alcance de este capítulo, ya que hay
literalmente cientos de módulos. Simplemente hay tantos módulos en estos días que es imposible cubrirlos
razonablemente todos. Todos los días se publica nueva información y, a veces, es incluso difícil para un profesional
estar al tanto de todo lo que hay. Mientras hablamos, se están publicando nuevas extensiones, por lo que puede
ser una buena idea echar un vistazo a PostgreSQL Extension Network (PGXN) (https://pgxn.org/), que contiene una
gran variedad de extensiones para PostgreSQL.
En este capítulo, cubriremos los siguientes temas:
• Comprender cómo funcionan las extensiones
• Hacer uso de los módulos de contribución
• Otras extensiones útiles
Tenga en cuenta que solo se cubrirán las extensiones más importantes.
Comprender cómo funcionan las extensiones
Antes de profundizar en las extensiones disponibles, es una buena idea echar un vistazo a cómo funcionan las
extensiones en primer lugar. Comprender el funcionamiento interno de la maquinaria de extensión puede ser muy beneficioso.
Machine Translated by Google
422 Decidir sobre extensiones útiles
Echemos un vistazo a la sintaxis primero:
test=# \h CREAR EXTENSIÓN
Comando: CREAR EXTENSIÓN
Descripción: instalar una extensión
Sintaxis:
CREAR EXTENSIÓN [SI NO EXISTE] extension_name
[ CON ] [ ESQUEMA nombre_esquema ]
[ VERSIÓN versión ]
[ CASCADA ]
URL: https://www.postgresql.org/docs/15/sqlcreateextension. html
Cuando desee implementar una extensión, simplemente llame al comando CREAR EXTENSIÓN. Verificará la extensión
y la cargará en su base de datos. Tenga en cuenta que la extensión se cargará en una base de datos y no en toda la
instancia.
Si cargamos una extensión, podemos decidir el esquema que queremos usar. Muchas extensiones se pueden
reubicar para que el usuario pueda elegir qué esquema usar. Entonces, es posible decidir sobre una versión
específica de la extensión. A menudo, no queremos implementar la última versión de una extensión porque el
cliente puede estar ejecutando un software obsoleto. En estos casos, puede ser útil poder implementar
cualquier versión que esté disponible en el sistema.
La cláusula FROM old_version requiere más atención. En los viejos tiempos, PostgreSQL no admitía extensiones, por
lo que aún existe una gran cantidad de código sin empaquetar. Esta opción hace que la cláusula CREATE EXTENSION
ejecute un programa de instalación alternativo que agrega objetos existentes a la extensión en lugar de crear nuevos
objetos. Asegúrese de que la cláusula SCHEMA especifique el esquema que contiene estos objetos preexistentes.
Úselo solo cuando tenga módulos antiguos.
Finalmente, tenemos la cláusula CASCADE. Algunas extensiones dependen de otras extensiones. La opción
CASCADE también implementará automáticamente esos paquetes de software. Lo siguiente es un ejemplo:
test=# CREAR EXTENSIÓN distanciatierra;
ERROR: la extensión requerida "cubo" no está instalada
SUGERENCIA: use CREAR EXTENSIÓN ... CASCADA para instalar también las extensiones requeridas.
El módulo Earthdistance implementa cálculos de distancia de gran círculo. Como ya sabrás, la distancia más corta entre
dos puntos de la Tierra no se puede seguir en línea recta; en cambio, un piloto tiene que ajustar su curso constantemente
para encontrar la ruta más rápida cuando vuela de un punto a otro . La cuestión es que la extensión de la distancia
terrestre depende de la extensión del cubo, lo que te permite realizar operaciones en una esfera.
Machine Translated by Google
Comprender cómo funcionan las extensiones 423
Para implementar automáticamente esta dependencia, se puede usar la cláusula CASCADE, como describimos anteriormente:
test=# CREAR EXTENSIÓN distanciatierra CASCADA;
AVISO: instalar la extensión requerida "cubo"
CREAR EXTENSIÓN
En este caso, se implementarán ambas extensiones, como se muestra en el mensaje AVISO. En la siguiente sección,
descubriremos qué extensión proporciona el sistema.
Comprobación de extensiones disponibles
PostgreSQL ofrece varias vistas para que podamos averiguar qué extensiones hay en el sistema y cuáles están realmente
implementadas. Una de esas vistas es pg_available_extensions:
test=# \d pg_extensiones_disponibles Ver
"pg_catalog.pg_extensiones_disponibles"
Columna | Tipo | Intercalación | Anulable | Por defecto
++++
comentario | |
Este contiene una lista de todas las extensiones disponibles, incluidos sus nombres, sus versiones predeterminadas y las
versiones que están instaladas actualmente. Para que esto sea más fácil para el usuario final, también hay una descripción
disponible que nos brinda más información sobre la extensión.
La siguiente lista contiene dos líneas que se han tomado de pg_available_extensions:
prueba=# \x
La pantalla ampliada está activada.
test=# SELECCIONAR * DESDE pg_disponible_extensiones LÍMITE 2;
[ REGISTRO 1 ]+
nombre |
versión_predeterminada sin acento | 1.1
versión_instalada | | diccionario de
comentario búsqueda de texto que elimina los acentos
[ REGISTRO 2 ]+
nombre | tsm_system_time
versión_predeterminada | 1.0
Machine Translated by Google
424 Decidir sobre extensiones útiles
versión_instalada |
comentario | método TABLESAMPLE que acepta el tiempo en
milisegundos como límite
Como puede ver en el bloque de código anterior, las extensiones earthdistance y plpgsql están habilitadas en mi
base de datos. La extensión plpgsql está ahí por defecto y se agregó earthdistance. La belleza de esta vista es
que puede obtener rápidamente una descripción general de lo que está instalado y lo que puede instalarse.
Sin embargo, en algunos casos, las extensiones están disponibles en más de una versión. Para obtener más información
sobre el control de versiones, consulte la siguiente vista:
test=# \d pg_disponible_extensión_versiones Ver
"pg_catalog.pg_disponible_extensión_versiones"
Columna | Tipo | modificadores
++
nombre | nombre | | texto
versión | | booleano | |
instalado booleano | |
superusuario booleano |
de confianza reubicable |
booleano | | nombre | | nombre[] | | texto |
esquema
requiere
comentario
Las vistas del sistema muestran todo lo que necesita saber sobre una extensión.
Hay disponible información más detallada aquí, como se muestra en el siguiente listado:
test=# SELECT * FROM pg_available_extension_versions LIMIT 1;
[ REGISTRO 1 ]
nombre | es |
versión 1.2
instalado | f superusuario
| t | t reubicable | t
de confianza
esquema
requiere | |
Machine Translated by Google
Comprender cómo funcionan las extensiones 425
comentario | tipos de datos para la numeración internacional de productos
normas
PostgreSQL también le dirá si se puede reubicar una extensión, en qué esquema se ha implementado
y qué otras extensiones se necesitan. Luego, está el comentario que describe la extensión, que se
mostró anteriormente. Tenga en cuenta que si ve una extensión diferente en la lista, no se preocupe.
A menos que se especifique ORDER BY, la salida puede ser aleatoria en caso de que sus entradas de catálogo ya
hayan sido modificadas antes.
Quizás se pregunte dónde encuentra PostgreSQL toda esta información sobre las extensiones en el sistema.
Suponiendo que haya implementado PostgreSQL 15.0 desde el repositorio RPM oficial de
PostgreSQL, el directorio /usr/pgsql15/share/extension contendrá un par de archivos:
...
bash4.3$ ls l citext* rwrr. 1 hs
hs 1028 21. Okt 10:22 citext1.01.1.sql rwrr. 1 hs hs 3424 21. Okt 10:22 citext1.11.2.sql
rwrr. 1 hs hs 850 21. Okt 10:22 citext1.21.3.sql rwrr. 1 hs hs 668 21. Octubre 10:22
citext1.31.4.sql
rwrr. 1 h h 2284 21 de octubre 10:22 citext1.41.5.sql
rwrr. 1 hs hs 13466 21. Okt 10:22 citext1.4.sql rwrr. 1 hs hs 427 21. Octubre
10:22 citext1.51.6.sql
rwrr. 1 hs hs 173 21. oct 10:22 citext.control
...
La versión predeterminada de la extensión citext (texto que no distingue entre mayúsculas y minúsculas) es 1.6, por
lo que hay un archivo llamado citext1.3.sql. Además de eso, ciertos archivos se utilizan para pasar de una versión a
la siguiente (1.0 → 1.1, 1.1 → 1.2, etc.).
Luego, está el archivo .control:
bash4.3$ cat citext.control
# extensión de texto citado
comment = 'tipo de datos para cadenas de caracteres que no distinguen entre
mayúsculas y minúsculas'
default_version = '1.6' module_pathname = '$libdir/citext'
reubicable = verdadero
confiable = verdadero
Machine Translated by Google
426 Decidir sobre extensiones útiles
Este archivo contiene todos los metadatos relacionados con la extensión; la primera entrada contiene el comentario.
Tenga en cuenta que este contenido es lo que se mostrará en las vistas del sistema que acabamos de discutir.
Cuando acceda a esas vistas, PostgreSQL irá a este directorio y leerá todos los archivos .control. Luego, está la
versión predeterminada y la ruta a los archivos binarios.
Si está instalando una extensión típica de RPM, el directorio será $libdir, que se encuentra dentro
de su directorio binario de PostgreSQL. Sin embargo, si ha escrito su propia extensión comercial,
es posible que resida en otro lugar.
La última configuración le dirá a PostgreSQL si la extensión puede residir en cualquier esquema o si tiene que
estar en un esquema fijo y predefinido.
Finalmente, está el archivo sin empaquetar. El siguiente es un extracto de la misma:
...
ALTERAR EXTENSIÓN citext AÑADIR tipo citext;
ALTERAR EXTENSIÓN citext AÑADIR función citextin(cstring);
ALTERAR EXTENSIÓN citext AÑADIR función citextout(citext);
ALTERAR EXTENSIÓN citext AÑADIR función citextrecv(interno);
...
El archivo sin empaquetar convertirá cualquier código existente en una extensión. Por lo tanto, es importante
consolidar el código existente en su base de datos. Después de esta introducción básica a las extensiones en
general, ahora examinaremos varias extensiones adicionales.
Hacer uso de los módulos de contribución
Ahora que hemos echado un vistazo a una introducción teórica a las extensiones, es hora de echar un
vistazo a algunas de las extensiones más importantes. En esta sección, aprenderá sobre los módulos que
se le proporcionan como parte del módulo de contribución de PostgreSQL. Cuando instale PostgreSQL, le
recomiendo que siempre instale estos módulos contrib, ya que contienen extensiones vitales que realmente
pueden facilitarle la vida.
En la próxima sección, se le guiará a través de algunas extensiones que considero las más
interesantes y útiles por una variedad de razones (para depuración, ajuste de rendimiento, etc.).
Usando el módulo del paquete de administración
La idea detrás del módulo adminpack es brindar a los administradores una forma de acceder al sistema
de archivos sin acceso SSH. El paquete contiene un par de funciones para hacer esto posible.
Machine Translated by Google
Haciendo uso de los módulos contrib 427
Para cargar el módulo en la base de datos, ejecute el siguiente comando:
test=# CREAR paquete de administración de EXTENSIÓN;
CREAR EXTENSIÓN
Una de las características más interesantes del módulo adminpack es la capacidad de inspeccionar archivos de
registro. La función pg_logdir_ls verifica el directorio de registro y devuelve una lista de archivos de registro:
test=# SELECT * FROM pg_catalog.pg_logdir_ls() AS (una marca de tiempo, b texto);
ERROR: el parámetro log_filename debe ser igual a 'postgresql%Y %m%d_%H%M%S.log'
Lo importante aquí es que el parámetro log_filename debe ajustarse a las necesidades del módulo adminpack. Si ejecuta
RPM que se han descargado de los repositorios de PostgreSQL, el parámetro log_filename se define como postgresql%a,
que debe cambiarse para evitar errores.
Después de realizar este cambio, se devuelve una lista de nombres de archivo de registro:
prueba=# SELECCIONA * DESDE pg_catalog.pg_logdir_ls()
AS (una marca de tiempo, b texto);
a | b
+
20221015 08:13:30 | registro/postgresql20221015_081330.log 20221015 08:13:23 | registro/
postgresql20221015_081323.log 20221015 08:13:21 | registro/postgresql20221015_081321.log
(3 filas)
Además de estas características, el módulo proporciona algunas funciones adicionales:
test=# SELECCIONA nombrepro
DE pg_proc
DONDE pronombre ~ 'pg_file_.*';
Pronombre
pg_file_write
pg_file_rename
pg_file_unlink pg_file_read
Machine Translated by Google
428 Decidir sobre extensiones útiles
pg_file_length
(5 filas)
Aquí puede leer, escribir, renombrar o simplemente eliminar archivos.
Nota
Por supuesto, estas funciones solo pueden ser llamadas por superusuarios.
La gente se queja de que el módulo no funciona con bastante frecuencia. Considere el siguiente mensaje de error:
prueba=# SELECCIONA * DESDE pg_catalog.pg_logdir_ls()
AS (una marca de tiempo, b texto);
ERROR: el parámetro log_filename debe ser igual a 'postgresql%Y %m%d_%H%M%S.log'
Básicamente, esto significa que el formato de sus archivos de registro no es adecuado para la extensión. Considere cambiarlo.
Aplicar filtros de floración
Desde PostgreSQL 9.6, ha sido posible agregar tipos de índice sobre la marcha usando extensiones. El nuevo
comando CREATE ACCESS METHOD, junto con algunas características adicionales, nos ha permitido crear
sobre la marcha tipos de índices totalmente funcionales y registrados en transacciones.
La extensión bloom proporciona a los usuarios de PostgreSQL filtros bloom, que son prefiltros que nos ayudan a
reducir de manera eficiente la cantidad de datos lo antes posible. La idea detrás de un filtro de floración es que
podemos calcular una máscara de bits y comparar la máscara de bits con la consulta. El filtro de floración puede
producir algunos falsos positivos, pero aun así reducirá drásticamente la cantidad de datos.
Esto es especialmente útil cuando una tabla consta de cientos de columnas y millones de filas. No es posible
indexar cientos de columnas con árboles B, por lo que un filtro de floración es una buena alternativa porque nos
permite indexar todo a la vez.
Para entender cómo funcionan las cosas, instalaremos la extensión:
test=# CREAR EXTENSIÓN bloom;
CREAR EXTENSIÓN
Ahora, necesitamos crear una tabla que contenga varias columnas:
test=# CREAR TABLA t_bloom (
Machine Translated by Google
Haciendo uso de los módulos contrib 429
serie de identificación,
col1 int4 PREDETERMINADO aleatorio() * 1000,
col2 int4 PREDETERMINADO aleatorio() * 1000,
col3 int4 PREDETERMINADO aleatorio() * 1000,
col4 int4 PREDETERMINADO aleatorio() * 1000,
col5 int4 PREDETERMINADO aleatorio() * 1000,
col6 int4 PREDETERMINADO aleatorio() * 1000,
col7 int4 PREDETERMINADO aleatorio() * 1000,
col8 int4 PREDETERMINADO aleatorio() * 1000,
col9 int4 PREDETERMINADO aleatorio() * 1000
);
CREAR MESA
Para hacer esto más fácil, estas columnas tienen un valor predeterminado para que los datos se puedan agregar fácilmente usando un simple
Cláusula SELECT:
test=# INSERTAR EN t_bloom (id)
SELECCIONE * DESDE generar_series(1, 1000000);
INSERTAR 0 1000000
La consulta anterior agrega 1 millón de filas a la tabla. Ahora, la tabla se puede indexar:
test=# CREAR ÍNDICE idx_bloom EN t_bloom
USANDO bloom(col1, col2, col3, col4, col5, col6, col7, col8, col9);
CREAR ÍNDICE
Tenga en cuenta que el índice contiene nueve columnas a la vez. A diferencia de un árbol B, el orden de esas
columnas realmente no marca la diferencia.
Nota
La tabla que acabamos de crear tiene alrededor de 65 MB sin índices.
El índice agrega otros 15 MB al espacio de almacenamiento:
prueba=# \di+ idx_bloom
Lista de relaciones
Esquema | Nombre | Tipo | Propietario | Mesa | Persistencia |
Tamaño | Descripción
Machine Translated by Google
430 Decidir sobre extensiones útiles
+++++ +
+
publico | idx_bloom | índice | hora | t_flor | permanente |
15 MB |
(1 fila)
La belleza del filtro de floración es que es posible buscar cualquier combinación de columnas:
test=# SET max_parallel_workers_per_gather TO 0;
COLOCAR
test=# explicar SELECCIONAR recuento(*)
DESDE t_bloom
DONDE col4 = 454 Y col3 = 354 Y col9 = 423;
PLAN DE CONSULTA
Agregado (costo=20352.02..20352.03 filas=1 ancho=8)
> Escaneo de montón de mapa de bits en t_bloom
(costo=20348.00..20352.02 filas=1 ancho=0)
Vuelva a comprobar Cond: ((col3 = 354) Y (col4 = 454)
Y (col9 = 423))
> Escaneo de índice de mapa de bits en idx_bloom
(costo=0.00..20348.00 filas=1 ancho=0)
Índice Cond: ((col3 = 354) Y (col4 = 454)
Y (col9 = 423))
(5 filas)
Lo que has visto hasta ahora se siente excepcional. Una pregunta natural que podría surgir es, ¿por qué no usar
siempre un filtro de floración? La razón es simple: la base de datos tiene que leer todo el filtro de floración para
poder usarlo. En el caso de, digamos, un árbol B, esto no es necesario.
En el futuro, es probable que se agreguen más tipos de índices para garantizar que se puedan cubrir incluso más casos de
uso con PostgreSQL.
Nota
Si desea leer más sobre los filtros de floración, considere leer nuestra publicación de blog en https://www. cybertec
postgresql.com/en/probandopostgresbloomindexes/.
Machine Translated by Google
Haciendo uso de los módulos contrib 431
Implementación de btree_gist y btree_gin
Hay aún más funciones relacionadas con la indexación que se pueden agregar. En PostgreSQL, tenemos el
concepto de clases de operadores, que analizamos en el Capítulo 3, Uso de índices.
El módulo contrib ofrece dos extensiones (a saber, btree_gist y btree_gin) para que podamos agregar la
funcionalidad Btree a los índices GiST y GIN. ¿Por qué es esto tan útil? Los índices GiST ofrecen varias
características que no son compatibles con los árboles B. Una de esas características es la capacidad de realizar
una búsqueda de kvecino más cercano (KNN) .
¿Por qué es esto relevante? Imagine que alguien está buscando datos que se agregaron ayer, alrededor del
mediodía. Entonces, ¿dónde fue eso? En algunos casos, puede ser difícil establecer límites, por ejemplo, si
alguien busca un producto que cuesta alrededor de 70 euros. KNN puede venir al rescate aquí.
Aquí hay un ejemplo:
test=# CREAR TABLA t_test (id int);
CREAR MESA
Es necesario agregar algunos datos simples:
prueba=# INSERTAR EN t_test SELECCIONAR * DESDE generar_series(1, 100000);
INSERTAR 0 100000
Ahora, la extensión se puede agregar:
test=# CREAR EXTENSIÓN btree_gist;
CREAR EXTENSIÓN
Agregar un índice esencial a la columna es fácil; simplemente use la cláusula USING gist. Tenga en cuenta que agregar
un índice esencial a una columna de enteros solo funciona si la extensión está presente. De lo contrario, PostgreSQL
informará que no existe una clase de operador adecuada:
test=# CREAR ÍNDICE idx_id EN t_test USANDO gist(id);
CREAR ÍNDICE
Una vez desplegado el índice, es posible ordenar por distancia:
prueba=# SELECCIONAR *
DESDE t_test
ORDENAR POR id <> 70
LÍMITE 6;
Machine Translated by Google
432 Decidir sobre extensiones útiles
identificación
70
71
69
68
72
73
(6 filas)
Como puede ver, la primera fila es una coincidencia exacta. Los partidos que siguen ya son menos
precisos y cada vez peores. La consulta siempre devolverá un número fijo de filas.
Lo importante aquí es el plan de ejecución:
test=# explicar SELECCIONAR *
DESDE t_test
ORDENAR POR id <> 70
LÍMITE 6;
PLAN DE CONSULTA
Límite (costo=0.28...0.53 filas=6 ancho=8)
> Escaneo de índice solamente usando idx_id en t_test
(costo=0.28..4196.28 filas=100000 ancho=8)
Ordenar por: (id <> 70)
(3 filas)
Como puede ver, PostgreSQL va directamente a un escaneo de índice, lo que acelera significativamente la consulta.
En versiones futuras de PostgreSQL, es muy probable que los árboles B también admitan búsquedas KNN. Ya se
ha agregado un parche para agregar esta característica a la lista de correo de desarrollo. Tal vez eventualmente
llegue al núcleo. Tener KNN como una función de árbol B eventualmente podría conducir a menos índices GiST
en tipos de datos estándar.
dblink: considerando la eliminación gradual
El deseo de usar enlaces de bases de datos existe desde hace muchos años. Sin embargo, a principios de siglo, los
contenedores de datos externos de PostgreSQL ni siquiera estaban en el horizonte, y definitivamente tampoco se
vislumbraba una implementación de enlace de base de datos tradicional. Por esta época, un desarrollador de PostgreSQL de
Machine Translated by Google
Haciendo uso de los módulos contrib 433
California (Joe Conway) fue pionero en el trabajo sobre conectividad de bases de datos al introducir el concepto de
dblink en PostgreSQL. Si bien dblink sirvió bien a las personas a lo largo de los años, ya no es lo último en tecnología.
Por lo tanto, se recomienda que nos alejemos de dblink a la implementación más moderna de SQL/MED
(que es una especificación que define la forma en que los datos externos se pueden integrar con una
base de datos relacional). La extensión postgres_fdw se creó sobre SQL/MED y ofrece más que solo
conectividad de base de datos, ya que le permite conectarse básicamente a cualquier fuente de datos.
Obteniendo archivos con file_fdw
En algunos casos, puede tener sentido leer un archivo del disco y exponerlo a PostgreSQL como una tabla. Esto es
exactamente lo que puede lograr con la extensión file_fdw. La idea es tener un módulo que te permita leer datos de un
disco y consultarlos usando SQL.
La instalación del módulo funciona como se esperaba:
CREAR EXTENSIÓN file_fdw;
Ahora, necesitamos crear un servidor virtual:
CREAR SERVIDOR file_server
CONTENEDOR DE DATOS EXTRANJEROS file_fdw;
file_server se basa en el contenedor de datos externos de la extensión file_fdw, que le dice a PostgreSQL cómo
acceder al archivo.
Para exponer un archivo como una tabla, use el siguiente comando:
CREAR TABLA EXTRANJERA t_passwd (
texto de nombre de usuario,
Contraseña texto,
fluido En t,
Gid En t,
gecos texto,
directorio texto,
caparazón texto
) SERVIDOR servidor_archivo
OPCIONES (formato 'texto', nombre de archivo '/etc/passwd',
encabezado 'falso', delimitador ':');
En este ejemplo, se expondrá el archivo /etc/passwd. Todos los campos deben enumerarse y los tipos de datos deben
asignarse en consecuencia. Toda la información adicional importante se pasa al
Machine Translated by Google
434 Decidir sobre extensiones útiles
módulo usando OPCIONES. En este ejemplo, PostgreSQL debe conocer el tipo de archivo (texto), el nombre
y la ruta del archivo, así como el delimitador. También es posible decirle a PostgreSQL si hay un encabezado.
Si la configuración es verdadera, la primera línea se omitirá y se considerará sin importancia. Saltarse los
encabezados es especialmente importante si carga un archivo CSV.
Una vez creada la tabla, es posible leer los datos:
SELECCIONE * DESDE t_passwd;
Como era de esperar, PostgreSQL devuelve el contenido de /etc/passwd:
prueba=# \x
La pantalla ampliada está activada.
test=# SELECCIONAR * DESDE t_passwd LIMIT 1;
[ REGISTRO 1 ]
nombre de usuario |
contraseña raíz | x | 0
| 0 | uido
Gid raíz | /raíz | /bin/
gecos bash
directorio
caparazón
Al mirar el plan de ejecución, verá que PostgreSQL usa lo que se conoce como escaneo externo para obtener los datos del
archivo:
prueba = # explicar (detallado verdadero, analizar verdadero) SELECT * FROM t_ passwd;
PLAN DE CONSULTA
Escaneo externo en public.t_passwd (costo = 0.00..2.70 filas = 17
ancho=168)
(tiempo real=0.018...0.090 filas=58 bucles=1)
Salida: nombre de usuario, contraseña, uid, gid, gecos, dir, shell
Archivo externo: /etc/passwd
Extranjero Tamaño del archivo: 3194 b
Tiempo de planificación: 0,066 ms
Tiempo de ejecución: 0,142 ms
(6 filas)
Machine Translated by Google
Hacer uso de los módulos contrib 435
El plan de ejecución también nos informa sobre el tamaño del archivo, etc. Ya que estamos hablando del planificador,
hay una nota al margen que vale la pena mencionar: PostgreSQL incluso obtendrá estadísticas para el archivo. El
planificador verifica el tamaño del archivo y asigna los mismos costos al archivo, tal como lo haría con una tabla
PostgreSQL normal del mismo tamaño.
Inspeccionar el almacenamiento usando pageinspect
Si enfrenta daños en el almacenamiento o algún otro problema relacionado con el almacenamiento que puede estar
relacionado con bloques defectuosos en una tabla, la extensión pageinspect puede ser el módulo que está buscando.
Comenzaremos creando la extensión, como se muestra en el siguiente ejemplo:
test=# CREAR EXTENSIÓN pageinspect;
CREAR EXTENSIÓN
La idea detrás de pageinspect es proporcionarle un módulo que le permita inspeccionar una tabla a nivel binario.
Al usar este módulo, lo más importante que debe hacer es buscar un bloque:
test=# SELECT * FROM get_raw_page('pg_class', 0);
...
Esta función devolverá un solo bloque. En el ejemplo anterior, es el primer bloque del parámetro pg_class, que es
una tabla del sistema. Por supuesto, depende del usuario elegir cualquier otra mesa.
A continuación, puede extraer el encabezado de la página:
prueba=# \x
La pantalla ampliada está activada.
test=# SELECT * FROM page_header(get_raw_page('pg_class', 0));
[ REGISTRO 1 ]
lsn | 0/8A8E310
suma de comprobación
1 | 0 |
más
bajo
banderas
| 212 | 6880 |
superior 8192
de página tamaño
especial | 8192 | 4 ciruela_xid
versión | 0
Machine Translated by Google
436 Decidir sobre extensiones útiles
El encabezado de la página ya contiene mucha información sobre la página. Si desea obtener más
información, puede llamar a la función heap_page_items, que disecciona la página y devuelve una fila por tupla:
prueba=# SELECCIONAR *
DESDE heap_page_items(get_raw_page('pg_class', 0))
LÍMITE 1;
[ REGISTRO 1 ] | 1 lp |
49
lp_off
|
t_xmin
| 2| lp_flags | 0 lp_len
t_campo3
t_xmax
| |
t_ctid
t_infomask
t_infomask2 |
| t_bits | | | t_hoff
t_datos t_oide | ...
También puede dividir los datos en varias tuplas:
test=# SELECT tuple_data_split('pg_class'::regclass, t_data, t_infomask, t_infomask2,
t_bits)
DESDE heap_page_items(get_raw_page('pg_class', 0))
LÍMITE 2;
[ REGISTRO 1 ]+ tuple_data_split |
[ REGISTRO 2 ]+ tuple_data_split | {"\
\x610000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000","\\x98080000","\\x50ac0c00","\\ x00000000","\\x01400000","\
\x00000000","\\x4eac0c00","\\ x00000000","\\xbb01000 0" ","\\x0050c347","\\x00000000","\\
x00000000","\\x01","\\x00","\\x70","\\x72","\\x0100"," \\
Machine Translated by Google
Haciendo uso de los módulos contrib 437
x0000","\\x00","\\x00","\\x00","\\x00","\\x00","\\x00","\\ x00","\\x01" ","\\x64","\\xc3400900","\
\x01000000",NULO,NULO}
Para leer los datos, debemos familiarizarnos con el formato en disco de PostgreSQL. De lo contrario, los datos
pueden parecer bastante oscuros.
pageinspect proporciona funciones para todos los métodos de acceso posibles (tablas, índices, etc.) y nos permite
diseccionar el almacenamiento para que se puedan proporcionar más detalles.
Investigando el almacenamiento en caché con pg_buffercache
Después de esta breve introducción a la extensión pageinspect, centraremos nuestra atención en
la extensión pg_ buffercache, que le permite observar en detalle el contenido de su caché de E/S:
test=# CREAR EXTENSIÓN pg_buffercache;
CREAR EXTENSIÓN
La extensión pg_buffercache le proporciona una vista que contiene los siguientes campos:
test=# \d pg_buffercache Ver
"public.pg_buffercache"
Columna | Tipo | Intercalación | Anulable | Por defecto
+++ +
| | |
| |
El campo bufferid es solo un número; identifica el búfer. Luego, está el campo relfilenode, que apunta al archivo
en el disco. Si queremos buscar a qué tabla pertenece un archivo, podemos consultar el módulo pg_class, que
también contiene un campo llamado relfilenode. Luego, tenemos los campos reldatabase y reltablespace. Tenga
en cuenta que todos los campos están definidos como del tipo oid, por lo que para extraer datos de una manera
más útil, es necesario unir las tablas del sistema.
El campo relforknumber nos dice qué parte de la tabla se almacena en caché. Podría ser el montón, el mapa de
espacio libre o algún otro componente, como el mapa de visibilidad. En el futuro, podemos esperar ver más tipos
de bifurcaciones de relaciones.
Machine Translated by Google
438 Decidir sobre extensiones útiles
El siguiente campo, relblocknumber, nos dice qué bloque se ha almacenado en caché. Finalmente, está el
indicador isdirty, que nos dice que se ha modificado un bloque, además de informarnos sobre el contador de
uso y la cantidad de backends que anclan el bloque.
Si desea comprender la extensión pg_buffercache, es importante agregar información adicional. Para averiguar
qué base de datos usa más el almacenamiento en caché, la siguiente consulta puede ayudar:
prueba = # SELECCIONE nombre de datos,
contar(*),
count(*) FILTRO (DONDE isdirty = true) COMO sucio
DESDE pg_buffercache COMO b, pg_database COMO d
DONDE d.oid = b.reldatabase
GRUPO POR ROLLUP (1);
nombre de datos | contar | sucio
++
a B C | 132 | 1 postgres
| 30 | 0 | 11975 | 53 | 12137 | 54
prueba
(4 filas)
En este caso, se debe unir la extensión pg_database. Como podemos ver, oid es el criterio de unión, lo que
puede no ser obvio para las personas que son nuevas en PostgreSQL.
A veces, es posible que queramos saber qué bloques de la base de datos que están conectados a nosotros están en caché.
Así es como funciona:
test=# SELECCIONA nombrerel,
amable,
contar(*),
count(*) FILTRO (DONDE isdirty = true) COMO sucio
DESDE pg_buffercache COMO b, pg_database COMO d, pg_class COMO
C
DONDE d.oid = b.reldatabase
AND c.relfilenode = b.relfilenode
Y nombre de datos = 'prueba'
GRUPO POR 1, 2
ORDEN POR 3 DESC
LÍMITE 7;
Machine Translated by Google
Hacer uso de los módulos contrib 439
renombrar | relkind| contar| sucio
+++
(7 filas)
En este caso, filtramos la base de datos actual y la unimos con el módulo pg_class, que contiene la lista de
objetos. La columna relkind es especialmente destacable: r se refiere a la tabla (relación) e i se refiere al índice.
Esto nos dice qué objeto estamos mirando.
Cifrado de datos con pgcrypto
Uno de los módulos más potentes de toda la sección de módulos de contribución es pgcrypto. Originalmente fue
escrito por uno de los administradores de sistemas de Skype y ofrece innumerables funciones para que podamos
cifrar y descifrar datos.
Ofrece funciones para cifrado simétrico y asimétrico. Debido a la gran cantidad de funciones disponibles,
definitivamente se recomienda consultar la página de documentación en https://www. postgresql.org/docs/current/
static/pgcrypto.html.
Debido al alcance limitado de este capítulo, es imposible profundizar en todos los detalles del módulo pgcrypto.
Precalentamiento de cachés con pg_prewarm
Cuando PostgreSQL funciona normalmente, intenta almacenar en caché datos importantes. La variable
shared_buffers es importante, ya que define el tamaño de la caché que administra PostgreSQL. El problema
ahora es el siguiente: si reinicia el servidor de la base de datos, se perderá el caché administrado por
PostgreSQL. Tal vez el sistema operativo todavía tenga algunos datos para reducir el impacto en el tiempo de
espera del disco, pero en muchos casos esto no será suficiente. La solución a este problema se llama la
extensión pg_prewarm. Instalemos ahora pg_prewarm y veamos qué podemos hacer con él:
test=# CREAR EXTENSIÓN pg_prewarm;
CREAR EXTENSIÓN
Machine Translated by Google
440 Decidir sobre extensiones útiles
Esta extensión implementa una función que nos permite precalentar explícitamente el caché cuando
sea necesario. El listado muestra las funciones asociadas a esta extensión:
prueba=# \x
La pantalla ampliada está activada.
Prueba=# \df *prewa*
Lista de funciones
[ REGISTRO 1 ]
Esquema | publico |
Nombre autoprecalentar_volcar_ahora |
Tipo de datos de resultado Empezando
Tipos de datos de argumentos | |
Tipo función
[ REGISTRO 2 ]
Esquema | publico |
Nombre autoprewarm_start_worker | vacío
Tipo de datos de resultado
Tipos de datos de argumentos | |
Tipo función
[ REGISTRO 3 ]
Esquema | publico |
Nombre pg_precalentamiento
Tipo de datos de resultado | Empezando
Tipos de datos de argumentos | regclass, modo texto DEFAULT 'buffer'::text,
bifurcación de texto DEFAULT 'main'::text,
primer_bloque bigint DEFAULT NULL::bigint, último_bloque bigint
DEFAULT NULL::bigint | función
Tipo
La forma más fácil y común de llamar a la extensión pg_prewarm es pedirle que almacene en caché un objeto completo:
prueba=# SELECCIONA pg_prewarm('t_test');
pg_precalentamiento
443
(1 fila)
Machine Translated by Google
Haciendo uso de los módulos contrib 441
Nota
Si una tabla es tan grande que no cabe en el caché, solo partes de la tabla permanecerán en el caché, lo que
está bien en la mayoría de los casos.
La función devuelve el número de bloques de 8 KB que fueron procesados por la llamada de función.
Si no desea almacenar en caché todos los bloques de un objeto, también puede seleccionar un rango específico dentro de la tabla.
En el siguiente ejemplo, podemos ver que los bloques 10 a 30 se almacenan en caché en la bifurcación principal:
test=# SELECT pg_prewarm('t_test', 'buffer', 'main', 10, 30);
pg_precalentamiento
21
(1 fila)
Aquí, está claro que se almacenaron en caché 21 bloques.
Inspeccionar el rendimiento con pg_stat_statements
pg_stat_statements es el módulo de contribución más importante que está disponible. Siempre debe estar habilitado y
está ahí para proporcionar datos de rendimiento superiores. Sin el módulo pg_stat_statements , es realmente difícil
rastrear los problemas de rendimiento.
Inspección de almacenamiento con pgstattuple
A veces, puede darse el caso de que las tablas en PostgreSQL crezcan de forma desproporcionada. El
término técnico para las mesas que han crecido demasiado es hinchazón de mesa. La pregunta que surge
ahora es, ¿qué mesas se han inflado y cuánto hay? La extensión pgstattuple nos ayudará a responder esas preguntas:
test=# CREAR EXTENSIÓN pgstattuple;
CREAR EXTENSIÓN
Como dijimos anteriormente, el módulo despliega un par de funciones. En el caso de la extensión
pgstattuple, estas funciones devuelven una fila que consta de un tipo compuesto. Por lo tanto, la función
debe llamarse en la cláusula FROM para garantizar un resultado legible:
prueba=# \x
La pantalla ampliada está activada.
test=# SELECT * FROM pgstattuple('t_test');
[ REGISTRO 1 ]
+
Machine Translated by Google
442 Decidir sobre extensiones útiles
table_len | | 3629056
tuple_len | 7
100000
7.16 tuple_count | 2800000
| 0 | 0 tuple_percent dead_tuple_count
| 16652
dead_tuple_len
espacio_libre dead_tuple_percent | 0
| 0.46 porcentaje_libre
En este ejemplo, la tabla que se usó para la prueba parece estar en bastante buen estado: la tabla tiene un
tamaño de 3,6 MB y no contiene filas muertas. El espacio libre también es limitado. Si el acceso a su tabla se
ralentiza debido a la hinchazón de la tabla, esto significa que la cantidad de filas muertas y la cantidad de
espacio libre habrán crecido de manera desproporcionada. Algo de espacio libre y un puñado de filas muertas
son normales; sin embargo, si la mesa ha crecido tanto que se compone principalmente de filas muertas y
espacio libre, se necesita una acción decisiva para volver a controlar la situación.
La extensión pgstattuple también proporciona una función que podemos usar para inspeccionar índices:
test=# CREAR ÍNDICE idx_id EN t_test (id);
CREAR ÍNDICE
La función pgstattindex devuelve mucha información sobre el índice que queremos inspeccionar:
prueba=# SELECCIONE * DESDE pgstatindex('idx_id');
[ REGISTRO 1 ]
+
versión | 2 | 1
tree_level |
index_size 2260992 | 3 | 1
root_block_no | 274
internal_pages | 0 | 0
leaf_pages | 89.83 |
páginas_vacías 0
páginas_eliminadas
densidad_de_hojas
promedio fragmentación_de_hojas
Nuestro índice es bastante denso (89%). Esta es una buena señal. La configuración predeterminada de FILLFACTOR para un
índice es del 90 %, por lo que un valor cercano al 90 % indica que el índice es muy bueno.
Machine Translated by Google
Haciendo uso de los módulos contrib 443
A veces, no desea consultar una sola tabla; en su lugar, desea verificarlos todos o solo todas las tablas en un
esquema. ¿Cómo se puede lograr esto? Normalmente, la lista de objetos que desea procesar está en la cláusula
FROM. Sin embargo, en mi ejemplo, la función ya está en la cláusula FROM, entonces, ¿cómo podemos hacer
que PostgreSQL haga un bucle sobre una lista de tablas? La respuesta es usar una unión LATERAL.
Tenga en cuenta que pgstattuple tiene que leer todo el objeto. Si nuestra base de datos es grande, puede llevar
bastante tiempo procesarla. Por lo tanto, puede ser una buena idea almacenar los resultados de la consulta que
acabamos de ver para que podamos inspeccionarlos a fondo sin tener que volver a ejecutar la consulta una y otra vez.
Búsqueda aproximada con pg_trgm
El módulo pg_trgm le permite realizar búsquedas aproximadas. Este módulo se analizó en el Capítulo 3, Uso de
índices.
Conexión a servidores remotos usando postgres_fdw
Los datos no siempre están disponibles en una sola ubicación. La mayoría de las veces, los datos se distribuyen por
toda la infraestructura y es posible que los datos que residen en varios lugares deban integrarse.
La solución a este problema es un contenedor de datos externos, tal como lo define el estándar SQL/MED.
En esta sección, discutiremos la extensión postgres_fdw. Es un módulo que nos permite obtener datos dinámicamente
de una fuente de datos de PostgreSQL. Lo primero que debemos hacer es implementar el contenedor de datos
externos:
test=# \h CREAR CONTENEDOR DE DATOS EXTRANJEROS
Comando: CREAR CONTENEDOR DE DATOS EXTRANJEROS
Descripción: define un nuevo envoltorio de datos externos
Sintaxis:
CREAR CONTENEDOR DE DATOS EXTRANJEROS nombre
[ HANDLER función_manejador | SIN MANEJADOR]
[ VALIDADOR validator_function | SIN VALIDADOR]
[ OPCIONES ( opción 'valor' [, ... ] ) ]
URL: https://www.postgresql.org/docs/15/sql createforeigndatawrapper.html
Afortunadamente, el comando CREATE FOREIGN DATA WRAPPER está oculto dentro de una extensión. Se puede
instalar fácilmente usando el proceso normal, de la siguiente manera:
test=# CREAR EXTENSIÓN postgres_fdw;
CREAR EXTENSIÓN
Machine Translated by Google
444 Decidir sobre extensiones útiles
Ahora, se debe definir un servidor virtual. Apuntará al otro host y le dirá a PostgreSQL dónde obtener los
datos. Al final de los datos, PostgreSQL tiene que construir una cadena de conexión completa: los datos del
servidor son lo primero que PostgreSQL debe conocer. La información del usuario se agregará más adelante.
El servidor solo contendrá el host, el puerto, etc. La sintaxis de CREAR SERVIDOR es la siguiente:
prueba=# \h CREAR SERVIDOR
Comando: CREAR SERVIDOR
Descripción: define un nuevo servidor externo
Sintaxis:
CREAR SERVIDOR [SI NO EXISTE] nombre_servidor
[ESCRIBA 'tipo_servidor'] [ VERSIÓN 'versión_servidor' ]
CONTENEDOR DE DATOS EXTRANJEROS
fdw_name [OPCIONES (opción 'valor' [, ... ] ) ]
URL: https://www.postgresql.org/docs/15/sqlcreateserver.html
Para comprender cómo funciona esto, crearemos una segunda base de datos en el mismo host y crearemos un servidor:
[hs@zenbook~]$ cliente creadob
[hs@zenbook~]$ psql cliente
cliente=# CREAR TABLA t_cliente (id int, nombre texto);
CREAR MESA
cliente=# CREATE TABLE t_company (país
texto,
nombre texto,
activo texto
);
CREAR MESA
cliente=# \d
Lista de relaciones
Esquema | Nombre | Tipo | Dueño
+++
| t_cliente | t_empresa | mesa | publico hs publico
| mesa | hora
(2 filas)
Ahora, el servidor debe agregarse a la base de datos de prueba estándar:
test=# CREAR SERVIDOR servidor_cliente
CONTENEDOR DE DATOS EXTRANJEROS postgres_fdw
Machine Translated by Google
Haciendo uso de los módulos contrib 445
OPCIONES (host 'localhost', dbname 'cliente', puerto
'5432');
CREAR SERVIDOR
Tenga en cuenta que toda la información importante se almacena como una cláusula de OPCIONES. Esto es algo
importante porque les da a los usuarios mucha flexibilidad. Hay muchos envoltorios de datos externos diferentes, y cada
uno de ellos necesitará diferentes opciones.
Una vez que se ha definido el servidor, es hora de mapear los usuarios. Si nos conectamos de un servidor a otro, es posible que no
tengamos el mismo usuario en ambas ubicaciones. Por lo tanto, los contenedores de datos externos requieren que los usuarios
definan la asignación de usuarios real de la siguiente manera:
test=# \h CREAR MAPEO DE USUARIO
Dominio: CREAR MAPEO DE USUARIO
Descripción: define una nueva asignación de un usuario a un servidor externo
Sintaxis:
CREAR MAPEO DE USUARIO [SI NO EXISTE]
PARA { nombre_de_usuario | USUARIO | ROL_ACTUAL | USUARIO_ACTUAL | PÚBLICO }
SERVIDOR nombre_servidor
[ OPCIONES ( opción 'valor' [ , ... ] ) ]
URL: https://www.postgresql.org/docs/15/sqlcreateusermapping. html
La sintaxis es bastante simple y se puede usar fácilmente:
test=# CREAR MAPEO DE USUARIO
PARA SERVIDOR DE USUARIO ACTUAL servidor_cliente
OPCIONES (usuario 'hs', contraseña 'abc');
CREAR MAPEO DE USUARIO
Nuevamente, toda la información importante está oculta en la cláusula OPTIONS. Según el tipo de contenedor de datos
externos, la lista de opciones será diferente. Tenga en cuenta que tenemos que usar los datos de usuario adecuados aquí, lo
que funcionará para nuestra configuración. En este caso, simplemente utilizaremos usuarios locales.
Una vez que la infraestructura está en su lugar, podemos crear tablas externas. La sintaxis para crear una tabla
externa es bastante similar a cómo crearíamos una tabla local normal. Se deben enumerar todas las columnas,
incluidos sus tipos de datos:
test=# CREAR TABLA EXTRANJERA f_cliente (id int, nombre texto)
SERVIDOR cliente_servidor
OPCIONES (nombre_esquema 'público', nombre_tabla 't_
Machine Translated by Google
446 Decidir sobre extensiones útiles
cliente');
CREAR TABLA EXTRANJERA
Se enumeran todas las columnas, como en el caso de una cláusula CREATE TABLE normal. Lo especial aquí es que la tabla
externa apunta a una tabla en el lado remoto. El nombre del esquema y el nombre de la tabla deben especificarse en la
cláusula OPTIONS. Una vez creada, la tabla se puede utilizar:
test=# SELECT * FROM f_cliente ; identificación | nombre
+
(0 filas)
Para verificar qué hace PostgreSQL internamente, es una buena idea ejecutar la cláusula EXPLAIN con
el parámetro de análisis. Esto revelará información sobre lo que realmente está pasando en el servidor:
test=# EXPLICAR (analizar verdadero, detallado verdadero)
SELECCIONE * DE f_cliente;
PLAN DE CONSULTA
Escaneo extranjero en public.f_customer
(costo=100.00..150.95 filas=1365 ancho=36)
(tiempo real=0.221...0.221 filas=0 bucles=1)
Salida: id, nombre SQL
remoto: SELECCIONE id, nombre DESDE public.t_customer
Tiempo de planificación: 0,067 ms
Tiempo de ejecución: 0,451 ms
(5 filas)
La parte importante aquí es SQL remoto. El contenedor de datos externos enviará una consulta al otro lado y obtendrá
la menor cantidad de datos posible, ya que se ejecutan tantas restricciones como sea posible en el lado remoto para
garantizar que no se procesen muchos datos localmente. Las condiciones de filtrado, las uniones e incluso los agregados
se pueden realizar de forma remota (a partir de PostgreSQL 10.0).
Si bien la cláusula CREATE FOREIGN TABLE seguramente es algo agradable de usar, puede ser bastante
engorroso enumerar todas esas columnas una y otra vez.
Machine Translated by Google
Haciendo uso de los módulos contrib 447
La solución a este problema es la cláusula IMPORT. Esto nos permite importar rápida y fácilmente esquemas
completos a una base de datos local, así como crear tablas foráneas:
prueba=# \h IMPORTAR
Comando: IMPORTAR ESQUEMA EXTRANJERO
Descripción: importar definiciones de tablas desde un servidor externo
Sintaxis:
IMPORTAR ESQUEMA EXTRANJERO esquema_remoto
[ { LIMITAR A | EXCEPTO } ( nombre_tabla [, ...] ) ]
DESDE SERVIDOR server_name
INTO local_schema
[OPCIONES (opción 'valor' [, ... ] ) ]
URL: https://www.postgresql.org/docs/15/sql importforeignschema.html
IMPORT nos permite vincular grandes conjuntos de tablas fácilmente. También reduce las posibilidades de errores
tipográficos y errores, ya que toda la información se obtiene directamente de la fuente de datos remota.
Así es como funciona:
test=# IMPORTAR ESQUEMA EXTRANJERO público
DEL SERVIDOR servidor_cliente AL público;
ESQUEMA EXTRANJERO IMPORTACION
En este caso, todas las tablas que se crearon previamente en el esquema público se vinculan directamente. Como
podemos ver, todas las tablas remotas ya están disponibles:
prueba=# \det
Lista de mesas extranjeras
Esquema | Mesa | Servidor
++
publico | f_cliente | cliente_servidor público | t_empresa |
cliente_servidor público | t_cliente | servidor_cliente (3 filas)
\det enumera todas las tablas foráneas, como se muestra en el código anterior.
Sin embargo, a veces las cosas salen mal al conectar una tabla externa. ¿Qué se puede hacer si hay un error
tipográfico en la configuración? Averigüémoslo.
Machine Translated by Google
448 Decidir sobre extensiones útiles
Manejo de errores y errores tipográficos
Crear tablas foráneas no es difícil; sin embargo, a veces sucede que las personas cometen errores, o tal
vez simplemente cambian las contraseñas que se han utilizado. Para manejar estos problemas,
PostgreSQL ofrece dos comandos: ALTER SERVER y ALTER USER MAPPING.
ALTER SERVER le permite modificar un servidor. Aquí está su sintaxis:
test=# \h ALTERAR SERVIDOR
Dominio: ALTERAR SERVIDOR
Descripción: cambiar la definición de un servidor externo
Sintaxis:
ALTER SERVER nombre [VERSIÓN 'nueva_versión']
[ OPCIONES ( [ AÑADIR | CONJUNTO | DROP ] opción ['valor'] [, ... ]
) ]
ALTERAR SERVIDOR nombre PROPIETARIO A { nuevo_propietario | ROL_ACTUAL |
USUARIO_ACTUAL | USUARIO_SESIÓN }
ALTER SERVER nombre RENOMBRAR A new_name URL:
https://www.postgresql.org/docs/15/sqlalterserver.html
Podemos usar este comando para agregar y quitar opciones para un servidor específico, lo cual es bueno si hemos
olvidado algo.
Para modificar la información del usuario, también podemos alterar el mapeo del usuario:
test=# \h ALTERAR MAPEO DE USUARIO
Dominio: ALTERAR MAPEO DE USUARIO
Descripción: cambiar la definición de un mapeo de usuario
Sintaxis:
ALTERAR LA ASIGNACIÓN DE USUARIOS PARA { nombre_de_usuario | USUARIO | ROL_ACTUAL |
USUARIO_ACTUAL | SESIÓN_USUARIO | PÚBLICO }
SERVIDOR nombre_servidor
OPCIONES ( [ AÑADIR | CONJUNTO | DROP ] opción ['valor'] [, ... ] )
URL: https://www.postgresql.org/docs/15/sqlalterusermapping. html
La interfaz SQL/MED se mejora continuamente y, en el momento de redactar este documento, se están agregando
funciones. En el futuro, incluso más optimizaciones llegarán al núcleo, lo que hará que la interfaz SQL/MED sea
una buena opción para mejorar la escalabilidad.
Hasta ahora, ha aprendido a usar envoltorios de datos externos. Ahora echemos un vistazo a algunas extensiones
más útiles.
Machine Translated by Google
Otras extensiones útiles 449
Otras extensiones útiles
Las extensiones que hemos descrito hasta ahora forman parte del paquete de contribución de PostgreSQL,
que se envía como parte del código fuente de PostgreSQL. Sin embargo, los paquetes que hemos visto aquí
no son los únicos que están disponibles en la comunidad de PostgreSQL. Muchos más paquetes nos
permiten hacer todo tipo de cosas.
El número de módulos crece día a día, y es imposible cubrirlos todos. Por lo tanto, solo quiero señalar los que me
parecen más importantes.
PostGIS (http://postgis.net/) es la interfaz de base de datos del sistema de información geográfica (GIS) en
el mundo de código abierto. Se ha adoptado en todo el mundo y es un estándar de facto en el mundo de
las bases de datos relacionales de código abierto. Es una solución profesional y extremadamente potente.
Si está buscando enrutamiento geoespacial, pgRouting puede ser justo lo que necesita. Ofrece varios algoritmos que
puede usar para encontrar las mejores conexiones entre ubicaciones y funciona sobre PostgreSQL.
En este capítulo, ya hemos aprendido acerca de la extensión postgres_fdw, que nos permite conectarnos a otras bases
de datos PostgreSQL. Hay muchos más envoltorios de datos extranjeros. Una de las más famosas y profesionales es la
extensión oracle_fdw. Le permite integrarse con Oracle y obtener datos por cable, lo que se puede hacer con la extensión
postgres_fdw.
En algunos casos, también puede estar interesado en probar la estabilidad de su infraestructura con pg_crash (https://
github.com/cybertecpostgresql/pg_crash). La idea es tener un módulo que bloquee constantemente su base de datos.
El módulo pg_crash es una excelente opción para probar y depurar grupos de conexiones y le permite volver a conectarse
a una base de datos que falla. El módulo pg_crash periódicamente causará estragos y eliminará las sesiones de la base
de datos o corromperá la memoria. Es ideal para pruebas a largo plazo.
Resumen
En este capítulo, aprendimos sobre algunos de los módulos más prometedores que se envían con la distribución estándar
de PostgreSQL. Estos módulos son bastante diversos y ofrecen de todo, desde conectividad de base de datos hasta
texto y módulos que no distinguen entre mayúsculas y minúsculas para que podamos inspeccionar el servidor. Sin
embargo, en esta sección, ha aprendido acerca de los módulos más importantes. Esto lo ayudará a implementar
configuraciones de bases de datos aún mayores.
Ahora que nos hemos ocupado de las extensiones, en el próximo capítulo centraremos nuestra atención en la migración.
Allí, aprenderemos cómo podemos pasar a PostgreSQL de la manera más simple.
Machine Translated by Google
Machine Translated by Google
12
Solución de problemas de PostgreSQL
En el Capítulo 11, Decidir sobre extensiones útiles, aprendimos sobre algunas extensiones útiles que se adoptan
ampliamente y que pueden dar un impulso real a su implementación. A modo de seguimiento, ahora se le presentará
la resolución de problemas de PostgreSQL. La idea es brindarle un enfoque sistemático para inspeccionar y reparar
su sistema para mejorar su rendimiento y evitar errores comunes. Tener un enfoque sistemático definitivamente
vale la pena. Muchos usuarios se enfrentan a problemas similares, por lo que tiene sentido echar un vistazo a los
problemas más comunes que acechan a las personas en todo el mundo.
En este capítulo se tratarán los siguientes temas:
• Acercándose a una base de datos desconocida
• Inspección de pg_stat_actividad
• Comprobación de consultas lentas
• Inspeccionar el registro •
Verificar índices faltantes
• Comprobación de memoria y E/S •
Comprensión de escenarios de error notables
Tenga en cuenta que muchas cosas pueden salir mal en la base de datos, por lo que es importante monitorearla
profesionalmente . Para averiguar qué está mal con la configuración de su base de datos, debe examinar el sistema de manera profesional.
Acercándose a una base de datos desconocida
Si administra un sistema a gran escala, es posible que no sepa qué está haciendo realmente el
sistema. Manejar cientos de sistemas implica que no sabrás qué está pasando con cada uno de ellos.
Lo más importante cuando se trata de solucionar problemas se reduce a una sola palabra: datos. Si
no hay suficientes datos, no hay forma de arreglar las cosas. Por lo tanto, el primer paso para la
solución de problemas es configurar siempre una herramienta de monitoreo, como pgwatch2
(disponible en https://www.cybertec postgresql.com/en/products/pgwatch2/), que le brinda información sobre su
Machine Translated by Google
452 Solución de problemas de PostgreSQL
servidor de base de datos. Sin una recopilación de datos adecuada, la solución de problemas es básicamente imposible.
Ten siempre en cuenta que sin datos, no tendrás ni idea.
Una vez que una herramienta de informes le informa sobre una situación que vale la pena verificar, significa que
ha demostrado ser útil para abordar el sistema de manera organizada.
Inspeccionando pg_stat_actividad
En primer lugar, verifiquemos el contenido de pg_stat_statements y respondamos las siguientes preguntas:
• ¿Cuántas consultas simultáneas se están ejecutando actualmente en su sistema?
• ¿Ve tipos similares de consultas que aparecen en la columna de consulta todo el tiempo?
• ¿Ve consultas que se han estado ejecutando durante mucho tiempo?
• ¿Existen bloqueos que no hayan sido otorgados?
• ¿Ve conexiones de hosts sospechosos?
La vista pg_stat_activity siempre debe verificarse primero porque nos dará una idea de lo que está sucediendo
en el sistema. Por supuesto, se supone que el monitoreo gráfico le dará una primera impresión del sistema. Sin
embargo, al final del día, todo se reduce a las consultas que realmente se ejecutan en el servidor. Por lo tanto,
una buena visión general del sistema, como la proporcionada por pg_stat_activity, es más que vital para rastrear
problemas.
Para que sea más fácil para usted, he compilado un par de consultas que me parecen útiles para detectar varios
tipos de problemas lo más rápido posible.
Consultando pg_stat_actividad
La siguiente consulta le muestra cuántas consultas se están ejecutando actualmente en su base de datos:
prueba = # SELECCIONE nombre de datos,
count(*) COMO abierto,
count(*) FILTRO (WHERE estado = 'activo') COMO activo,
cuenta(*) FILTRO (DONDE estado = 'inactivo') COMO inactivo,
cuenta(*) FILTRO (DONDE estado = 'inactivo en transacción')
COMO inactivo_en_trans
DESDE pg_stat_actividad
WHERE backend_type = 'backend del cliente'
GRUPO POR ACUMULACIÓN (1);
Machine Translated by Google
Inspeccionando pg_stat_actividad 453
nombre de datos | abierto | activo | inactivo | inactivo_en_trans
++++
prueba | 2 | | 1 | | 0 | | 1 |
2 1 0 1
(2 filas)
Para mostrar la mayor cantidad de información posible en la misma pantalla, se utilizan agregados parciales. Podemos
ver consultas activas, inactivas e inactivas en transacción. Si podemos ver una gran cantidad de consultas inactivas en
transacciones, definitivamente es importante profundizar para determinar cuánto tiempo se han mantenido abiertas esas
transacciones. La siguiente lista muestra por cuánto tiempo se pueden encontrar transacciones:
test=# SELECCIONA pid, xact_start, now() xact_start AS duración
DESDE pg_stat_actividad
DONDE estado COMO '%transacción%'
ORDEN POR 3 DESC;
pid | xact_start | duración
++
19758 | 20221021 10:33:09.058342+02 | 22:12:10.194363
(1 fila)
La transacción en el listado anterior ha estado abierta por más de 22 horas. La pregunta principal ahora es, ¿cómo
puede una transacción estar abierta por tanto tiempo? En la mayoría de las aplicaciones, una transacción que demora
tanto es altamente sospechosa y potencialmente peligrosa. ¿De dónde viene el peligro? Como aprendimos
anteriormente en este libro, el comando VACUUM solo puede limpiar filas muertas si ninguna transacción puede
verlas más. Ahora bien, si una transacción permanece abierta durante horas o incluso días, el comando VACUUM no
puede producir resultados útiles, lo que conducirá a la sobrecarga de la tabla, y la sobrecarga de la mesa naturalmente
conducirá a un rendimiento increíblemente malo. Por lo tanto, debemos estar atentos al resultado de esta consulta.
pgwatch2 tiene una métrica incorporada para ayudarlo a monitorear estas cosas. Lo mismo es cierto para otras
herramientas de monitoreo de alta calidad .
Por lo tanto, se recomienda encarecidamente asegurarse de que las transacciones largas se controlen o eliminen en caso de que se
vuelvan demasiado largas. Desde la versión 9.6 en adelante, PostgreSQL tiene una función llamada instantánea demasiado antigua,
que nos permite finalizar transacciones largas si las instantáneas están activas durante demasiado tiempo.
También es una buena idea comprobar si se están realizando consultas de larga duración:
test=# SELECT now() query_start AS duración, nombre de datos, consulta
DESDE pg_stat_actividad
DONDE estado = 'activo'
ORDEN POR 1 DESC;
Machine Translated by Google
454 Solución de problemas de PostgreSQL
duración | nombre de datos | consulta
++
00:00:38.814526 | desarrollador | | SELECCIONE pg_sleep (10000);
00:00:00 prueba | SELECCIONE ahora() query_start AS
duración,
nombre de datos, consulta
DESDE pg_stat_actividad
DONDE estado = 'activo'
ORDEN POR 1 DESC;
(2 filas)
En este caso, se inspeccionan todas las consultas activas y las declaraciones calculan cuánto tiempo ha estado
activa cada consulta. A menudo, vemos consultas similares que aparecen en la parte superior, lo que puede darnos
algunas pistas valiosas sobre lo que está sucediendo en nuestro sistema. Si desea finalizar una consulta que ha
estado activa en su sistema, le recomendamos que eche un vistazo a la función pg_terminate_backend, que puede
hacer exactamente eso.
Tratamiento de declaraciones de Hibernate
Muchos mapeadores relacionales de objetos (ORM), como Hibernate, generan declaraciones SQL increíblemente largas.
El problema es este: pg_stat_activity solo almacenará los primeros 1024 bytes de la consulta en la vista
del sistema. El resto está truncado. En el caso de una consulta larga generada por un ORM como
Hibernate, la consulta se corta antes de que comiencen las partes interesantes (como la cláusula FROM, entre otras).
La solución a este problema es establecer un parámetro de configuración en el archivo postgresql.conf de la siguiente manera:
test=# MOSTRAR track_activity_query_size;
seguimiento_actividad_consulta_tamaño
1kB
(1 fila)
Si aumentamos este parámetro a un valor razonablemente alto (quizás 32,768) y reiniciamos PostgreSQL,
podremos ver consultas mucho más largas y detectar problemas más fácilmente.
Averiguar de dónde provienen las consultas
Al inspeccionar pg_stat_activity, ciertos campos nos dirán de dónde proviene una consulta:
dirección_cliente | red |
nombre_host_cliente | texto | | entero |
puerto_cliente
Machine Translated by Google
Comprobación de consultas lentas 455
Estos campos contendrán direcciones IP y nombres de host (si están configurados), pero ¿qué sucede si
cada aplicación envía sus solicitudes desde la misma IP porque, por ejemplo, todas las aplicaciones residen
en el mismo servidor de aplicaciones? Será muy difícil para nosotros ver qué aplicación generó qué consulta.
La solución a este problema es pedir a los desarrolladores que configuren una variable application_name, como
se muestra en el siguiente bloque:
test=# MOSTRAR nombre_aplicación;
Nombre de la aplicación
Psql
(1 fila)
test=# SET application_name TO 'some_name';
COLOCAR
test=# MOSTRAR nombre_aplicación;
Nombre de la aplicación
algún_nombre
(1 fila)
Si los usuarios y los desarrolladores cooperan, la variable application_name aparecerá en la vista del sistema
y hará que sea mucho más fácil ver de dónde proviene una consulta. La variable application_name también
se puede establecer como parte de la cadena de conexión. En la siguiente sección, intentaremos resolver
todo lo relacionado con las consultas lentas.
Comprobación de consultas lentas
Después de inspeccionar pg_stat_activity, tiene sentido echar un vistazo a las consultas lentas y que consumen
mucho tiempo. Básicamente, hay dos formas de abordar este problema:
• Busque consultas lentas individuales en el registro
• Busque tipos de consultas que toman demasiado tiempo
Encontrar consultas únicas y lentas es el enfoque clásico para el ajuste del rendimiento. Al establecer la variable log_min_
duration_statement en un umbral deseado, PostgreSQL comenzará a escribir una línea de registro para cada consulta que
supere este umbral. De forma predeterminada, el registro de consultas lentas está desactivado, de la siguiente manera:
Machine Translated by Google
456 Solución de problemas de PostgreSQL
test=# MOSTRAR log_min_duration_statement;
log_min_duration_statement
1
(1 fila)
Sin embargo, establecer esta variable en un valor razonablemente bueno tiene mucho sentido. Dependiendo de su carga
de trabajo, el tiempo deseado puede, por supuesto, variar.
En muchos casos, el valor deseado puede diferir de una base de datos a otra. Por lo tanto, también es posible
usar la variable de una manera más detallada, como se muestra aquí:
prueba=# ALTERAR BASE DE DATOS prueba
SET log_min_duration_statement A 10000;
ALTERAR BASE DE DATOS
Establecer el parámetro solo para una determinada base de datos tiene mucho sentido si sus bases de datos enfrentan
diferentes cargas de trabajo.
Al usar el registro de consultas lentas, es importante tener en cuenta un factor importante: muchas consultas más
pequeñas pueden generar una carga mayor que solo un puñado de consultas de ejecución lenta. Por supuesto,
siempre tiene sentido estar al tanto de las consultas lentas individuales, pero a veces, esas consultas no son el problema.
Considere el siguiente ejemplo: en su sistema, se ejecutan 1 millón de consultas, cada una de las cuales demora 500
milisegundos, junto con algunas consultas analíticas que se ejecutan durante un par de milisegundos cada una. Claramente,
el problema real nunca aparecerá en el registro de consultas lentas, mientras que cada exportación de datos, cada creación
de índice y cada carga masiva (que de todos modos no se puede evitar en la mayoría de los casos) enviará spam al registro
y nos indicará la dirección equivocada.
Por lo tanto, mi recomendación personal es usar un registro de consultas lento, pero úselo con cuidado y precaución.
Sin embargo, lo más importante es ser consciente de lo que realmente estamos midiendo.
El mejor enfoque, en mi opinión, es trabajar más intensamente con la vista pg_stat_statements. Ofrecerá
información agregada, y no solo información sobre consultas individuales. La vista pg_ stat_statements se analizó
anteriormente en este libro. Sin embargo, su importancia no se puede enfatizar lo suficiente.
Inspeccionar consultas individuales
A veces, se identifican consultas lentas, pero aún no tenemos idea de lo que realmente está sucediendo. El
siguiente paso es, por supuesto, inspeccionar el plan de ejecución de la consulta y ver qué sucede. identificando
Machine Translated by Google
Comprobación de consultas lentas 457
aquellas operaciones clave en el plan que son responsables del mal tiempo de ejecución es bastante simple. Pruebe y use la
siguiente lista de verificación:
• Trate de ver dónde está en el plan que el tiempo comienza a dispararse
• Comprobar si faltan índices (una de las principales razones del mal rendimiento)
• Utilice la cláusula EXPLAIN (búferes verdaderos, análisis verdaderos, etc.) para ver si su consulta usa
demasiados búferes.
• Active el parámetro track_io_timing para averiguar si hay un problema de E/S o un problema de CPU
(verifique explícitamente si hay E/S aleatorias).
• Busque estimaciones incorrectas e intente corregirlas
• Busque procedimientos almacenados que se ejecuten con demasiada frecuencia
• Trate de averiguar si algunos de ellos se pueden marcar como ESTABLES o INMUTABLES, siempre que
Esto es posible
Tenga en cuenta que pg_stat_statements no tiene en cuenta el tiempo de análisis, por lo que si sus consultas son
muy largas (como cadenas de consulta), entonces pg_stat_statements puede ser un poco engañoso.
Profundizando con perf En la mayoría de
los casos, trabajar con esta pequeña lista de verificación lo ayudará a rastrear la mayoría de los problemas de una
manera bastante rápida y eficiente. Sin embargo, incluso la información extraída del motor de la base de datos a
veces no es suficiente.
La herramienta perf es una herramienta de análisis para Linux que le permite ver directamente qué funciones de C
están causando problemas en su sistema. Por lo general, perf no se instala de forma predeterminada, por lo que se
recomienda que lo instale. Para usar perf en su servidor, simplemente inicie sesión en una raíz y ejecute el siguiente comando:
parte superior perforada
La pantalla se actualizará cada dos segundos y tendrás la oportunidad de ver lo que sucede
en vivo. La siguiente lista muestra cómo se vería un punto de referencia estándar de solo lectura:
Muestras: 164 K de eventos 'cycles:ppp', Recuento de eventos (aprox.): 109789128766
Objeto compartido de sobrecarga Símbolo
3.10% postgres [.] AllocSetAlloc
1,99% posgreso 1,51% [.] BuscarCatCache
posgresivo [.] base_yyparse [.]
1.42% postgres hash_search_with_hash_value [.] vfprintf
1.27% libc2.22.so
1.13% libc2.22.so [.] _int_malloc
Machine Translated by Google
458 Solución de problemas de PostgreSQL
0,87 % posgreso 0,74 [.] palo
% posgresivo [.] MemoryContextAllocZeroAligned
0.66% libc2.22.so [.] __strcmp_sse2_unaligned [k]
0,66 % [núcleo] _raw_spin_lock_irqsave [.] _bt_compare [k]
0,66 % postgres 0,63 __fget_light
% [núcleo]
0.62% libc2.22.so [.] estresado
Puede ver que ninguna función toma demasiado tiempo de CPU en nuestra muestra, lo que nos dice que el
sistema está bien.
Sin embargo, esto puede no ser siempre el caso. Hay un problema llamado contención spinlock que es
bastante común. El núcleo de PostgreSQL utiliza spinlocks para sincronizar cosas como el acceso al búfer.
Un spinlock es una función proporcionada por las CPU modernas para evitar interacciones del sistema operativo para
operaciones pequeñas (como incrementar un número). Si cree que puede estar enfrentando una contención de
spinlock, los síntomas son los siguientes:
• Una carga de CPU realmente alta
• Rendimiento increíblemente bajo (las consultas que generalmente toman milisegundos, de repente toman segundos) •
La E/S es inusualmente baja porque la CPU está ocupada intercambiando bloqueos
En muchos casos, la contención de spinlock ocurre repentinamente. Su sistema está bien cuando, de repente, la
carga aumenta y el rendimiento cae como una piedra. El comando perf top revelará que la mayor parte de este
tiempo se dedica a una función C llamada s_lock. Si este es el caso, debe intentar hacer lo siguiente:
páginas_enormes = probar # activar, desactivar o probar
Cambie páginas_grandes de intentar a desactivar. Puede ser una buena idea desactivar por completo las páginas
grandes a nivel del sistema operativo. En general, parece que algunos núcleos son más propensos a producir
este tipo de problemas que otros. La serie Red Hat 2.6.32 parece ser especialmente mala (tenga en cuenta que
he usado la palabra parece aquí).
La herramienta perf también es interesante si usa PostGIS. Si las principales funciones de la lista están todas relacionadas
con GIS (como en alguna biblioteca subyacente), sabrá que el problema probablemente no provenga de un mal ajuste de
PostgreSQL, sino que simplemente esté relacionado con operaciones costosas que toman tiempo para completarse.
Inspeccionando el registro
Si su sistema tiene problemas, tiene sentido inspeccionar el registro para ver qué está pasando. El punto importante es
este: no todas las entradas de registro se crean de la misma manera. PostgreSQL tiene una jerarquía de entradas de
registro que van desde DEBUG hasta PANIC.
Machine Translated by Google
Comprobación de índices faltantes 459
Para el administrador, los siguientes tres niveles de error son de gran importancia:
• ERROR
• FATAL
• PÁNICO
ERROR se usa para problemas como errores de sintaxis, problemas relacionados con permisos y más. Su registro
siempre contendrá mensajes de error. El factor crítico es este: ¿con qué frecuencia aparece cierto tipo de error ?
Producir millones de errores de sintaxis ciertamente no es una estrategia ideal para ejecutar un servidor de base de datos.
FATAL da más miedo que ERROR; verá mensajes como no se pudo asignar memoria para el nombre de la
memoria compartida o el estado inesperado de walreceiver. En otras palabras, estos mensajes de error ya son
realmente aterradores y le dirán que las cosas van mal.
Finalmente, está el PÁNICO. Si te encuentras con este tipo de mensaje, sabes que algo anda muy, muy mal. Los
ejemplos clásicos de PANIC son la corrupción de las tablas de bloqueo o la creación de demasiados semáforos .
Otro ejemplo sería "memoria insuficiente". Esto dará como resultado el cierre del servidor de la base de datos. En
la siguiente sección, aprenderá sobre los índices que faltan.
Comprobación de índices faltantes
Una vez que hayamos terminado con los primeros tres pasos, es importante echar un vistazo al rendimiento en general.
Como he dicho continuamente a lo largo de este libro, los índices faltantes son totalmente responsables del rendimiento de
la base de datos súper bajo, por lo que cada vez que nos enfrentamos a un sistema lento, se recomienda que verifiquemos
los índices faltantes e implementemos lo que sea necesario.
Por lo general, los clientes nos piden que optimicemos el nivel de RAID, ajustemos el kernel o hagamos otras cosas sofisticadas.
En realidad, estas solicitudes complicadas a menudo se reducen a un puñado de índices faltantes. En mi opinión,
siempre tiene sentido dedicar algo de tiempo extra a comprobar si todos los índices deseados están ahí. La
comprobación de los índices que faltan no es difícil ni requiere mucho tiempo, por lo que debe hacerse todo el
tiempo, independientemente del tipo de problema de rendimiento que enfrente.
Esta es mi consulta favorita para obtener una impresión de dónde puede faltar un índice:
SELECCIONE schemanname, relname, seq_scan, seq_tup_read, idx_scan,
seq_tup_read / seq_scan AS avg
DESDE pg_stat_user_tables
DONDE seq_scan > 0
ORDENAR POR seq_tup_read DESC
LÍMITE 20;
Intente encontrar tablas grandes (con un valor promedio alto) que se analicen con frecuencia. Estas tablas normalmente
aparecerán en la parte superior.
Machine Translated by Google
460 Solución de problemas de PostgreSQL
Comprobación de memoria y E/S
Una vez que hayamos encontrado los índices faltantes, podemos inspeccionar la memoria y la E/S. Para averiguar
qué está pasando , tiene sentido activar track_io_timing. Si está activado, PostgreSQL recopilará información
sobre el tiempo de espera del disco y se la presentará.
A menudo, la principal pregunta que se hace un cliente es: si añadimos más discos, ¿será más rápido? Es
posible adivinar lo que sucederá, pero en general, medir es la estrategia mejor y más útil. Habilitar
track_io_timing lo ayudará a recopilar los datos para realmente resolver esto.
PostgreSQL expone el tiempo de espera del disco de varias maneras. Una forma de inspeccionar las cosas es echar
un vistazo a pg_stat_database:
prueba=# \d pg_stat_database
Ver "pg_catalog.pg_stat_database"
Columna | Tipo | modificadores
++
fecha | oid |
nombre de datos nombre | |
...
conflictos | grande | |
temp_bytes Empezando |
...
blk_read_time | precisión doble blk_write_time |
Precisión doble | |
Tenga en cuenta que hay dos campos hacia el final del bloque anterior: blk_read_time y blk_write_time.
Nos informarán sobre la cantidad de tiempo que PostgreSQL ha pasado esperando que el sistema
operativo responda. Tenga en cuenta que aquí no estamos midiendo realmente el tiempo de espera del
disco, sino el tiempo que tarda el sistema operativo en devolver los datos.
Si el sistema operativo produce aciertos en la memoria caché, este tiempo será bastante bajo. Si el sistema operativo tiene
que manejar E/S aleatorias realmente desagradables, veremos que un solo bloque puede tardar incluso un par de milisegundos.
En muchos casos, los valores altos de blk_read_time y blk_write_time ocurren cuando temp_files y temp_bytes
muestran números altos. Además, en muchos casos, esto apunta a una mala configuración de work_mem o una
mala configuración de maintenance_work_mem. Recuerde esto: si PostgreSQL no puede hacer cosas en la
memoria, tiene que volcarse al disco. Puede usar la operación temp_files para detectar esto. Siempre que haya
archivos temporales, existe la posibilidad de un tiempo de espera de disco desagradable.
Machine Translated by Google
Comprobación de memoria y E/S 461
Si bien una vista global a nivel de base de datos tiene sentido, no brinda información detallada sobre la
fuente real del problema. A menudo, solo unas pocas consultas son las culpables del mal rendimiento. La
forma de detectarlos es usar pg_stat_statements, como se muestra en el siguiente bloque:
prueba=# \d pg_stat_statements
Ver "public.pg_stat_statements"
Columna | Tipo | modificadores
++
...
consulta | texto |
llamadas Empezando | |
...
total_exec_time | precisión doble |
...
temp_blks_read | grande | | grande | | precisión
temp_blks_write doble | | precisión doble |
blk_read_time
blk_write_time
Podrá ver, por consulta, si hay una espera de disco. La parte importante es el valor de blk_time en
combinación con total_time. La proporción es lo que cuenta. En general, una consulta que representa
más del 30 % de la espera del disco se puede considerar muy limitada a E/S.
Una vez que hemos revisado las tablas del sistema PostgreSQL, tiene sentido inspeccionar lo que nos
dice el comando vmstat en Linux. Alternativamente, podemos usar el comando iostat:
[hs@computadora ~]$ vmstat 2 ^lprocs
memoria intercambiar io sistema cpu
rb swpd libre buff caché si tan bi bo en cs us sy id wa st
0 0 367088 199488 96 2320388 0 2 83 96 106 156 16 6 78 0 0
0 0 367088 198140 96 2320504 0 0 0 10 595 2624 3 1 96 0 0
0 0 367088 191448 96 2320964 0 0 0 8 920 2957 8 2 90 0 0
Al trabajar con bases de datos, debemos centrar nuestra atención en tres campos: bi, bo y wa. El campo bi
nos informa sobre el número de bloques leídos; 1.000 es equivalente a 1 Mbps. El campo bo se refiere a
bloqueos. Nos informa sobre la cantidad de datos escritos en el disco. En cierto modo, bi y bo son el
rendimiento bruto. No consideraría que un número sea dañino. ¿Qué sucede si un problema tiene un valor wa alto?
Los valores bajos para los campos bi y bo, combinados con un valor alto de wa, nos informan sobre un disco potencial
Machine Translated by Google
462 Solución de problemas de PostgreSQL
cuello de botella, que probablemente esté relacionado con una gran cantidad de E/S aleatorias que tienen lugar en su sistema.
Cuanto mayor sea el valor de wa, más lentas serán sus consultas porque debe esperar en el disco para responder.
Nota importante
Un buen rendimiento sin procesar es bueno, pero a veces también puede señalar un problema. Si se
necesita un alto rendimiento en un sistema de procesamiento de transacciones en línea (OLTP) , puede
indicarle que no hay suficiente RAM para almacenar cosas en caché, o que faltan índices y PostgreSQL
tiene que leer demasiados datos. Tenga en cuenta que las cosas están interconectadas y que los datos
no deben verse de forma aislada.
Comprensión de escenarios de error notables
Después de repasar las pautas básicas para buscar los problemas más comunes que enfrentará en su base de datos,
las próximas secciones discutirán algunos de los escenarios de error más comunes que ocurren en el mundo de
PostgreSQL.
Enfrentando la corrupción de obstrucciones
PostgreSQL tiene algo llamado registro de confirmación (ahora llamado pg_xact; formalmente se conocía como
pg_clog). Esto rastrea el estado de cada transacción en el sistema y ayuda a PostgreSQL a determinar si se puede ver
una fila. En general, una transacción puede estar en cuatro estados:
#define TRANSACTION_STATUS_IN_PROGRESS #define 0x00
TRANSACTION_STATUS_COMMITTED #define 0x01
TRANSACTION_STATUS_ABORTED #define 0x02
TRANSACTION_STATUS_SUB_COMMITTED 0x03
La obstrucción tiene un directorio separado en la instancia de la base de datos PostgreSQL (pg_xact).
En el pasado, las personas informaron algo llamado corrupción de obstrucción, que puede ser causado por discos
defectuosos o errores en PostgreSQL que se han solucionado a lo largo de los años. Un registro de confirmación
corrupto es algo bastante desagradable porque todos nuestros datos están ahí, pero PostgreSQL no sabe si las cosas
siguen siendo válidas. La corrupción en esta área es nada menos que un desastre total.
¿Cómo se da cuenta el administrador de que el registro de confirmación está roto? Esto es lo que normalmente vemos:
ERROR: no se pudo acceder al estado de la transacción 118831
Si PostgreSQL no puede acceder al estado de una transacción, se producirán varios problemas. La pregunta principal
es: ¿cómo se puede solucionar esto? Para aclararlo, no hay forma de solucionar realmente el problema; solo podemos
intentar rescatar la mayor cantidad de datos posible.
Machine Translated by Google
Comprensión de escenarios de error notables 463
Como ya dijimos, el registro de confirmación mantiene 2 bits por transacción. Esto significa que tenemos 4
transacciones por byte, lo que nos deja con 32 768 transacciones por bloque. Una vez que hayamos descubierto
qué bloque es, podemos falsificar el registro de transacciones:
dd if=/dev/zero of=<ubicación del directorio de datos>/pg_xact/0001
bs=256K cuenta=1
Podemos usar dd (un antiguo comando de Unix para E/S de bajo nivel) para falsificar el registro de transacciones y establecer el
estado de confirmación en el valor deseado. La pregunta central es realmente: ¿qué estado de transacción se debe usar? La
respuesta es que cualquier estado es realmente incorrecto porque realmente no sabemos cómo terminaron esas transacciones.
Sin embargo, por lo general, es una buena idea configurarlos como comprometidos para que perdamos menos datos. Realmente
depende de nuestra carga de trabajo y nuestros datos al momento de decidir qué es menos disruptivo.
Cuando tengamos que utilizar esta técnica, debemos fingir el mínimo atascamiento que sea necesario. Recuerde, básicamente
estamos falsificando el estado de confirmación, lo cual no es bueno para un motor de base de datos.
Una vez que hayamos falsificado la obstrucción, debemos crear una copia de seguridad lo más rápido que podamos y recrear la
instancia de la base de datos desde cero. El sistema con el que estamos trabajando ya no es muy confiable, por lo que debemos
intentar extraer los datos lo más rápido que podamos. Tenga esto en cuenta: los datos que estamos a punto de extraer pueden ser
contradictorios y erróneos, por lo que nos aseguraremos de que se impongan algunos controles de calidad a todo lo que podamos
rescatar de nuestro servidor de base de datos.
Comprender los mensajes del punto de control
Los puntos de control son esenciales para la integridad de los datos, así como para el rendimiento. Cuanto más separados estén
los puntos de control, mejor será el rendimiento. En PostgreSQL, la configuración predeterminada suele ser bastante conservadora
y, por lo tanto, los puntos de control son relativamente rápidos. Si se cambia una gran cantidad de datos en el núcleo de la base
de datos al mismo tiempo, PostgreSQL puede decirnos que considera que los puntos de control son demasiado frecuentes. El
archivo LOG mostrará las siguientes entradas:
REGISTRO: los puntos de control ocurren con demasiada frecuencia (2 segundos de diferencia)
REGISTRO: los puntos de control ocurren con demasiada frecuencia (3 segundos de diferencia)
Durante la escritura intensa debido al volcado, la restauración o alguna otra operación importante, PostgreSQL puede notar que
los parámetros de configuración son demasiado bajos. Se envía un mensaje al archivo LOG para decirnos exactamente eso.
Si vemos este tipo de mensaje, se recomienda encarecidamente, por razones de rendimiento, que aumentemos las distancias de
los puntos de control aumentando drásticamente el parámetro max_wal_size (en versiones anteriores, la configuración se llamaba
checkpoint_segments). En versiones recientes de PostgreSQL, la configuración predeterminada ya es mucho mejor de lo que solía
ser. Sin embargo, escribir datos con demasiada frecuencia aún puede suceder fácilmente.
Machine Translated by Google
464 Solución de problemas de PostgreSQL
Cuando vemos un mensaje sobre puntos de control, hay una cosa que debemos tener en cuenta. Hacer puntos de control
con demasiada frecuencia no es peligroso en absoluto, simplemente conduce a un mal rendimiento. Escribir es simplemente
mucho más lento de lo que podría ser, pero nuestros datos no están en peligro. Aumentar lo suficiente la distancia entre
dos puntos de control hará que el error desaparezca y, al mismo tiempo, acelerará nuestra instancia de la base de datos.
Administrar páginas de datos corruptos
PostgreSQL es un sistema de base de datos muy estable. Protege los datos tanto como sea posible y ha demostrado su
valía a lo largo de los años. Sin embargo, PostgreSQL se basa en un hardware sólido y un sistema de archivos que funciona
correctamente. Si el almacenamiento falla, también lo hará PostgreSQL; no hay mucho que podamos hacer al respecto,
además de agregar réplicas para que las cosas sean más seguras.
De vez en cuando, sucede que el sistema de archivos o el disco fallan. En muchos casos, sin embargo, todo no irá
al sur; solo un par de bloques se corrompen por cualquier motivo. Recientemente, hemos visto que esto sucede en
entornos virtuales. Algunas máquinas virtuales no se vacían en el disco de forma predeterminada, lo que significa
que PostgreSQL no puede confiar en que las cosas se escriban en el disco. Este tipo de comportamiento puede
conducir a problemas aleatorios que son difíciles de predecir.
Cuando ya no se puede leer un bloque, es posible que encuentre un mensaje de error como el siguiente:
"no se pudo leer el bloque %u en el archivo "%s": %m"
La consulta que está a punto de ejecutar generará un error y dejará de funcionar. Afortunadamente, PostgreSQL
tiene un medio para lidiar con estas cosas:
test=# SET zero_danged_pages TO encendido;
COLOCAR
test=# MOSTRAR cero_páginas_dañadas;
cero_páginas_dañadas
En
(1 fila)
La variable zero_damaged_pages es una variable de configuración que nos permite tratar con páginas rotas.
En lugar de arrojar un error, PostgreSQL tomará el bloque y simplemente lo llenará con ceros.
Tenga en cuenta que esto definitivamente conducirá a la pérdida de datos, pero recuerde, los datos se rompieron o se perdieron antes
de todos modos, por lo que esta es simplemente una forma de lidiar con la corrupción causada por cosas malas que suceden en nuestro
sistema de almacenamiento.
Nota importante.
Aconsejaría a todos que manejen la variable zero_damaged_pages con cuidado: tenga en cuenta lo que
está haciendo cuando la llama.
Machine Translated by Google
Comprensión de escenarios de error notables 465
Gestión de conexión descuidada
En PostgreSQL, cada conexión de base de datos es un proceso separado. Todos esos procesos se sincronizan usando
memoria compartida (técnicamente, en la mayoría de los casos, es memoria mapeada, pero para este ejemplo, esto no
hace ninguna diferencia). Esta memoria compartida contiene la caché de E/S y una lista de conexiones de bases de datos
activas, bloqueos y otras cosas vitales que permiten que el sistema funcione correctamente.
Cuando se cierra una conexión, eliminará todas las entradas relevantes de la memoria compartida y dejará el
sistema en un estado sano. Sin embargo, ¿qué sucede cuando una conexión de base de datos simplemente falla
por cualquier motivo?
El postmaster (el proceso principal) detectará que falta uno de los procesos secundarios. Luego, se terminarán
todas las demás conexiones y se iniciará un proceso de puesta al día. ¿Por qué es esto necesario? Cuando un
proceso falla, el área de memoria compartida puede ser editada por el proceso. En otras palabras, un proceso que
falla puede corromper la memoria compartida. Por lo tanto, el postmaster reacciona y expulsa a todos los usuarios
antes de que la corrupción se propague por el sistema. Toda la memoria se limpia y todo el mundo tiene que
volver a conectarse.
Desde el punto de vista del usuario final, parece que PostgreSQL se bloqueó y se reinició, lo cual no es el caso.
Dado que un proceso no puede reaccionar a su propio bloqueo (fallo de segmentación) o alguna otra señal, limpiar
todo es absolutamente esencial para proteger sus datos.
Lo mismo sucede si usa el comando kill 9 en una conexión de base de datos. La conexión no puede captar la
señal (9 no puede captarse por definición) y, por lo tanto, el administrador de correo tiene que reaccionar de nuevo.
Inflación de la mesa de lucha
La sobrecarga de tablas es uno de los problemas más importantes cuando se trata de PostgreSQL. Cuando nos
enfrentamos a un mal rendimiento, siempre es una buena idea averiguar si hay objetos que requieren mucho más
espacio del que se supone que deben tener.
¿Cómo podemos averiguar dónde está ocurriendo la hinchazón de la mesa? Consulte la vista pg_stat_user_tables:
test=# \d pg_stat_user_tables Ver
"pg_catalog.pg_stat_user_tables"
Columna | Tipo | modificadores
++
reliquido | oid | |
renombrar nombre |
...
n_live_tup | grande |
n_dead_tup Empezando | |
Machine Translated by Google
466 Solución de problemas de PostgreSQL
Los campos n_live_tup y n_dead_tup nos dan una idea de lo que está pasando, y también podemos usar pgstattuple.
¿Qué podemos hacer si hay una gran hinchazón en la mesa? La primera opción es ejecutar el comando VACUUM FULL.
El problema es que la cláusula VACUUM FULL necesita un bloqueo de tabla. En una tabla grande, esto puede ser un problema
real porque los usuarios no pueden escribir en la tabla mientras se está reescribiendo.
Nota importante Si
usa al menos PostgreSQL 9.6, puede usar una herramienta llamada pg_squeeze. Esto organiza una mesa detrás de
escena sin bloqueos (https://www.cybertecpostgresql.com/en/products/pg_squeeze/ ). Esto es especialmente útil si
está reorganizando una tabla muy grande.
Resumen
En este capítulo, aprendimos cómo abordar sistemáticamente un sistema de base de datos y detectar los problemas más
comunes que enfrentan las personas con PostgreSQL. Aprendimos sobre algunas tablas importantes del sistema, así como
sobre otros factores importantes que pueden determinar si tendremos éxito o fracasaremos. Es especialmente importante
estar atento a la hinchazón de la tabla y a los mensajes de error peligrosos. Tenga en cuenta que, según el tipo de carga de
trabajo, puede enfrentar diferentes desafíos. Los más importantes se han cubierto en este capítulo.
En el capítulo final de este libro, centraremos nuestra atención en la migración a PostgreSQL. Si está utilizando Oracle o algún
otro sistema de base de datos, es posible que desee consultar PostgreSQL. En el Capítulo 13, Migración a PostgreSQL,
discutiremos todo lo relacionado con esto.
Preguntas
Tratemos de probar nuestro conocimiento ahora y echemos un vistazo a algunas de las preguntas más comunes que enfrentan
los usuarios de PostgreSQL:
• ¿Por qué las bases de datos no se administran solas?
• ¿PostgreSQL encuentra corrupción a menudo?
• ¿PostgreSQL requiere atención constante?
Las respuestas a estas preguntas se pueden encontrar en el repositorio de GitHub (https://github.com/PacktPublishing/
MasteringPostgreSQL15 ).
Machine Translated by Google
13
Migrando a PostgreSQL
En el Capítulo 12, Solución de problemas de PostgreSQL, aprendimos cómo abordar los problemas más
comunes relacionados con la solución de problemas de PostgreSQL. Lo importante es tener un enfoque
sistemático para rastrear los problemas, que es exactamente lo que se proporciona aquí.
El último capítulo de este libro trata sobre cómo pasar de otras bases de datos a PostgreSQL. Es posible que
muchos de ustedes todavía sufran el dolor causado por los costos de licencia de bases de datos comerciales. Quiero
darles a todos ustedes una salida y mostrarles cómo se pueden mover los datos de un sistema propietario a PostgreSQL.
Migrar a PostgreSQL tiene sentido no solo desde un punto de vista financiero, sino también si está buscando
funciones y flexibilidad más avanzadas. PostgreSQL tiene mucho que ofrecer y, al momento de escribir este artículo,
se agregan nuevas funciones a diario. Lo mismo se aplica a la cantidad de herramientas que están disponibles para
migrar a PostgreSQL. Las cosas están mejorando cada vez más, y los desarrolladores están publicando más y
mejores herramientas todo el tiempo.
En este capítulo se tratarán los siguientes temas:
• Migración de sentencias SQL a PostgreSQL
• Pasar de Oracle a PostgreSQL
Al final de este capítulo, debería poder mover una base de datos básica de otro sistema a PostgreSQL.
Migración de sentencias SQL a PostgreSQL
Al pasar de una base de datos a PostgreSQL, tiene sentido echar un vistazo y averiguar qué motor de base
de datos proporciona qué tipo de funcionalidad. Mover los datos y la estructura en sí suele ser bastante fácil.
Sin embargo, reescribir SQL podría no serlo. Por lo tanto, decidí incluir una sección que se enfoca
explícitamente en varias funciones avanzadas de SQL y su disponibilidad en los motores de bases de datos actuales.
Machine Translated by Google
468 Migrando a PostgreSQL
Uso de uniones LATERALES
En SQL, una unión LATERAL básicamente puede verse como una especie de bucle. Esto nos permite parametrizar
un join y ejecutar todo lo que está dentro de la cláusula LATERAL más de una vez. Aquí hay un ejemplo simple de esto:
prueba=# SELECCIONAR *
DESDE generar_series(1, 4) COMO x,
LATERAL (SELECCIONA array_agg(y)
DESDE generar_series(1, x) COMO y
) AS z;
x | array_agg
+
1 | {1} |
2 {1,2} |
3 {1,2,3} |
4 {1,2,3,4}
(4 filas)
La cláusula LATERAL se llamará para cada instancia de x. Para el usuario final, es básicamente una especie
de bucle.
Compatibilidad con uniones LATERALES
Una característica importante de SQL son las uniones LATERALES. La siguiente lista muestra qué motores
admiten uniones LATERALES y cuáles no:
• PostgreSQL: Compatible desde PostgreSQL 9.3
• SQLite: no soportado (recientemente se ha publicado una tesis de maestría sobre este tema) •
Db2 LUW: soportado desde la versión 9.1 (2005)
• Oracle: Compatible desde 12c
• Microsoft SQL Server: compatible desde 2005 pero con una sintaxis diferente
Las uniones LATERALES son importantes. Desafortunadamente, este tipo de unión a menudo se subestima
o simplemente no se conoce. Por lo tanto, es muy recomendable profundizar en este tema.
Uso de conjuntos de agrupación
Los conjuntos de agrupación son muy útiles si queremos ejecutar más de un agregado al mismo tiempo. El uso de
conjuntos de agrupación puede acelerar la agregación porque no tenemos que procesar los datos más de una vez.
Machine Translated by Google
Migración de sentencias SQL a PostgreSQL 469
Aquí hay un ejemplo de esto:
prueba=# SELECCIONA x % 2, array_agg(x)
DESDE generar_series(1, 4) COMO x
GRUPO POR ROLLUP (1);
?¿columna? | array_agg
+
0 | {2,4} 1 |
{1,3} | {2,4,1,3}
(3 filas)
PostgreSQL ofrece más que solo la cláusula ROLLUP. También se admiten las cláusulas CUBE y
GROUPING SETS .
Conjuntos de agrupación compatibles
Los conjuntos de agrupación son esenciales para generar más de una agregación en una sola consulta. La
siguiente lista muestra qué motores admiten conjuntos de agrupación y cuáles no:
• PostgreSQL: Compatible desde PostgreSQL 9.5
• SQLite: no compatible
• Db2 LUW: compatible desde al menos 1999
• Oracle: Compatible desde 9iR1 (alrededor de 2000)
• Microsoft SQL Server: Compatible desde 2008
Los conjuntos de agrupación son realmente útiles si se trata de todo tipo de cargas de trabajo analíticas.
Uso de la cláusula WITH: expresiones de tabla comunes
Las expresiones de tabla comunes son una buena manera de ejecutar cosas dentro de una instrucción SQL, pero solo una vez.
PostgreSQL ejecutará todas las cláusulas WITH y nos permitirá usar los resultados a lo largo de la consulta.
Aquí hay un ejemplo simplificado de esto:
test=# CON x COMO (SELECCIONE avg(id)
DESDE generar_series(1, 10) COMO id)
SELECCIONE *, y (SELECCIONE el promedio DE x) COMO diferencia
DESDE generar_series(1, 10) COMO y
DONDE y > (SELECCIONE el promedio DESDE x);
Machine Translated by Google
470 Migrando a PostgreSQL
y | diferencia
+
6 | 0.5000000000000000 7 |
1.5000000000000000 8 |
2.5000000000000000 9 |
3.5000000000000000 10 |
4.5000000000000000
(5 filas)
En este ejemplo, las expresiones de tabla comunes (CTE) de la cláusula WITH calculan el valor promedio
de la serie temporal generada por la función generate_series. La x resultante se puede usar como una tabla
en toda la consulta. En mi ejemplo, x se usa dos veces.
Apoyando la cláusula WITH
La siguiente lista muestra qué motores admiten la cláusula WITH y cuáles no:
• PostgreSQL: Compatible desde PostgreSQL 8.4
• SQLite: Compatible desde 3.8.3
• Db2 LUW: Compatible desde 8 (2000)
• Oracle: Compatible desde 9iR2
• Microsoft SQL Server: Compatible desde 2005
Nota importante
Tenga en cuenta que, en PostgreSQL, un CTE puede incluso admitir escrituras (las cláusulas INSERT,
UPDATE y DELETE). No hay otra base de datos que yo sepa que realmente pueda hacer eso.
Usando la cláusula WITH RECURSIVE
La cláusula WITH viene en dos formas:
• CTE estándar, como se muestra en la sección anterior (usando la cláusula WITH)
• Un método para ejecutar recursiones en SQL
La forma simple de un CTE se cubrió en la sección anterior. En la siguiente sección, se cubrirá la versión
recursiva.
Machine Translated by Google
Migración de sentencias SQL a PostgreSQL 471
Compatibilidad con la cláusula WITH RECURSIVE
La siguiente lista muestra qué motores admiten la cláusula WITH RECURSIVE y cuáles no:
• PostgreSQL: Compatible desde PostgreSQL 8.4
• SQLite: Compatible desde 3.8.3
• Db2 LUW: Compatible desde 7 (2000)
• Oracle: Soportado desde 11gR2 (en Oracle suele ser más común usar el CONNECT
cláusula BY en lugar de la cláusula WITH RECURSIVE)
• Microsoft SQL Server: Compatible desde 2005
Uso de la cláusula FILTER
Al mirar el estándar SQL en sí, notará que la cláusula FILTER existe desde SQL (2003). Sin embargo, no muchos
sistemas admiten este elemento de sintaxis tan útil.
He aquí un ejemplo de esto:
prueba=# SELECCIONA cuenta(*),
cuenta(*) FILTRO (DONDE id < 5),
cuenta(*) FILTRO (DONDE id > 2)
DESDE generar_series(1, 10) COMO id; contar | contar |
contar
++
10 | 4 | 8 (1 fila)
La cláusula FILTER es útil si no se puede usar una condición dentro de una cláusula WHERE normal porque
algún otro agregado necesita los datos.
Antes de la introducción de la cláusula FILTER, se podía lograr lo mismo utilizando una forma de sintaxis más
engorrosa:
SELECCIONE suma (CASO .. ENTONCES 1 MÁS 0 FIN) COMO lo que sea DESDE
CUANDO alguna_tabla;
Machine Translated by Google
472 Migrando a PostgreSQL
Apoyando la cláusula FILTER
La siguiente lista muestra qué motores admiten la cláusula FILTER y cuáles no:
• PostgreSQL: compatible desde PostgreSQL 9.4
• SQLite: no compatible
• Db2 LUW: no compatible
• Oracle: no compatible
• Microsoft SQL Server: no compatible
Uso de funciones de ventana
Las ventanas y el análisis ya se han discutido extensamente en este libro. Por lo tanto, podemos pasar directamente
al cumplimiento de SQL.
Soporte de ventanas y análisis
La siguiente lista muestra qué motores admiten las funciones de Windows y cuáles no:
• PostgreSQL: Compatible desde PostgreSQL 8.4
• SQLite: Desde la versión 3.25.0 (20180915)
• Db2 LUW: Compatible desde la versión 7
• Oracle: Soportado desde la versión 8i
• Microsoft SQL Server: Compatible desde 2005
Nota importante
Algunas otras bases de datos, como Hive, Impala, Spark y NuoDB, también admiten análisis.
Uso de conjuntos ordenados: la cláusula DENTRO DEL GRUPO
Los conjuntos ordenados son bastante nuevos en PostgreSQL. La diferencia entre un conjunto ordenado y un agregado
normal es que, en el caso de un conjunto ordenado, la forma en que se alimentan los datos al agregado marca la diferencia.
Suponga que desea encontrar una tendencia en sus datos: el orden de los datos es relevante.
Aquí hay un ejemplo simple de cómo calcular un valor mediano:
prueba = # SELECCIONE id % 2,
percentile_disc(0.5) DENTRO DEL GRUPO (ORDENAR POR id)
DESDE generar_series (1, 123) COMO id
Machine Translated by Google
Migración de sentencias SQL a PostgreSQL 473
GRUPO POR 1;
?¿columna? | disco_percentil
+
0 | 62 1 |
61
(2 filas)
La mediana solo se puede determinar si hay una entrada ordenada.
Compatibilidad con la cláusula DENTRO DEL GRUPO
La siguiente lista muestra qué motores admiten las funciones de Windows y cuáles no:
• PostgreSQL: compatible desde PostgreSQL 9.4
• SQLite: no compatible
• Db2 LUW: Compatible
• Oracle: Compatible desde la versión 9iR1
• Microsoft SQL Server: compatible, pero la consulta debe remodelarse mediante la función de ventana
Uso de la cláusula TABLESAMPLE
El muestreo de tablas ha sido durante mucho tiempo la verdadera fortaleza de los proveedores de bases de datos comerciales.
Los sistemas de bases de datos tradicionales han proporcionado muestreo durante muchos años. Sin embargo, el monopolio
se ha roto. Desde PostgreSQL 9.5, también tenemos una solución al problema del muestreo.
Así es como funciona:
test=# CREAR TABLA t_test (id int);
CREAR MESA
test=# INSERTAR EN t_test
SELECCIONE * DESDE generar_series(1, 1000000);
INSERTAR 0 1000000
Primero, se crea una tabla que contiene 1 millón de filas. Entonces, las pruebas se pueden ejecutar:
prueba=# SELECCIONA recuento(*), promedio(id)
DESDE t_test TABLESAMPLE BERNOULLI (1); contar |
promedio
+
9802 | 502453.220873291165
Machine Translated by Google
474 Migrando a PostgreSQL
(1 fila)
prueba=# SELECCIONA recuento(*), promedio(id)
DESDE t_test TABLESAMPLE BERNOULLI (1); contar |
promedio
+
10082 | (1 497514.321959928586
fila)
En este ejemplo, la misma prueba se ejecuta dos veces. En cada caso se utiliza una muestra aleatoria del 1%. Ambos valores
medios están bastante cerca de los 5 millones, por lo que el resultado es bastante bueno desde el punto de vista estadístico.
Compatibilidad con la cláusula TABLESAMPLE
La siguiente lista muestra qué motores admiten la cláusula TABLESAMPLE y cuáles no:
• PostgreSQL: compatible desde PostgreSQL 9.5 •
SQLite: no compatible
• Db2 LUW: Compatible desde la versión 8.2
• Oracle: Soportado desde la versión 8
• Microsoft SQL Server: Compatible desde 2005
Uso de límite/compensación
Limitar un resultado en SQL es una historia algo triste. En resumen, cada base de datos hace las cosas de manera diferente.
Aunque en realidad existe un estándar de SQL para limitar los resultados, no todo el mundo es totalmente compatible con la forma
en que se supone que deben ser las cosas. La forma correcta de limitar los datos es usar la siguiente sintaxis:
test=# SELECCIONA * DESDE t_test OBTENER SOLO LAS PRIMERAS 3 FILAS;
identificación
1
2
3
(3 filas)
Si nunca antes ha visto esta sintaxis, no se preocupe. Definitivamente no estás sólo.
Machine Translated by Google
Migración de sentencias SQL a PostgreSQL 475
Compatibilidad con la cláusula FETCH FIRST
La siguiente lista muestra qué motores admiten la cláusula FETCH FIRST y cuáles no:
• PostgreSQL: compatible desde PostgreSQL 8.4 (generalmente, se usa límite/compensación)
• SQLite: Compatible desde la versión 2.1.0
• Db2 LUW: Compatible desde la versión 7
• Oracle: Compatible desde la versión 12c (utiliza subselecciones con la función row_num)
• Microsoft SQL Server: compatible desde 2012 (tradicionalmente, se usa top n)
Como puede ver, limitar los conjuntos de resultados es bastante complicado, y cuando está transfiriendo una base de
datos comercial a PostgreSQL, lo más probable es que se enfrente a alguna sintaxis propietaria.
Uso de la cláusula OFFSET
La cláusula OFFSET es similar a la cláusula FETCH FIRST. Es fácil de usar, pero no ha sido ampliamente adoptado. No
es tan malo como lo es en la cláusula FETCH FIRST, pero aún tiende a ser un problema.
Apoyando la cláusula OFFSET
La siguiente lista muestra qué motores admiten la cláusula OFFSET y cuáles no:
• PostgreSQL: Compatible desde PostgreSQL 6.5
• SQLite: Compatible desde la versión 2.1.0
• Db2 LUW: Compatible desde la versión 11.1
• Oracle: Compatible desde la versión 12c
• Microsoft SQL Server: Compatible desde 2012
Como puede ver, limitar los conjuntos de resultados es bastante complicado, y cuando está transfiriendo una base de
datos comercial a PostgreSQL, es probable que se enfrente a alguna sintaxis propietaria.
Uso de tablas temporales
Algunos motores de base de datos proporcionan tablas temporales para manejar el control de versiones.
Desafortunadamente, no existe el control de versiones listo para usar en PostgreSQL. Por lo tanto, si se muda de Db2 u
Oracle, hay trabajo por delante para migrar la funcionalidad deseada a PostgreSQL. Básicamente, cambiar un poco el
código en el lado de PostgreSQL no es demasiado difícil. Sin embargo, requiere alguna intervención manual: ya no es
un trabajo directo de copiar y pegar.
Machine Translated by Google
476 Migrando a PostgreSQL
Tablas temporales de apoyo
La siguiente lista muestra qué motores admiten tablas temporales y cuáles no:
• PostgreSQL: no compatible
• SQLite: no compatible
• Db2 LUW: Compatible desde la versión 10.1
• Oracle: Soportado desde la versión 12cR1
• Microsoft SQL Server: Compatible desde 2016
Coincidencia de patrones en series de tiempo
En el momento de escribir este artículo, el estándar SQL más reciente (SQL 2016) proporciona una característica diseñada
para encontrar coincidencias en series temporales. Hasta el momento, solo Oracle ha implementado esta funcionalidad en su
última versión del producto.
En este punto, ningún otro proveedor de bases de datos los ha seguido y ha agregado una funcionalidad similar. Si desea
modelar esta tecnología de punta en PostgreSQL, debe trabajar con la función de ventana y las subselecciones. Hacer coincidir
patrones de series de tiempo en Oracle es bastante poderoso; no hay un solo tipo de consulta para lograr esto en PostgreSQL.
Pasando de Oracle a PostgreSQL
Hasta ahora, hemos visto cómo las funciones SQL avanzadas más importantes se pueden portar o utilizar en PostgreSQL.
Dada esta introducción, es hora de echar un vistazo a la migración de los sistemas de bases de datos de Oracle en particular.
En estos días, la migración de Oracle a PostgreSQL se ha vuelto muy popular debido a la nueva licencia y
política comercial de Oracle. En todo el mundo, la gente se está alejando de Oracle y está adoptando PostgreSQL.
Uso de la extensión oracle_fdw para mover datos
Uno de mis métodos preferidos para mover usuarios de Oracle a PostgreSQL es la extensión oracle_fdw
de Laurenz Albe (https://github.com/laurenz/oracle_fdw). Es un contenedor de datos externos (FDW) que
le permite representar una tabla en Oracle como una tabla en PostgreSQL. La extensión oracle_fdw es
una de las FDW más sofisticadas y es sólida como una roca, bien documentada, gratuita y de código abierto.
La instalación de la extensión oracle_fdw requiere que instale la biblioteca de cliente de Oracle. Afortunadamente, ya existen
paquetes RPM que se pueden usar de inmediato (http://www.oracle.com/technetwork/topics/linuxx8664soft092277.html ). La
extensión oracle_fdw necesita el controlador OCI para comunicarse con Oracle. Además de los controladores de cliente Oracle
listos para usar, también hay un paquete RPM para la propia extensión oracle_fdw, que proporciona la comunidad. Si usted
Machine Translated by Google
Migración de Oracle a PostgreSQL 477
no está utilizando un sistema basado en RPM, es posible que deba compilar las cosas por su cuenta, lo que claramente es
posible pero requiere un poco más de mano de obra.
Una vez que se ha instalado el software, se puede habilitar fácilmente:
test=# CREAR EXTENSIÓN oracle_fdw;
La cláusula CREATE EXTENSION carga la extensión en su base de datos deseada. Ahora, se puede crear un
servidor y los usuarios se pueden asignar a sus contrapartes en el lado de Oracle, de la siguiente manera:
test=# CREAR SERVIDOR oraserver ENVOLTORIO DE DATOS EXTERNOS oracle_fdw OPCIONES
(dbserver '//dbserver.example.com/ORADB');
test=# CREAR MAPEO DE USUARIO PARA SERVIDOR postgres oradb
OPCIONES (usuario 'orauser', contraseña 'orapass');
Ahora, es el momento de buscar algunos datos. Mi forma preferida es usar la cláusula IMPORT FOREIGN
SCHEMA para importar las definiciones de datos. La cláusula IMPORT FOREIGN SCHEMA creará una tabla
externa para cada tabla en un esquema remoto y expondrá los datos en el lado de Oracle, que luego se pueden leer fácilmente.
La forma más fácil de utilizar la importación de esquemas es crear esquemas separados en PostgreSQL que solo
contengan el esquema de la base de datos. Luego, los datos se pueden absorber fácilmente en PostgreSQL
usando el FDW. Esto se aplica a casi todos los FDW que admiten la cláusula IMPORT FOREIGN SCHEMA.
Si bien la extensión oracle_fdw hace la mayor parte del trabajo por nosotros, todavía tiene sentido ver cómo se asignan
los tipos de datos. Oracle y PostgreSQL no proporcionan exactamente los mismos tipos de datos, por lo que algunas
asignaciones se realizan mediante la extensión oracle_fdw o manualmente. La siguiente tabla proporciona una
descripción general de cómo se asignan los tipos. La columna de la izquierda muestra los tipos de Oracle, mientras
que la columna de la derecha muestra las posibles contrapartes de PostgreSQL:
Tipos de oráculo Tipos de PostgreSQL
CARBONIZARSE
char, varchar y texto
NCHAR char, varchar y texto
VARCHAR char, varchar y texto
VARCHAR2 char, varchar y texto
NVARCHAR2 char, varchar y texto
CLUB char, varchar y texto
LARGO char, varchar y texto
CRUDO
uuid y bytea
Machine Translated by Google
478 Migrando a PostgreSQL
Tipos de oráculo Tipos de PostgreSQL
GOTA bytea
ARCHIVOB
bytea (solo lectura)
CRUDO LARGO bytea
NÚMERO numérico, float4, float8, char, varchar y texto
NÚMERO(n,m) con m<=0 numérico, float4, float8, int2 e int4,
int8, booleano, char, varchar y texto
FLOTAR numérico, float4, float8, char, varchar y texto
BINARIO_FLOAT numérico, float4, float8, char, varchar y texto
BINARIO_DOBLE numérico, float4, float8, char, varchar y texto
FECHA fecha, marca de tiempo, marca de tiempo, char, varchar
y texto
MARCA DE TIEMPO fecha, marca de tiempo, marca de tiempo, char, varchar
y texto
MARCA DE TIEMPO CON ZONA HORARIA fecha, marca de tiempo, marca de tiempo, char, varchar
y texto
MARCA DE TIEMPO CON ZONA HORARIA LOCAL fecha, marca de tiempo, marca de tiempo, char, varchar y
texto
INTERVALO AÑO A MES intervalo, char, varchar y texto
INTERVALO DÍA A SEGUNDO intervalo, char, varchar y texto
MDSYS.SDO_GEOMETRÍA geometría
Tabla 13.1 – Asignaciones de tipos de datos de Oracle/PostgreSQL
Si desea utilizar geometrías, asegúrese de que PostGIS esté instalado en su servidor de base de datos.
La desventaja de la extensión oracle_fdw es que no puede migrar procedimientos de forma inmediata.
Los procedimientos almacenados son algo especiales y requieren alguna intervención manual.
Machine Translated by Google
Migración de Oracle a PostgreSQL 479
Uso de ora_migrator para una migración rápida
Si bien oracle_fdw es un buen comienzo, podemos hacerlo aún mejor. ora_migrator
(consulte https://www.cybertecpostgresql.com/en/ora_migratormovingfromoracleto
postgresqlevenfaster / y https://github.com/cybertecpostgresql/ ora_migrator) ha sido
desarrollado sobre oracle_fdw y utiliza todas sus características de la manera más
eficiente posible. ¿Como funciona? Una vez que haya instalado ora_migrator desde
nuestra página de GitHub, puede habilitar la extensión usando el siguiente comando:
CREAR EXTENSIÓN ora_migrator;
Una vez que se ha instalado el módulo, tiene sentido echar un vistazo a lo que está haciendo ora_migrator.
Ejecutemos una llamada de muestra e inspeccionemos la salida:
SELECCIONE oracle_migrate(servidor => 'oracle', only_schemas =>
'{LAURENZ,SOCIAL}');
AVISO: creación de esquemas de ensayo "ora_stage" y "pgsql_stage"
...
AVISO: Creación de vistas de metadatos de Oracle en el esquema "ora_stage"
...
AVISO: Copia de definiciones en el esquema de ensayo de PostgreSQL "pgsql_stage"...
AVISO: Creando esquemas...
AVISO: Creando secuencias...
AVISO: Creando tablas foráneas...
AVISO: Migrando la tabla laurenz.log...
...
AVISO: Migrando tabla social.email...
AVISO: Migración de la tabla laurenz.numbers...
AVISO: Creando restricciones UNIQUE y PRIMARY KEY...
ADVERTENCIA: Error al crear clave principal o restricción única en
tabla laurenz.badstring
DETALLE: la relación "laurenz.badstring" no existe:
ADVERTENCIA: Error al crear clave principal o restricción única en
mesa laurenz.hasnul
DETALLE: la relación "laurenz.hasnul" no existe:
AVISO: Creación de restricciones FOREIGN KEY...
AVISO: Creación de restricciones CHECK...
AVISO: Creando índices...
AVISO: Configuración de valores predeterminados de columna...
Machine Translated by Google
480 Migrando a PostgreSQL
AVISO: Eliminación de esquemas de ensayo...
AVISO: Migración completada con 4 errores.
oracle_migrate
(1 fila)
El funcionamiento de ora_migrator es el siguiente:
1. Primero, clona partes del catálogo del sistema Oracle y coloca estos datos en un esquema de preparación dentro
una base de datos PostgreSQL.
2. Luego, esta información se transforma para que podamos usarla en el lado de PostgreSQL para crear tablas,
índices y vistas fácilmente. En esta etapa, realizamos conversiones de tipos de datos, etc.
3. Finalmente, los datos se copian y se aplican índices y restricciones.
Lo que acabas de ver es el caso más simplista. En realidad, es posible que desee realizar un procesamiento
posterior por su cuenta. oracle_migrate es solo una función contenedora y, por lo tanto, también puede llamar a
los pasos individuales necesarios por su cuenta, paso a paso. La documentación muestra qué se puede hacer en
qué nivel, y podrá migrar objetos de forma sencilla.
A diferencia de otras herramientas, ora_migrator no intenta hacer cosas que en realidad son imposibles de
hacer correctamente. Los componentes más importantes que ora_migrator no modifica son los procedimientos.
Básicamente, es imposible convertir de forma completa y automática los procedimientos de Oracle en
procedimientos de PostgreSQL. Por lo tanto, no debemos intentar transformarlos. En resumen, la migración de
procedimientos sigue siendo un proceso parcialmente manual.
ora_migrator se mejora constantemente y funciona para todas las versiones de Oracle, comenzando con la versión 11.2.
Migrator CYBERTEC – migración para los “grandes”
Si está buscando una solución comercial más completa que venga con soporte 24/7 y
mucho más, podemos recomendarle que eche un vistazo a CYBERTEC Migrator, que está
disponible en mi sitio web (https://www.cybertecpostgresql.com /es/productos/cybertecmigrator/).
Viene con paralelismo incorporado, predicción avanzada de tipos de datos, migración sin tiempo de inactividad, reescrituras
automáticas de código y mucho más.
Durante nuestras pruebas, hemos visto velocidades de transferencia de hasta 1,5 GB/segundo, que es la implementación más rápida
que conozco actualmente. Echa un vistazo a mi sitio web para obtener más información.
Machine Translated by Google
Pasando de Oracle a PostgreSQL 481
Uso de Ora2Pg para migrar desde Oracle
Las personas migraron de Oracle a PostgreSQL mucho antes de que existieran los FDW. Los altos costos de las licencias han
afectado a los usuarios durante mucho tiempo, por lo que cambiarse a PostgreSQL ha sido algo natural durante muchos años.
La alternativa a la extensión oracle_fdw es algo llamado Ora2Pg, que existe desde hace
muchos años y se puede descargar de forma gratuita desde https://github.com/darold/Ora2Pg.
Ora2Pg está escrito en Perl y tiene una larga tradición de nuevos lanzamientos.
Las características que proporciona Ora2Pg son impresionantes:
• Migración del esquema completo de la base de datos, incluidas tablas, vistas, secuencias e índices (restricciones únicas, primarias,
de clave externa y de verificación).
• Migración de privilegios para usuarios y grupos.
• Migración de tablas particionadas.
• Capacidad para exportar funciones, activadores, procedimientos, paquetes y cuerpos de paquetes predefinidos.
• Migración de datos completos o parciales (utilizando una cláusula WHERE). •
Soporte completo de objetos BLOB de Oracle como bytea de PostgreSQL.
• Capacidad para exportar vistas de Oracle como tablas de PostgreSQL. •
Capacidad para exportar tipos definidos por el usuario de Oracle.
• Conversión automática básica de código PL/SQL a código PL/pgSQL. Tenga en cuenta que no es posible una conversión
completamente automática de todo. Sin embargo, muchas cosas se pueden transformar automáticamente.
• Capacidad para exportar tablas de Oracle como tablas FDW.
• Capacidad para exportar vistas materializadas.
• Capacidad para mostrar informes detallados sobre el contenido de la base de datos de Oracle.
• Evaluación de la complejidad del proceso de migración de una base de datos Oracle.
• Evaluación de costos de migración de código PL/SQL desde un archivo. •
Habilidad para generar archivos XML para ser usados con Pentaho Data Integrator (Kettle).
• Capacidad para exportar el localizador de Oracle y las geometrías espaciales a PostGIS. •
Capacidad para exportar enlaces de bases de datos como Oracle FDW.
• Capacidad para exportar sinónimos como vistas. •
Capacidad para exportar un directorio como una tabla externa o un directorio para la extensión external_file.
• Capacidad para enviar una lista de órdenes SQL a través de múltiples conexiones PostgreSQL. • Capacidad
para realizar una función de diferenciación entre las bases de datos Oracle y PostgreSQL con fines de prueba.
Machine Translated by Google
482 Migrando a PostgreSQL
Usar Ora2Pg parece difícil a primera vista. Sin embargo, en realidad es mucho más fácil de lo que parece. El
concepto básico es el siguiente:
/usr/local/bin/Ora2Pg c /alguna_ruta/nueva_Ora2Pg.conf
Ora2Pg necesita un archivo de configuración para ejecutarse. El archivo de configuración contiene toda la información necesaria para manejar
el proceso. El archivo de configuración predeterminado ya es realmente bueno y es un buen punto de partida para la mayoría de las migraciones.
En el lenguaje Ora2Pg, una migración es un proyecto.
La configuración impulsará todo el proyecto. Cuando lo ejecute, Ora2Pg creará un par de
directorios con todos los datos extraídos de Oracle:
Ora2Pg project_base /app/migration/ init_project test_ project Creando proyecto
test_project. /aplicación/migración/
proyecto_de_prueba/esquema/
dblinks/
directorios/
funciones/
subvenciones/
mviews/
paquetes/
particiones/
procedimientos/
secuencias/
sinónimos/ tablas/ tablespaces/ disparadores/ tipos/ vistas/
fuentes/
funciones/
mviews/
paquetes/
particiones/
procedimientos/
disparadores/
Machine Translated by Google
Pasando de Oracle a PostgreSQL 483
tipos/
vistas/
data/
config/
reports/
Generando archivo de configuración genérico Creando script
export_schema.sh para automatizar todas las exportaciones.
Creando script import_all.sh para automatizar todas las importaciones.
Como puede ver, se generan scripts que solo se pueden ejecutar. Los datos resultantes se pueden importar
fácilmente a PostgreSQL. Esté preparado para cambiar los procedimientos aquí y allá. No todo se puede
migrar automáticamente, por lo que es necesaria la intervención manual.
Errores comunes
Hay algunos elementos de sintaxis muy básicos que funcionan en Oracle que podrían no funcionar en PostgreSQL.
En esta sección se enumeran algunos de los escollos más importantes a tener en cuenta. Por supuesto, esta
lista no está completa de ninguna manera, pero debería indicarle la dirección correcta.
En Oracle, es posible que haya encontrado la siguiente declaración:
ELIMINAR mitabla;
En PostgreSQL, esta declaración es incorrecta ya que PostgreSQL requiere que use una cláusula FROM en la
declaración DELETE. La buena noticia es que este tipo de declaración es fácil de arreglar.
Lo siguiente que puede encontrar es lo siguiente:
SELECCIONE sysdate DESDE dual;
PostgreSQL no tiene función sysdate ni función dual. La parte de la función dual es fácil de arreglar, ya que
simplemente puede crear una función VIEW que devuelva una línea. En Oracle, la función dual funciona de la
siguiente manera:
SQL> descripción dual
Nombre nulo? Tipo
FICTICIO VARCHAR2(1)
SQL> seleccione * de dual;
D
X
Machine Translated by Google
484 Migrando a PostgreSQL
En PostgreSQL, se puede lograr lo mismo creando la siguiente función VIEW:
CREAR VISTA dual COMO SELECCIONAR 'X' COMO dummy;
La función sysdate también es fácil de arreglar. En PostgreSQL, si SYSDATE se usa en SQL, entonces
tenemos que usar statement_timestamp(), y si SYSDATE se usa en PL/PGSQL, entonces tenemos que usar
clock_timestamp().
Es decir, SYSDATE se comporta como la función STABLE de PostgreSQL cuando la usamos en una instrucción SQL.
Otro problema común es la falta de tipos de datos, como VARCHAR2, así como la falta de funciones especiales
que solo son compatibles con Oracle. Una buena manera de solucionar estos problemas es instalar la extensión
orafce, que proporciona la mayor parte de la información que normalmente se necesita, incluidas las funciones
más utilizadas. Sin duda, tiene sentido visitar https://github.com/orafce/orafce para obtener más información
sobre la extensión orafce. Ha existido durante muchos años y es una pieza sólida de software.
Un estudio reciente ha demostrado que la extensión orafce ayuda a garantizar que el 73 % de todos los SQL de
Oracle se puedan ejecutar en PostgreSQL sin modificaciones si la extensión orafce está disponible (realizada por NTT).
Uno de los errores más comunes es la forma en que Oracle maneja las uniones externas. Considere el siguiente ejemplo:
SELECCIONE employee_id, manager_id
DE empleados
DONDE empleados.manager_id(+) = empleados.employee_id;
PostgreSQL no proporciona este tipo de sintaxis y nunca lo hará. Por lo tanto, la combinación debe reescribirse
como una combinación externa adecuada. El signo + es muy específico de Oracle y debe eliminarse.
En este capítulo, ya aprendió algunas lecciones valiosas sobre cómo pasar de bases de datos como Oracle a
PostgreSQL. Migrar los sistemas de bases de datos MySQL y MariaDB a PostgreSQL es bastante fácil. La
razón de esto es que Oracle puede ser costoso y un poco engorroso de vez en cuando. Lo mismo se aplica a
Informix. Sin embargo, tanto Informix como Oracle tienen una cosa importante en común: las restricciones
CHECK se respetan correctamente y los tipos de datos se manejan correctamente. En general, podemos
asumir con seguridad que los datos en esos sistemas comerciales son en gran parte correctos y no violan las
reglas más básicas de integridad de datos y sentido común.
Nuestro próximo candidato es diferente. Muchas cosas que sabe acerca de las bases de datos comerciales no son
ciertas en MySQL. El término NOT NULL no significa mucho para MySQL (a menos que use explícitamente el modo estricto).
En Oracle, Informix, Db2 y todos los demás sistemas que conozco, NOT NULL es una ley que se obedece en todas las
circunstancias. MySQL no toma estas restricciones tan en serio por defecto. (Aunque, para ser justos, esto ha cambiado
en versiones recientes. El modo estricto no estaba activado de forma predeterminada hasta hace muy poco tiempo. Sin
embargo, muchas bases de datos antiguas aún usan la configuración predeterminada anterior).
Machine Translated by Google
Resumen 485
En el caso de las migraciones, esto provoca una serie de problemas. ¿Qué vas a hacer con los datos que
son técnicamente incorrectos? Si su columna NOT NULL de repente revela innumerables entradas NULL,
¿cómo va a manejar eso? MySQL no solo inserta valores NULL en columnas NOT NULL. Insertará una
cadena vacía o 0 según el tipo de datos, por lo que las cosas pueden ponerse bastante feas.
Resumen
En este capítulo, entendimos cómo migrar declaraciones SQL a PostgreSQL y aprendimos a mover una base de
datos básica de otro sistema a PostgreSQL. La migración es un tema importante y cada vez más personas
adoptan PostgreSQL todos los días.
PostgreSQL 15 tiene muchas funciones nuevas, como particiones integradas mejoradas y mucho más. En el
futuro, veremos muchos más desarrollos en todas las áreas de PostgreSQL, especialmente aquellos que permitan
a los usuarios escalar más y ejecutar consultas aún más rápido. Todavía estamos por ver qué nos depara el futuro.
Machine Translated by Google
Machine Translated by Google
Índice
configurar, realizar 377379 configurar,
simbolos mejorar la seguridad 379 cronogramas
.pgpass 384
usando 350, 351 autovacío
configurando 41
A
extensiones disponibles
comprobación 423 426
bloque de tiempo real 210
módulo adminpack usando
426428
B
bloqueos de aviso copias de
utilizando 38, 39 seguridad de contraseñas y conexión
eficiencia de información, pasar 349 realizar
agregados , mejorando 142144 347, 348 realizar, métodos
agregados hipotéticos, escribiendo 144146 soporte, 347 pg_dump, ejecutar 348, 349
agregando para consultas paralelas 141, 142 escribiendo reproducir 355 subconjuntos de datos,
137 bloques de extraer 352 copias
código anónimo usando 263, 264 de seguridad, contraseñas e información
verificación de de conexión
replicación asincrónica , para
asegurar disponibilidad 380383 conflictos, administrar .pgpass, usando 350, 351
385, 386 conmutación por error, realizar variables de entorno, usando 349, 350 archivos de
383 detener 380 hacer, confiable servicio, usando 351, 352 copias de
387 reanudar seguridad base
380 configurar 376 creando 366, 367
copias de seguridad base, archivo de
registros de transacciones , pruebas 369371
Machine Translated by Google
488 Índice
objetivos de respaldo, definir 368 escaneos de solo índice, usando 67, 68
ancho de banda, reducir 367 usados, para mejorar la velocidad 6266
formatos diferentes, usar 368 manejo de seguridad a nivel
pg_basebackup, rastrear 369 de columna 332334
tablespaces, mapear 368 expresiones de tabla comunes (CTE) 470 conexiones
mecanismos básicos de bloqueo 2426 312, 313 inspección 314
replicación bidireccional (BDR) 397 enlazar plegado constante
direcciones 312, 313 escaneos 202, 203 contiene operador 104,
de mapa de bits 106 gestión de contenido system
usando, efectivamente 59 (CMS) 24, 379 módulos de contribución
Índices de rango de bloques (BRIN) índices 86, 87 que
extienden 87, 88 filtros módulo adminpack, usando 426428 filtros
de floración bloom, aplicando 428430 btree_gin,
aplicando 428430 implementando 431, 432 btree_gist,
enlace de referencia 430 implementando 431, 432 cachés
btree_gin precalentando con pg_prewarm
extensión 439441
implementando 431, 432
btree_gist datos, cifrado con pgcrypto 439 dblink 432
implementando 431, 432
Índices de árbol B archivos, obteniendo con file_fdw
extensión 433435
datos ordenados, usando 56, 57
Características del árbol B 68 búsqueda difusa, con rendimiento del módulo pg_trgm
índices combinados 68 443, inspección con pg_stat_statements
datos, agregar al indexar 72 índices 441 extensión pg_buffercache,
funcionales, agregar 70 consumo de que se usa para investigar el almacenamiento
espacio, reducir 70, 71 en caché 437439 extensión
postgres_fdw, que se usa para conectarse
C a servidores remotos 443447
almacenamiento, inspección con pageinspect
CHECK cláusulas 339 extensión 435437
mensajes de punto de control 463 almacenamiento, inspección con pgstattuple
puntos de control 361 extensión 441443
gestión de utilizando el
limpieza 3941 sistema de archivos 426 copyonwrite (COW) 363
corrupción de obstrucción bloque de costos 210
462 frente a 462, 463 modelo de costo 50, 51
tablas agrupadas índices de cobertura 68
Comando CLUSTER, usando 66, 67
Machine Translated by Google
Índice 489
Comando CREAR PUBLICACIÓN Python 2, desaprobando el
usando 397400 soporte 2, eliminando de pg_dump 1, 2 usando 1
CREAR ROL
versus CREAR USUARIO 324 esperar eventos 4
Comando CREAR SUSCRIPCIÓN parpadeo 432
usando 397400 interbloqueos
correlación entre columnas 214 observar 3638
CUBO palabra clave 110, 111 privilegios
Migrador CYBERTEC 480 predeterminados configurar
Servicios PostgreSQL de CYBERTEC 334, 335 función dense_rank() 129
URL 342 características relacionadas con el desarrollador
UCI locales 7, 8
D Comando MERGE, agregando a
PostgreSQL 10, 11
datos NULL, trabajando con 9 tipos
ordenar, dentro de la ventana 119121 de datos numéricos 8
particionar 118, 119 ON DELETE SET NULL, manejando 9 vistas
seguridad a nivel de base de del invocador de seguridad 7
datos definir 326328 ÚNICO trabajando con 9 campo
permisos 326 de nombre distinguido (DN) 175 dólar cotizando
Lenguaje de definición de datos (DDL) 325 262, 263
particionar datos 226 Cláusula
DROP TABLE, usar 232, 233 particiones hash,
mi
manejar 238, 239 estructuras heredadas,
modificar 231 tablas heredadas, crear 226228 Variables de entorno
particiones de lista, usar 236238 usando 349, 350
PostgreSQL 15.x particionar 233 ERROR 459
particiones de rango, usar 234236 escenarios de error
estrategias, manejar 233, 234 mensajes de punto de control
restricciones de tablas, aplicar 463 corrupción de obstrucción, frente a
229231 tablas, trabajar con 232 462, 463 gestión de conexiones 465
páginas de datos corruptos, gestión 464
Funciones relacionadas con DBA hinchamiento de tablas, solución 465, 466
Funcionalidad de registro JSON, agregando 46 EXCLUIR GRUPO 127
permisos, agregando a variables 3 EXCLUIR LAZOS 126, 127
pg_stat_statements, mejorando 3, 4 usuarios ventajas de los operadores
predefinidos, agregando 2, 3 esquema de exclusión, tomando 98, 99
público, arreglando 2
Machine Translated by Google
490 Índice
planes de ejecución 209 mapa de espacio libre (FSM) 40
acercándose, sistemáticamente 209, 210 búsquedas de texto completo 94
Cláusula EXPLAIN, usar 211 problemas, depuración 9698
detectar 211 planes de ejecución, Índices GIN, definición 95, 96 cadenas,
problemas con el uso del búfer, comparación 95 toma, ventaja
inspeccionar 214216 cambios en el tiempo de operadores de exclusión 98, 99
de ejecución, detectar 212 estimaciones, estadísticas de
inspeccionar 212214 uso alto del búfer, palabras, recopilación 98
arreglar 216, 217 llamadas de función
comando EXPLICAR planes en caché, usando el
etapas 52 número 306, reduciendo 304, 305
usando 51, 52 función en línea 203205 funciones
extensiones 261
trabajando 421, 423 anatomía 261, 262
bloques de código anónimo, usando 263, 264 costos,
asignando a 306 cotización en
F
dólares 262, 263 fundamentos
FATAL 459 261
Cláusula FETCH FIRST rendimiento, mejora 304 tipos 305
motores compatibles 475 usando 264,
extensión file_fdw usando, 265 usando, para
para recuperar archivos 433435 varios propósitos 307309 búsqueda difusa
formatos de archivo
manipulación 352354 consultas LIKE, acelerando 92, 93 ejecutando
Cláusula FILTRO 90 expresiones
soporte, motores 472 usados, regulares, manejando 93 tomando, ventaja
para combinar conjuntos de agrupación 113 usando de pg_trgm 9092
471 función
first_value() 135 envoltura de datos
GRAMO
foráneos (FDW) 476 exploración foránea 434
errores de tablas Índices invertidos generalizados (GIN) 84 que definen 95,
foráneas, manejo 96 que extienden 84,
448 errores tipográficos, manejo 85
448 Árbol de búsqueda generalizado (GiST) índices 81 que se
PARA COMPARTIR extienden 8284
usando 33 casos de uso 81
PARA ACTUALIZAR trabajando con 81, 82
usando 3033
Machine Translated by Google
Índice 491
Solicitud de Servicio de Seguridad Genérica usuarios, creando 323326
Interfaz de programa (GSSAPI) 318 usuarios, modificando 323326
Optimización de consultas genéticas (GEQO) 225 Direcciones de Protocolo de Internet (IP) 312
optimizador de consultas genéticas 225, E/S
226 sistema de información geográfica (GIS) 449 buscando 460, 461
manejo de
datos globales 356
Cláusula GRANT
j
usando 332 join_collapse_limit variable 220, 221 poda de
conjuntos de agrupación unión 205207 uniones
107 aplicando 217219 uniones
109111 combinando, con FILTRO cláusula externas, procesamiento 219
113 rendimiento, investigando 111113 JSON
motores de apoyo 469 trabajando con 150
usando 468 JSONB
trabajando con 150
H documento JSON
acceder 153156 crear
clúster de alta disponibilidad
150152 mostrar
configuración, con índices hash 150152 convertir, en
Patroni 400 81 filas 152, 153 compilación justo
combinación a tiempo (JIT) 253 configurar consultas JIT
hash 195 manejo 254, ejecutar 255257
de particiones hash
238, 239 agregados hipotéticos 116, 117
escritura 144146
k
kvecino más cercano (KNN) 431
I
funciones inmutables 305
deduplicación de índices 62
L
índices 50, 51 función lag() 132134 función
implementación 55, last_value() 135
56 usando LATERAL se une a
5961 escaneos de los motores de soporte 468
solo índice usando la
usando 67, 68 manejo de función 468 lead() 132134
seguridad a nivel de instancia 321323
Machine Translated by Google
492 Índice
Acceso ligero a directorios Concurrencia de múltiples versiones
Protocolo (LDAP) 318 enlace Control (MVCC) 25
de referencia, para autenticación 318
Consultas LIKE
norte
acelerando 92, 93 límite/
compensación bucle anidado 195
usando la direcciones de enlace
configuración 474 de seguridad de red 311, 312
listen_addresses , permisos de nivel de columna 312
correctamente 313 lista de particiones seguridad de nivel de columna, manejo 332334
utilizando 236238 conexiones 312
bloqueando 17 permisos a nivel de base de datos 311
seguridad a nivel de base de datos, definición 326328
registros inspeccionando privilegios predeterminados, configuración 334, 335
458 control de acceso basado en host 311
archivos de permisos a nivel de instancia 311
registro creando 184 archivos postgresql.conf, seguridad a nivel de instancia, manejo 321323 manejo
configurando 184 ranuras de 311, 312 archivo
replicación lógica manejando 394397 pg_hba.conf, manejo 316318
casos de uso 397 RLS 312
máquina virtual de bajo nivel (LLVM) 255 permisos de nivel de esquema 312
permisos de nivel de esquema, ajuste 328 permisos
de nivel de tabla 312 tablas,
METRO
trabajando con 331, 332
max_connections en rendimiento función nth_value() 135 función
enlace de referencia 314 ntile() 130, 131
max_wal_senders variable 364
max_wal_size variable 362 mediana
O
114
combinar unir 195, 196 mapeo relacional de objetos (ORM) 205
función minvfunc 143 Asignadores relacionales de objetos (ORM) 454
índices faltantes
comprobando 459461 reasignación de objetos 344, 345
función msfunc 143 Cláusula de compensación
múltiples índices motores compatibles 475
usando 5759 usando 475
tipos de rango múltiple funciones de ventanas integradas
manipulación 105, 106 dense_rank() 129
Machine Translated by Google
Índice 493
first_value() 135 lag()
PAG
132134 last_value()
135 lead() 132134 extensión pageinspect usando,
nth_value() 135 para inspeccionar el almacenamiento 435437
ntile() 130, 131 rank() PÁNICO 459
129 row_number() manejo de volcados
136 usando paralelos 316
129 procesamiento de paralelismo 252, 253 consultas
transacciones paralelas
en línea PostgreSQL, operaciones 252 usando
(OLTP) 35, 161, 462 247251 ajuste de
patrocinador de código abierto 7 parámetros ,
clases de operadores 7274 para un buen rendimiento de
creando, para Btree 74 clases consultas 239243 manejo de
de operadores, para Btree creando contraseñas
78 clases de 325
operadores personalizadas, probando 79, 80 nuevos patrones
operadores, creando 7478 instalando 401406
usando, para configurar HA cluster 400
Características de Ora2Pg 481 trabajando 400, 401
usando, para migrar desde Oracle 481483 Patroni templates creando
Oráculo 406413 etcd,
migración, a PostgreSQL 476 configurando 413415 IP de servicio,
extensión oracle_fdw 449 enlace de usando 418 estado de servicio,
referencia 476 verificando 415417 coincidencia de patrones ,
usando, para mover datos 476478 en series
Migración de Oracle, a las trampas de de tiempo 476 inspeccionando el
PostgreSQL 483, 484 rendimiento 314
extensión de oráculo
enlace de referencia 484 características relacionadas con el
ora_migrator rendimiento múltiples algoritmos de compresión, agregando
usando, para una migración rápida 479, 480 11 consultas paralelas, manejando eficientemente 12
conjuntos ordenados 114 estadísticas, manejando 12
usando 114116 Recuperación WAL, captura previa 12
procesamiento herramienta de rendimiento
de uniones externas 219 457, 458
inspección de permisos 340343
Machine Translated by Google
494 Índice
Extensión pg_buffercache utilizada, SPI, uso para funciones de retorno
para investigar el almacenamiento en caché 437439 de conjuntos 297, 298
módulo pg_crash 449 enlace disparadores, escritura 299
de referencia 449 usando, para abstracción de tipo de datos 293, 294
pgcrypto PL/Perl y PL/PerlU decidiendo,
utilizado, para cifrar datos 439 pg_dump entre 294, 295 plperlu 266
en ejecución
348, 349 PL/pgSQL 266, 267
archivo pg_hba.conf código, estructurando 267
métodos de autenticación 317, 318 tipos compuestos, utilizando 283, 284 cursores,
configuración 365, 366 utilizando para obtener datos en
fragmentos 279283
administración 316, 317
extensión pg_prewarm utilizada, manejo de errores 276, 277
para precalentamiento de cachés 439441 control de flujo 269, 270
enlace de Cláusula GET DIAGNOSTICS, usando 277, 278 bucles
referencia pg_squeeze 466 269
pg_stat_actividad consideraciones de rendimiento 268, 269 citando,
Declaraciones de Hibernate, tratamiento 454 manejando 270274 ámbitos,
inspección 452 administrando 275
fuente de consultas, detección 454, 455 procedimientos almacenados, escribiendo
consulta 452454 290292 formato de cadena, manejando
pg_stat_statements 270274 disparadores, escribiendo 284290
utilizado, para inspeccionar el rendimiento 441 Código PL/Python
extensión pgstattuple utilizada, 300 , escritura 300, 301
para inspeccionar el almacenamiento 441443 errores, manejo 303, 304
ventaja de Interfaz SPI, usando 301303
pg_trgm , tomando 9092 módulo Autenticación conectable
pg_trgm utilizado, para Módulo (PAM) 318
búsqueda difusa 443 enlace de Recuperación en un punto en el tiempo (PITR) 364
referencia uso, ventajas 364
pgwatch2 451 PostGIS
manejo de ranuras de replicación enlace de referencia 449
física 392394 plperl 266 extensión postgres_fdw 449
usado, para conectar a control remoto
PL/Perl 292, 293 servidores 443447
datos, compartir a través de llamadas de función postgresql
299 escapar 298 Oracle, migración 476
Interfaz SPI, usando 296, 297 Sentencias SQL, migrando a 467
Machine Translated by Google
Índice 495
PostgreSQL 15.x partición 233 archivo Errores de transacciones de
postgresql.conf PostgreSQL , manejo 21, 22
configuración 184 SAVEPOINT, usando 22, 23 DDL
destino y rotación del registro, transaccionales 23, 24 trabajando
definición 185, 186 con 1721
registro 187190
conflictos de replicación, supervisión 190
q
consultas lentas, registro 187
syslog, configuración 186 optimizador de consultas
Modelo de costos de PostgreSQL 5355 193 plegado constante 202, 203
Enlace de referencia de restricciones de igualdad, aplicación 197, 198
documentación de PostgreSQL 299 planes de ejecución, verificación 199201
Índice de PostgreSQL, tipos 80 búsqueda exhaustiva 198
índices adicionales, sumando 88, 89 escenarios de falla, evitar 201, 202 función
Índices de rango de bloque (BRIN) índices 86, 87 en línea 203205 combinación
Índices invertidos generalizados (GIN) 84 hash 195 opciones
Árbol de búsqueda generalizado (GiST) índices 81 de combinación, evaluación 194
índices hash 81 poda de combinación
Índices de GiST con particiones espaciales 205207 combinar
(SPGiST) 85, 86 combinación 195, 196 bucle
Vistas del sistema PostgreSQL anidado, usar 195 operaciones de configuración,
estadísticas de escritores de acelerar 207209 configuraciones,
antecedentes, seguimiento 170, 171 deshabilitar 221225
Progreso de CREATE INDEX, rastreando 176179 bases configuraciones , habilitando 221225
de datos, inspeccionando 163166 transformaciones, aplicando
índices 169, 170 196 trabajando, ejemplo 194 de rendimiento de consulta
pg_stat_activity, verificando 160163 tareas administrativas, acelerar 246, 247 parámetros,
pg_stat_statements, usando 179184 ajustar 239243 clasificar, acelerar
pg_stat_user_tables 168, 169 243246
Conexiones SSL, verificar 175 tablas,
inspeccionar 166, 167 archivos de R
registro de transacciones, archivar 172174
archivos de registro de transacciones, transmitir GAMA 124126
172174 archivos de registro de transacciones, partición de rango
rastrear 172174 transacciones, inspeccionar 175, 176 usando 234236
Progreso de VACÍO, seguimiento 176179 rangos
trabajando con 160 consultando, eficientemente 103105
Machine Translated by Google
496 Índice
tipos de rango 102, 103 PostgreSQL 14 y anteriores, comportamientos 328, 329
ejemplo de uso 107 PostgreSQL 15 y más allá,
función rank() 129 comportamientos 330, 331
recursiones 146 Capa de conexión segura (SSL) 316
manipulación 146, 147 manejo 319321
ejemplo práctico, inspección 148150 Proveedor de soporte de seguridad
Red Hat Enterprise Linux (RHEL) 315 manejo de Interfaz (SSPI) 318
expresiones regulares 93 Aislamiento de instantánea serializable (SSI) 35
enlace de referencia 35
Usuario de acceso telefónico de autenticación remota Interfaz de programación del servidor (SPI) 296 archivos
Servicio (RADIO) 318 de servicio
características relacionadas con la replicación usando 351, 352
2PC, para decodificación lógica 12 función de retorno establecida (SRF) 280
ALTERAR SUSCRIPCIÓN, mejorando 13, 14 comando agregados simples
archive_library 15 filtrado de columnas, creando 137141
agregando 13 copias de seguridad ventanas deslizantes
base comprimidas, admitiendo 14, 15 filtrado de filas, usando 121124
agregando 13 consultas lentas
ranuras de replicación 391 comprobando 455, 456
ranuras de replicación lógica 392397 consultas individuales, inspeccionando 456, 457
ranuras de replicación física 392394 instantánea demasiado antigua error
propósito 391 453 usando 47
Palabra clave ROLLUP 109111 GiST con particiones espaciales
seguridad de nivel de fila (RLS) 323 (SPGiST) índices 86
exploración 335340 contención spinlock 458
función row_number() 136 Sentencias SQL 266
filas migrando, a PostgreSQL 467
Documento JSON, convirtiéndose en 152, 153 Migración de sentencias SQL a PostgreSQL
FILAS 124126 Unión LATERAL, usando 468
estadísticas de tiempo de ejecución función estable 305
reuniendo 159 optimización
de almacenamiento 3941
S lenguajes de procedimientos almacenados 259, 260
explorando 265, 266
PUNTO DE GUARDADO PL/Perl 292, 293
usando 22, 23 PL/pgSQL267
permisos de nivel de esquema PL/Python 300
ajustando 328
Machine Translated by Google
Índice 497
procedimientos almacenados optimizando 362, 363
261 fundamentos 261 archivo pg_hba.conf, configurando 365, 366
escribir en Pl/pgSQL 290292 reproduciendo 371373
cadenas marca de tiempo, encontrando 373375
comparar 95 actas
subconjuntos de utilizando 264, 265
datos extraer 352 problemas relacionados con la envoltura de
funciones de soporte transacciones
escapar 298 explorando 42 transformaciones
replicación síncrona aplicar 196
durabilidad, ajuste 389391 subselecciones, aplanar 197
actualización a 388, 389 vista, alinear 196, 197
Protocolo de control de transmisión (TCP) 313
uso, evitando 314316
T
lenguaje de confianza 266
mesas Compromiso de dos fases (2PC) 12
trabajando con 331, 332
Cláusula TABLESAMPLE
tu
motores de apoyo 474 usando
473, 474 modo UNIÓN
corrupto 266 versus UNIÓN TODOS 147, 148
tablas temporales base de datos desconocida
que soportan motores 476 acercándose 451, 452
usando 475 lenguaje no confiable 266
El almacenamiento de atributos de gran tamaño usuarios
Técnica (TOSTADA) 168 cayendo 344, 345
series de tiempo Cláusulas USING 339
patrones, a juego 476
Lenguaje de comandos de herramientas (Tcl)
V
259 función to_tsvector 94
niveles de aislamiento de transacciones 3335 VACÍO
registro de transacciones 360, configurando 41
361 archivado, limpieza 375, 376 características, usando
bibliotecas de archivo, uso de 365 47, 48 trabajando 4346
copias de seguridad base, creación VACÍO LLENO 43
366, 367 puntos de enlace de referencia 43
control 361 configuración, para archivado 364, 365 función volátil 305
Machine Translated by Google
498 Índice
W
ventana wal_level variable
364
datos, pedidos dentro de cláusulas
de ventana 119121
abstracción 128
funciones de ventanas 128
motores de soporte 472
usando 472
utilizando 117, 118
Cláusula CON
motores de apoyo 470 usando
469, 470
Cláusula DENTRO DEL GRUPO
motores de apoyo 473 usando
472
CON cláusula RECURSIVA
motores de apoyo 471 usando
470
Registro de escritura anticipada (WAL) 360
X
xlog 360
Machine Translated by Google
Packtpub.com
Suscríbase a nuestra biblioteca digital en línea para obtener acceso completo a más de 7000 libros y videos, así como herramientas
líderes en la industria para ayudarlo a planificar su desarrollo personal y avanzar en su carrera. Para obtener más información,
visite nuestro sitio web.
¿Por qué suscribirse?
• Pase menos tiempo aprendiendo y más tiempo codificando con libros electrónicos y videos prácticos de más de 4000
profesionales de la industria
• Mejore su aprendizaje con planes de habilidades creados especialmente para usted
• Obtenga un libro electrónico o un video gratis todos los meses
• Capacidad de búsqueda completa para acceder fácilmente a
información vital • Copiar y pegar, imprimir y marcar contenido
¿Sabía que Packt ofrece versiones de libros electrónicos de cada libro publicado, con archivos PDF y ePub disponibles? Puede
actualizar a la versión de libro electrónico en packtpub.com y, como cliente del libro impreso, tiene derecho a un descuento en la
copia del libro electrónico. Póngase en contacto con nosotros en customercare@packtpub. com para más detalles.
En www.packtpub.com, también puede leer una colección de artículos técnicos gratuitos, suscribirse a una variedad de boletines
gratuitos y recibir descuentos y ofertas exclusivos en libros y libros electrónicos de Packt.
Machine Translated by Google
Otros libros que puede disfrutar
Si disfrutó de este libro, es posible que le interesen estos otros libros de Packt:
AWS Certified Database Guía de certificación especializada (DBSC01)
kate gawron
ISBN: 9781803243108
• Familiarícese con la base de datos certificada por AWS: formato de examen especializado
• Explore los servicios de la base de datos de AWS y la terminología
clave. • Trabaje con la consola y la línea de comandos de AWS que se utilizan para administrar las bases de datos.
• Probar y refinar las métricas de desempeño para tomar decisiones clave y reducir costos
• Comprender cómo manejar los riesgos de seguridad y tomar decisiones sobre la infraestructura de la base de datos
y despliegue
• Mejore su comprensión de los temas que ha aprendido usando ejemplos prácticos del mundo real
• Identificar y resolver problemas comunes de RDS, Aurora y DynamoDB
Machine Translated by Google
Otros libros que puede disfrutar 501
Recetario de administración de PostgreSQL 14
Simon RiggsGianni Ciolli
ISBN: 9781803248974
• Planifique, administre y mantenga las bases de datos de PostgreSQL en
producción • Trabaje con las funciones recién introducidas de PostgreSQL
14 • Use pgAdmin u OmniDB para realizar tareas de administrador de base de datos (DBA)
• Use psql para escribir scripts precisos y repetibles
• Comprender cómo abordar problemas de datos del mundo real con la ayuda de ejemplos
• Seleccionar e implementar técnicas sólidas de respaldo y recuperación en PostgreSQL 14 •
Implementar las mejores prácticas para planificar y diseñar bases de datos activas
Machine Translated by Google
502
Packt está buscando autores como tú
Si está interesado en convertirse en autor de Packt, visite authors.packtpub.com y presente su solicitud hoy.
Hemos trabajado con miles de desarrolladores y profesionales de la tecnología, como usted, para ayudarlos a
compartir sus conocimientos con la comunidad tecnológica global. Puede hacer una solicitud general,
postularse para un tema candente específico para el que estamos reclutando un autor o enviar su propia idea.
Comparte tus pensamientos
Ahora que ha terminado de dominar PostgreSQL 15, ¡nos encantaría escuchar sus opiniones! Si compró el libro en
Amazon, haga clic aquí para ir directamente a la página de reseñas de Amazon de este libro y compartir sus
comentarios o dejar una reseña en el sitio donde lo compró.
Su revisión es importante para nosotros y la comunidad tecnológica y nos ayudará a asegurarnos de que ofrecemos
contenido de excelente calidad.
Machine Translated by Google
503
Descargue una copia gratuita en PDF de este libro
¡Gracias por comprar este libro!
¿Le gusta leer sobre la marcha pero no puede llevar sus libros impresos a todas partes?
¿La compra de su libro electrónico no es compatible con el dispositivo de su elección?
No se preocupe, ahora con cada libro de Packt obtiene una versión en PDF sin DRM de ese libro sin costo alguno.
Lea en cualquier lugar, en cualquier lugar, en cualquier dispositivo. Busque, copie y pegue el código de sus libros técnicos favoritos
directamente en su aplicación.
Las ventajas no terminan ahí, puede obtener acceso exclusivo a descuentos, boletines y excelente contenido gratuito en su bandeja
de entrada todos los días.
Siga estos sencillos pasos para obtener los beneficios:
1. Escanea el código QR o visita el siguiente enlace
https://packt.link/freeebook/9781803248349
2. Envía tu comprobante de compra
3. ¡Eso es todo! Le enviaremos su PDF gratuito y otros beneficios a su correo electrónico directamente
Machine Translated by Google