Su informe termina en aproximadamente un segundo en el host de la base de datos y en cuatro por la VPN, y el número que imprime psql apunta directamente a la base de datos. Ese número es un cronómetro del lado del cliente, no una medición del servidor. Cubre la sentencia de salida, la espera de una respuesta y las filas de vuelta. Separar esas tres cosas es la única forma de saber qué lado ajustar.

Qué mide realmente el temporizador de psql

\timing on inicia un cronómetro justo antes de que el cliente escriba la sentencia en el socket y lo detiene cuando se ha leído la última fila de ese resultado. La ejecución del propio servidor está en algún lugar dentro de esa ventana, y también cada milisegundo que los paquetes pasan en el túnel. Nada en la salida los separa. EXPLAIN también imprime un número, pero ese lo produce el servidor y excluye la espera del cliente. Dos números, dos preguntas distintas.

Inicie una sesión, deje la medición activada y ejecute la sentencia que le interesa desde dentro. Reutilizar una sesión mantiene el coste de conexión fuera de la primera medición.

psql "host=db.internal port=5432 dbname=appdb user=app" \
  -c "\timing on" \
  -c "select count(*) from orders where created_at > now() - interval '1 day';"

La cifra sigue incluyendo la transferencia del resultado, que es lo que se quiere aquí. Ya no incluye el intercambio de conexión y autenticación que añadiría una invocación nueva.

Compare una sentencia por tres caminos

Ejecute la misma sentencia por el socket Unix, por la dirección LAN y por el nombre de host VPN, y compare las tres. Si solo la última es lenta, la consulta no es la variable.

Cada invocación se conecta primero y solo después mide la sentencia, así que el coste de conexión queda fuera del número mientras la transferencia queda dentro. Mantenga la cláusula WHERE, el recuento de filas y la máquina cliente idénticos, o estará comparando cargas de trabajo en lugar de caminos.

psql -h /var/run/postgresql -U app -d appdb -c "\timing on" -c "select count(*) from orders;"
psql -h 10.40.0.12 -U app -d appdb -c "\timing on" -c "select count(*) from orders;"
psql -h db.vpn.internal -U app -d appdb -c "\timing on" -c "select count(*) from orders;"

En una LAN la diferencia suele ser lo bastante pequeña para ignorarla; por VPN a menudo no lo es. Ordenar los tres resultados le dice después contra qué espera atacar.

Un socket local lento significa trabajo del servidor o planificación. Un resultado LAN lento significa la consulta o el volumen que mueve. Solo un resultado VPN lento significa latencia, MTU o ancho de banda disponible en el camino, y ningún cambio de índice lo arregla.

Saque el tiempo de ejecución del propio servidor

El servidor puede informar de cuánto tiempo pasó, y esa cifra es la que hay que comparar con la del cliente. EXPLAIN con ANALYZE ejecuta la sentencia e imprime el tiempo de planificación y ejecución dentro del plan, mientras que BUFFERS añade el comportamiento de la caché que a menudo explica una ejecución en caliente frente a una en frío. No lo apunte a sentencias de escritura salvo que pretenda escribir. En una réplica de solo lectura es el lugar más seguro para empezar, y la referencia de psql documenta el alcance del temporizador en el lado del cliente.

Para las sentencias que la aplicación ya ejecuta, pg_stat_statements agrega la ejecución del lado del servidor sin volver a ejecutar nada. Las filas son por consulta normalizada, así que una entrada puede cubrir varios puntos de llamada.

select calls, round(mean_exec_time::numeric, 2) as mean_ms, rows
from pg_stat_statements
order by mean_exec_time desc
limit 5;

Los nombres de columna varían según la versión: PostgreSQL 13 y posteriores exponen mean_exec_time, mientras que las versiones anteriores lo llaman mean_time, así que compruebe con \d pg_stat_statements si esa consulta da error. Estos valores excluyen la planificación y excluyen el viaje hasta su cliente. Esa exclusión es precisamente por lo que son útiles para la comparación.

Cuente los viajes de ida y vuelta, no solo los milisegundos

Un informe construido con doscientas sentencias pequeñas dependientes multiplica por doscientas cualquier latencia que añada el camino. Una sola sentencia que devuelve un millón de filas paga esa latencia una vez y luego paga ancho de banda durante mucho tiempo. Ambas se ven iguales en un temporizador de cliente, y necesitan correcciones opuestas.

Mida el recuento de sentencias en lugar de estimarlo. Lea el total, ejecute el informe una vez, vuelva a leerlo y reste.

select sum(calls) as calls from pg_stat_statements;
-- run the report here
select sum(calls) as calls from pg_stat_statements;

Si el delta es grande y cada sentencia es barata en el servidor, el agrupamiento es la corrección que compensa. Combine consultas dependientes, use una sola sentencia basada en conjuntos o lleve el bucle desde un cursor. En psql interactivo, \set FETCH_COUNT 1000 hace que un resultado grande llegue por lotes y cambia cómo se comportan la transferencia y la memoria del cliente. No reduce el trabajo del servidor, así que no rescatará una consulta lenta.

Revise el túnel por retransmisiones y paradas de MTU

Cuando las sentencias pequeñas son rápidas y los resultados grandes se atascan a mitad de camino, la consulta suele estar bien y el camino no. Un túnel cuyo MTU es inferior al enlace subyacente, combinado con ICMP bloqueado, produce exactamente ese patrón: llegan los primeros paquetes, desaparecen los grandes y la transferencia se cuelga hasta que reintenta. Los contadores de retransmisión del lado del cliente lo muestran.

Vigile el socket y los contadores del núcleo mientras se ejecuta una transferencia deliberadamente lenta. Los totales de retransmisión son acumulativos, así que anótelos antes y después en lugar de leer una sola muestra.

ss -tin dst 10.40.0.12
netstat -s | grep -iE "retransmit|segments retransmitted"
ping -M do -s 1400 -c 3 10.40.0.12

Las retransmisiones que suben durante la transferencia, junto con un ping que tiene éxito a 1400 bytes pero falla a 1472, apuntan a un problema de MTU y no de base de datos. Bajar el MTU de la interfaz del túnel o limitar el MSS en la pasarela es la corrección habitual, y los mismos tres comandos lo confirman después. Si las retransmisiones se mantienen planas mientras la transferencia sigue lenta, mire el ancho de banda permitido al túnel y no las pérdidas.

Una secuencia corta cubre el siguiente informe lento: compare una sentencia en local, en la LAN y por la VPN; lea la ejecución del servidor desde EXPLAIN o pg_stat_statements; cuente las llamadas antes y después de una sola ejecución; y vigile las retransmisiones mientras se mueve el resultado grande. Cada paso descarta un lado del camino. Ninguno exige adivinar qué componente reiniciar.

Artículos relacionados