Está en la página 1de 263

Machine Translated by Google

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  top­N.  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:  top­N  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/postgresql­parallel­create  index­for­better­performance/.  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

Esquema  | Nombre |  Tipo  |  Propietario  |  Persistencia  |  Tamaño


­­­­­­­­+­­­­­­­­­­­­+­­­­­­­+­­­­­­­+­­­­­­­­­­­­  ­+­­­­­­­­

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.cybertec­postgresql.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.cybertec­postgresql.com/en/postgresql­parallel­create­index­for­
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:

­­with­llvm  build  con  soporte  JIT  basado  en  LLVM
...

Ruta  LLVM_CONFIG  al  comando  llvm­config

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+y­pi()),
promedio(y­pi()),  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+y­pi()),  promedio(y­pi()),  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  Transact­SQL,  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/sql­createlanguage.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/sql­call.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:  2022­11­10  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

­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+­­­­­­­

2021­11­23  11:25:54.800394+01  |  2021­11­23   2

11:25:54.800394+01  |  2021­11­23  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/errcodes­appendix.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/sql­createtrigger.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  ('2017­05­04  14:43',  ­300)  DEVOLUCIÓN  *;  |  temperatura

identificación  |  t
­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­

1 |  2017­05­04  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  ('2017­05­04  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 |  2017­05­04  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,"2017­10­04  15:47:14.129151",10.4552665632218)
AVISO:  (2,"2017­10­04  15:47:14.129151",12.8670312650502)
AVISO:  (3,"2017­10­04  15:47:14.129151",14.3934494629502)
AVISO:  (4,"2017­10­04  15:47:14.129151",4.35718866065145)
AVISO:  (5,"2017­10­04  15:47:14.129151",10.9121138229966)
INSERTAR  0  5
AVISO:  datos  antiguos:

AVISO:  (1,"2017­10­04  15:47:14.129151",10.4552665632218)
AVISO:  (2,"2017­10­04  15:47:14.129151",12.8670312650502)
AVISO:  (3,"2017­10­04  15:47:14.129151",14.3934494629502)
AVISO:  (4,"2017­10­04  15:47:14.129151",4.35718866065145)
AVISO:  (5,"2017­10­04  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/sql­createprocedure.  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]  =~ /^[a­z0­9.]+@[a­z0­9.­]+$/)  {

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/sql­createddomain.  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(UTF­8)',  "/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

­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­

2021­11­23  15:36:22.097+01  |  2021­11­23  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/sql­createcast.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/sql­createcollation.  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/
Mastering­PostgreSQL­15­ ).
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.  cybertec­postgresql.com/max_connections­
performance­impacts/.

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  
scram­sha­256  en  lugar  de  PostgreSQL  10  y  versiones  posteriores.
Machine Translated by Google

318 Administrar  la  seguridad  de  PostgreSQL

•  scram­sha­256:  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/auth­ldap.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  scram­sha­256
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  og­rwx  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
­­­­­­­­­­­­­­­­+­­­­­­­­­+­­­­­­­­­­­+­­­­­­­­­­+­  ­­­­­­
­­

pid |  entero  |  |   |   |  

SSL booleano  |  |  texto   |   |  

versión |  |  texto  |  |  entero   |   |  

cifrar |  |  texto  |   |   |  

pedacitos cliente_dn   |   |  

numérico  | cliente_serial  |   |   |  

| |

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 ECDHE­RSA­AES256­GCM­SHA384  |  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/sql­createuser.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  =  'UTC­4';
ALTERAR  ROL

prueba=>  SELECCIONA  ahora();
ahora

­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­

2021­10­09  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

­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­

2021­10­09  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/sql­createpolicy.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,  'R2­D2');
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/sql­alterpolicy.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
|  R2­D2
(3  filas)

El  rol  R2­D2  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.cybertec­postgresql.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/cybertec­postgresql/pg_permission.  git

Clonación  en  'pg_permission'...  remoto:  Enumeración  
de  objetos:  111,  hecho.  remoto:  Total  111  (delta  0),  reutilizado  0  
(delta  0),  paquete­reutilizado  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
­­­­­­­­­­­­­+­­­­­­­­­­­+­­­­­­­­­­­+­­­­­­­­­­+­  ­­­­­­­­

tipo_objeto  |  tipo_obj  |  |  nombre  |   |   |  

nombre_rol   |   |  

nombre_esquema  |  nombre  |  nombre_objeto   |   |  

|  texto |  C   |   |  

nombre_columna  |  nombre  |  permiso  |   |   |  

tipo_perm  |  |  booleano  |  otorgada |   |  

| |

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/sql­droprole.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/sql­reasign­owned.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/
Mastering­PostgreSQL­15­ ).
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,  ­­
no­password  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  2022­11­15  10:26:46  UTC
;  nombre  de  la  base  de  datos:  prueba

;  Entradas  TOC:  25

;  Compresión:  ­1 ;  Versión  de  
volcado:  1.14­0
;  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
­rw­rw­r­­.  1  h  h  85  M  4  de  enero  15:54  3095.dat.gz  ­rw­rw­r­­.  1  h  h  107  4  de  
enero  15:54  3096.dat.gz  ­rw­rw­r­­.  1  h  h  740  K  4  de  enero  15:54  3097.dat.gz

­rw­rw­r­­.  1  h  h  39  4  ene  15:54  3098.dat.gz
­rw­rw­r­­.  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/
Mastering­PostgreSQL­15­ ).
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  Write­Ahead  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  ­­wal­segsize=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  30s­1d

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.postgresql­support.com/checkpoint­distance­and­mount­of­wal/ .

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
­­wal­mé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.

•  ­­wal­method=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,  ­wal­method=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  ­wal­method=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,  ­­tasa­max=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,  ­­tablespace­mapping=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
­­­­­­­­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­+­­­­­­­­­­­+­­­­­  ­­­­­+­­­­­­
­­­

pid |  entero  |  |  texto  |   |   |  

fase |  grande  |  |   |   |  

respaldo_total grande  |  copia   |   |  

seguridad_transmitida  |   de   |   |  

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  =  '2022­10­02  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...
2022­12­14  09:53:16.688  CET  [60000]  REGISTRO:  iniciando  PostgreSQL  15.1  en  aarch64­
apple­darwin21.6.0,  compilado  por  Apple  clang  versión  13.1.6  (clang­1316.0.21.2.5),  64  
bits
2022­12­14  09:53:16.690  CET  [60000]  REGISTRO:  escucha  en  la  dirección  IPv6  "::1",  
puerto  7000
2022­12­14  09:53:16.690  CET  [60000]  REGISTRO:  escucha  en  la  dirección  IPv4  
"127.0.0.1",  puerto  7000
2022­12­14  09:53:16.691  CET  [60000]  REGISTRO:  escucha  en  el  socket  de  Unix  "/
tmp/.s.PGSQL.7000"
2022­12­14  09:53:16.694  CET  [60003]  REGISTRO:  se  interrumpió  el  sistema  de  base  de  
datos;  último  conocido  en  2022­12­14  09:52:24  CET  2022­12­14  09:53:16.721  
CET  [60003]  REGISTRO:  rehacer  comienza  en  0/2000028

2022­12­14  09:53:16.721  CET  [60003]  REGISTRO:  estado  de  recuperación  consistente  
alcanzado  en  0/2000100
2022­12­14  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

2022­12­14  09:53:16.728  CET  [60001]  REGISTRO:  inicio  del  punto  de  control:  espera  
inmediata  al  final  de  la  recuperación
2022­12­14  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

2022­12­14  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  ­­wal­method=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
­­wal­mé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  ­­wal­method=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,  ­­write­recovery­conf  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:  (no­tuple­data)
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/sql­createpublication.  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  vip­manager  (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  clave­valor  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  "+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+"

eco  "|  eco   ­ETCD: v3.5.1 |"  


"|  eco  "|   ­  PATRONES: v2.1.5 |"  
eco  "| ­  POSTGRESQL:  13/12/14/15 |"  
INSTALADOR  DE  CLÚSTER  v0.1 |"
echo  "+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­+"
eco  ""

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­*­linux­amd64.tar.gz  rm  ­rf /tmp/etcd­descarga­
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

linux­amd64.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}­linux­amd64/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/EL­8­x86_64/pgdg­redhat­repo­
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/EL­8­x86_64/pgdg­redhat­repo­latest.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  postgresqlXX­servidor
#  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  patroni­etcd.x86_64  1> /dev/null  2>> /
dev/  nulo  #  instalar  patroni  echo  ­e  "${BLANCO}###################################  "  echo  
"Instalando  Patroni ...."  yum  
install  ­qy  epel­release  1> /dev/null  2>> /dev/null  yum  install  ­qy  patroni­etcd.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/ssl­cert­snakeoil.pem  #  keyfile: /etc/ssl/private/ssl­cert­snakeoil.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/ssl­cert­snakeoil.pem  #  cacert: /etc/ssl/certs/ssl­cacert­
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  scram­sha­256  ­  host  all  all  0.0.0.0/0  scram­sha­256  #­  hostssl  all  all  0.0.0.0 /0  scram­
sha­256  #  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:  rep­pass
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=network­online.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}  \  ­­data­dir=$
{_ETCD_DATA_DIR}  \  ­­initial­
advertise  ­peer­urls=$
{_ETCD_INITIAL_ADVERTISE_PEER_  URLS}  \  ­­listen­peer­urls=${_ETCD_LISTEN_PEER_URLS}  \  
­­listen­
client­urls=${_ETCD_LISTEN_CLIENT_URLS}  \  ­­advertise­client­urls=$
{_ETCD_ADVERTISE_CLIENT_URLS}  \  ­­initial­cluster­token=$
{_ETCD_INITIAL_CLUSTER_TOKEN}  \  ­­initial­cluster=${_ETCD_INITIAL_CLUSTER}  \  ­­
initial­cluster­state=${_ETCD_INITIAL_CLUSTER_STATE}  \  ­­heartbeat­interval=$
{_ETCD_HEARTBEAT_INTERVAL}  \  ­­  elección­tiempo  de  espera=$
{_ETCD_ELECTION_TIMEOUT}

Reiniciar  =  en  caso  de  falla
Reiniciar  segundo  =  5

[Instalar]
WantedBy=multi­usuario.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=red­online.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=multi­usuario.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  2022­10­25  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/pgsql­14/bin/postgres  ­D /  var/lib/postgresql/14/
batman  ­­config­file=/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]:  2022­10­25  23:06:20,038  INFO:  ninguna  acción.  Soy  (postgresql0)  
el  líder  con  el  candado  Okt  25  23:06:30  node1  patroni[5680]:  2022­10­25  23:06:30,039  INFO:  sin  
acción.  Soy  (postgresql0)  el  líder  con  el  candado  Okt  25  23:06:40  node1  patroni[5680]:  2022­10­25  
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)  ­­­­­­­­+­­­­+­­­­­­­­
­­­+

|  Miembro   |  Anfitrión |  Role |  Estado  |  TL  |  Retraso


en  MB  |
+­­­­­­­­­­­­­­+­­­­­­­­­­­­­­­+­­­­­­­­­+­­­­­­­­  ­+­­­­+­­­­­
­­­­­­+

|  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,  

­­config­file  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,...

edit­config  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

show­config  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  edit­config.  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  vip­manager  (https://github.com/cybertec  postgresql/vip­manager),  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/Mastering­PostgreSQL­15­ ).
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/sql­createextension.  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
­­­­­­­­­­­­­­­­­­+­­­­­­+­­­­­­­­­­­+­­­­­­­­­+  ­­­­­­­­­

nombre |  nombre  |   |   |  

versión_predeterminada  |  texto  |  versión_instalada   |   |  

|  texto  |  C  |  texto  | |   |  

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/pgsql­15/share/extension  contendrá  un  par  de  archivos:

...
­bash­4.3$  ls  ­l  citext*  ­rw­r­­r­­.  1  hs  
hs  1028  21.  Okt  10:22  citext­­1.0­­1.1.sql  ­rw­r­­r­­.  1  hs  hs  3424  21.  Okt  10:22  citext­­1.1­­1.2.sql  
­rw­r­­r­­.  1  hs  hs  850  21.  Okt  10:22  citext­­1.2­­1.3.sql  ­rw­r­­r­­.  1  hs  hs  668  21.  Octubre  10:22  
citext­­1.3­­1.4.sql

­rw­r­­r­­.  1  h  h  2284  21  de  octubre  10:22  citext­­1.4­­1.5.sql
­rw­r­­r­­.  1  hs  hs  13466  21.  Okt  10:22  citext­­1.4.sql  ­rw­r­­r­­.  1  hs  hs  427  21.  Octubre  
10:22  citext­­1.5­­1.6.sql
­rw­r­­r­­.  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  citext­­1.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:

­bash­4.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

­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­

2022­10­15  08:13:30  |  registro/postgresql­2022­10­15_081330.log  2022­10­15  08:13:23  |  registro/

postgresql­2022­10­15_081323.log  2022­10­15  08:13:21  |  registro/postgresql­2022­10­15_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/probando­postgres­bloom­indexes/.
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  B­tree  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  k­vecino  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
­­­­­­­­­­­­­­­­­­+­­­­­­­­­­+­­­­­­­­­­­­­­­­­­+­­­­­­­­  ­­+­­­­­­­­­

ID  de  búfer |  entero  |  |  oid  |  |   |   |  

relfilenode oid  |  |  oid  |  |   |   |  

reltablespace pequeño  |   |   |  

base  de  datos  rel relblocknumber  |   |   |  

relforknúmero grande  |  |  booleano   |   |  

|  es  sucio  |  pequeño  |  pinning_backends  |  entero   |   |  

| |   |  

recuento  de  uso |   |  

| |

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
­­­­­­­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­+­­­­­­­+­­­­­  ­­

t_bloom   |  r  |   |  8338  |  0  |  1962  |  


idx_bloom   yo  |   0  |  549  |  0  |  445  |  
idx_id yo  |   0  |  90  |  0  |  60  |  0  |  
t_prueba   r   34  |  0
|  r  pg_estadística  |  r  pg_depend  
pg_depend_reference_index  |  i

(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/sql­createserver.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/sql­createusermapping.  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/sql­alterserver.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/sql­alterusermapping.  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/cybertec­postgresql/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  |  2022­10­21  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%  libc­2.22.so
1.13%  libc­2.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%  libc­2.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%  libc­2.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_files   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  |   |  

nombre  de  esquema nombre  |   |  

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.cybertec­postgresql.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/
Mastering­PostgreSQL­15­ ).
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  (2018­09­15)

•  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/linuxx86­64soft­092277.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.cybertec­postgresql.com/en/ora_migrator­moving­from­oracle­to  
postgresql­even­faster /  y  https://github.com/cybertec­postgresql/  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.cybertec­postgresql.com /es/productos/cybertec­migrator/).
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  377­379  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  

426­428
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  142­144   347,  348  realizar,  métodos  

agregados  hipotéticos,  escribiendo  144­146  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  380­383  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  369­371
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  62­66  

formatos  diferentes,  usar  368   manejo  de  seguridad  a  nivel  

pg_basebackup,  rastrear  369   de  columna  332­334  

tablespaces,  mapear  368   expresiones  de  tabla  comunes  (CTE)  470  conexiones  

mecanismos  básicos  de  bloqueo  24­26   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  426­428  filtros  
de  floración bloom,  aplicando  428­430  btree_gin,  

aplicando  428­430   implementando  431,  432  btree_gist,  
enlace  de  referencia  430 implementando  431,  432  cachés  

btree_gin   precalentando  con  pg_prewarm
extensión  439­441
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  433­435
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é  437­439  extensión  

postgres_fdw,  que  se  usa  para  conectarse  

C a  servidores  remotos  443­447

almacenamiento,  inspección  con  pageinspect
CHECK  cláusulas  339 extensión  435­437

mensajes  de  punto  de  control  463   almacenamiento,  inspección  con  pgstattuple

puntos  de  control  361   extensión  441­443

gestión  de   utilizando  el  

limpieza  39­41   sistema  de  archivos  426  copy­on­write  (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  397­400 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  397­400 interbloqueos

correlación  entre  columnas  214 observar  36­38  
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  119­121   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  326­328   Ú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  226­228   Variables  de  entorno

particiones  de  lista,  usar  236­238   usando  349,  350

PostgreSQL  15.x  particionar  233   ERROR  459

particiones  de  rango,  usar  234­236   escenarios  de  error

estrategias,  manejar  233,  234   mensajes  de  punto  de  control  

restricciones  de  tablas,  aplicar   463  corrupción  de  obstrucción,  frente  a  

229­231  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  4­6   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  96­98

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  214­216  cambios  en  el  tiempo   de  operadores  de  exclusión  98,  99  

de  ejecución,  detectar  212  estimaciones,   estadísticas  de  

inspeccionar  212­214  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  203­205  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  433­435   varios  propósitos  307­309  búsqueda  difusa
formatos  de  archivo

manipulación  352­354 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  90­92

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  82­84

usando  33 casos  de  uso  81

PARA  ACTUALIZAR trabajando  con  81,  82

usando  30­33
Machine Translated by Google

Índice 491

Solicitud  de  Servicio  de  Seguridad  Genérica usuarios,  creando  323­326  
Interfaz  de  programa  (GSSAPI)  318 usuarios,  modificando  323­326
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  205­207  uniones  
107  aplicando   217­219  uniones  
109­111  combinando,  con  FILTRO  cláusula   externas,  procesamiento  219
113  rendimiento,  investigando  111­113   JSON  

motores  de  apoyo  469   trabajando  con  150
usando  468 JSONB  

trabajando  con  150

H documento  JSON

acceder  153­156  crear  
clúster  de  alta  disponibilidad
150­152  mostrar  
configuración,  con  índices  hash   150­152  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  255­257
de  particiones  hash  
238,  239  agregados  hipotéticos  116,  117  
escritura  144­146
k
k­vecino  más  cercano  (KNN)  431
I
funciones  inmutables  305

deduplicación  de  índices  62  
L
índices  50,  51   función  lag()  132­134  función  
implementación  55,   last_value()  135

56  usando   LATERAL  se  une  a  
59­61  escaneos  de   los  motores  de  soporte  468  
solo  índice   usando  la  
usando  67,  68  manejo  de   función  468  lead()  132­134

seguridad  a  nivel  de  instancia  321­323
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  332­334

utilizando  236­238   conexiones  312

bloqueando  17   permisos  a  nivel  de  base  de  datos  311  

seguridad  a  nivel  de  base  de  datos,  definición  326­328  

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  321­323  manejo  

configurando  184  ranuras  de   311,  312  archivo  

replicación  lógica  manejando  394­397 pg_hba.conf,  manejo  316­318
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  459­461 reasignación  de  objetos  344,  345
función  msfunc  143 Cláusula  de  compensación

múltiples  índices   motores  compatibles  475  

usando  57­59   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

132­134  last_value()  

135  lead()  132­134   extensión  pageinspect  usando,  

nth_value()  135   para  inspeccionar  el  almacenamiento  435­437

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 247­251  ajuste  de  

patrocinador  de  código  abierto  7   parámetros ,  

clases  de  operadores  72­74   para  un  buen  rendimiento  de  

creando,  para  B­tree  74  clases   consultas  239­243  manejo  de  

de  operadores,  para  B­tree  creando   contraseñas  

78  clases  de   325

operadores  personalizadas,  probando  79,  80  nuevos   patrones

operadores,  creando  74­78 instalando  401­406  

usando,  para  configurar  HA  cluster  400  
Características  de  Ora2Pg  481 trabajando  400,  401

usando,  para  migrar  desde  Oracle  481­483 Patroni  templates  creando  
Oráculo 406­413  etcd,  

migración,  a  PostgreSQL  476 configurando  413­415  IP  de  servicio,  
extensión  oracle_fdw  449  enlace  de   usando  418  estado  de  servicio,  
referencia  476 verificando  415­417  coincidencia  de  patrones ,  

usando,  para  mover  datos  476­478 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  114­116   Recuperación  WAL,  captura  previa  12  

procesamiento   herramienta  de  rendimiento  

de  uniones  externas  219 457,  458  

inspección  de  permisos  340­343
Machine Translated by Google

494 Índice

Extensión  pg_buffercache  utilizada,   SPI,  uso  para  funciones  de  retorno  

para  investigar  el  almacenamiento  en  caché  437­439   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  279­283
administración  316,  317  

extensión  pg_prewarm  utilizada,   manejo  de  errores  276,  277  

para  precalentamiento  de  cachés  439­441 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  270­274  ámbitos,  

inspección  452   administrando  275  

fuente  de  consultas,  detección  454,  455   procedimientos  almacenados,  escribiendo  

consulta  452­454   290­292  formato  de  cadena,  manejando  

pg_stat_statements   270­274  disparadores,  escribiendo  284­290

utilizado,  para  inspeccionar  el  rendimiento  441   Código  PL/Python  

extensión  pgstattuple  utilizada,   300 ,  escritura  300,  301  

para  inspeccionar  el  almacenamiento  441­443   errores,  manejo  303,  304

ventaja  de   Interfaz  SPI,  usando  301­303

pg_trgm ,  tomando  90­92  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  392­394  plperl  266 extensión  postgres_fdw  449

usado,  para  conectar  a  control  remoto
PL/Perl  292,  293 servidores  443­447

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  17­21

registro  187­190  

conflictos  de  replicación,  supervisión  190  
q
consultas  lentas,  registro  187  

syslog,  configuración  186   optimizador  de  consultas  
Modelo  de  costos  de  PostgreSQL  53­55   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  199­201  
Í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  203­205  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   205­207  combinar  
(SP­GiST)  85,  86 combinación  195,  196  bucle  
Vistas  del  sistema  PostgreSQL anidado,  usar  195  operaciones  de  configuración,  

estadísticas  de  escritores  de   acelerar  207­209  configuraciones,  
antecedentes,  seguimiento  170,  171 deshabilitar  221­225  

Progreso  de  CREATE  INDEX,  rastreando  176­179  bases   configuraciones ,  habilitando  221­225  

de  datos,  inspeccionando  163­166   transformaciones,  aplicando  
índices  169,  170   196  trabajando,  ejemplo  194  de  rendimiento  de  consulta

pg_stat_activity,  verificando  160­163   tareas  administrativas,  acelerar  246,  247  parámetros,  

pg_stat_statements,  usando  179­184   ajustar  239­243  clasificar,  acelerar  

pg_stat_user_tables  168,  169 243­246

Conexiones  SSL,  verificar  175  tablas,  

inspeccionar  166,  167  archivos  de   R
registro  de  transacciones,  archivar  172­174  

archivos  de  registro  de  transacciones,  transmitir   GAMA  124­126

172­174  archivos  de  registro  de  transacciones,   partición  de  rango  

rastrear  172­174  transacciones,  inspeccionar  175,  176 usando  234­236

Progreso  de  VACÍO,  seguimiento  176­179   rangos  

trabajando  con  160 consultando,  eficientemente  103­105
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  319­321

ejemplo  práctico,  inspección  148­150 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  137­141

agregando  13  copias  de  seguridad   ventanas  deslizantes  

base  comprimidas,  admitiendo  14,  15  filtrado  de  filas,   usando  121­124  

agregando  13 consultas  lentas  

ranuras  de  replicación  391   comprobando  455,  456  

ranuras  de  replicación  lógica  392­397   consultas  individuales,  inspeccionando  456,  457  

ranuras  de  replicación  física  392­394   instantánea  demasiado  antigua  error  

propósito  391 453  usando  47

Palabra  clave  ROLLUP  109­111   GiST  con  particiones  espaciales

seguridad  de  nivel  de  fila  (RLS)  323   (SP­GiST)  índices  86  

exploración  335­340   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  124­126 Unión  LATERAL,  usando  468  
estadísticas  de  tiempo  de  ejecución función  estable  305

reuniendo  159 optimización  

de  almacenamiento  39­41  

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  290­292   reproduciendo  371­373  
cadenas   marca  de  tiempo,  encontrando  373­375
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  389­391   subselecciones,  aplanar  197  
actualización  a  388,  389 vista,  alinear  196,  197
Protocolo  de  control  de  transmisión  (TCP)  313

uso,  evitando  314­316
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  33­35 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  43­46
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  119­121

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  (DBS­C01)

kate  gawron

ISBN:  978­1­80324­310­8

•  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:  978­1­80324­897­4

•  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/free­ebook/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

También podría gustarte