KoderKoder.ai
PreciosEmpresasEducaciónPara inversores
Iniciar sesiónComenzar

Producto

PreciosEmpresasPara inversores

Recursos

ContáctanosSoporteEducaciónBlog

Legal

Política de privacidadTérminos de usoSeguridadPolítica de uso aceptableReportar abuso

Social

LinkedInTwitter
Koder.ai
Idioma

© 2026 Koder.ai. Todos los derechos reservados.

Inicio›Blog›Ajuste de rendimiento Go + Postgres: un playbook enfocado para APIs
14 dic 2025·8 min

Ajuste de rendimiento Go + Postgres: un playbook enfocado para APIs

Guía de ajuste de rendimiento Go + Postgres para APIs generadas por IA: gestionar pool de conexiones, revisar planes de consulta, indexar inteligentemente, paginar sin degradar y optimizar JSON.

Ajuste de rendimiento Go + Postgres: un playbook enfocado para APIs

Cómo se manifiesta lo “lento” en APIs Go sobre Postgres

Las APIs generadas por IA pueden parecer rápidas en pruebas iniciales. Llamas un endpoint unas cuantas veces, el conjunto de datos es pequeño y las peticiones llegan de una en una. Luego llega el tráfico real: endpoints mezclados, cargas en ráfaga, caches más fríos y más filas de las previstas. El mismo código puede empezar a sentirse aleatoriamente lento aunque en realidad nada se haya roto.

La lentitud suele aparecer de varias formas: picos de latencia (la mayoría de peticiones están bien, pero algunas tardan 5x a 50x más), timeouts (un pequeño porcentaje falla) o CPU alta (CPU de Postgres por trabajo de consultas, o CPU de Go por JSON, goroutines, logging y reintentos).

Un escenario común es un endpoint de lista con un filtro flexible que devuelve un JSON grande. En una base de datos de prueba escanea unos pocos miles de filas y termina rápido. En producción escanea unos millones de filas, las ordena y solo después aplica un LIMIT. La API sigue “funcionando”, pero la latencia p95 se dispara y algunas peticiones hacen timeout durante ráfagas.

Para separar la lentitud de la base de datos de la lentitud de la app, mantén el modelo mental simple.

Si la base de datos es la lenta, tu handler en Go pasa la mayor parte del tiempo esperando la consulta. También puedes ver muchas peticiones “en vuelo” mientras la CPU de Go aparece normal.

Si la app es la lenta, la consulta termina rápido, pero se pierde tiempo después de la consulta: construir grandes objetos de respuesta, serializar JSON, ejecutar consultas extra por fila o hacer demasiado trabajo por petición. La CPU y memoria de Go suben, y la latencia crece con el tamaño de la respuesta.

“Lo suficientemente bien” antes del lanzamiento no es perfección. Para muchos endpoints CRUD, apunta a una latencia p95 estable (no solo la media), comportamiento predecible bajo ráfagas y sin timeouts en tu pico esperado. La meta es sencilla: nada de peticiones sorprendentemente lentas cuando crecen los datos y el tráfico, y señales claras cuando algo deriva.

Línea base primero: los pocos números que importan

Antes de tunear nada, decide qué significa “bueno” para tu API. Sin una línea base, es fácil pasar horas cambiando ajustes y no saber si mejoraste o solo moviste el cuello de botella.

Tres números suelen contar la mayor parte de la historia:

  • latencia p95 de las peticiones (no la media)
  • tasa de errores (HTTP 5xx, timeouts, peticiones canceladas)
  • tiempo de BD por petición (cuánto espera cada petición en Postgres)

p95 es la métrica del “mal día”. Si p95 es alto pero la media está bien, un pequeño conjunto de peticiones está haciendo demasiado trabajo, quedando bloqueado por locks o disparando planes lentos.

Haz visibles las consultas lentas temprano. En Postgres, habilita el registro de consultas lentas con un umbral bajo para pruebas pre-lanzamiento (por ejemplo, 100–200 ms) y registra la sentencia completa para poder copiarla a un cliente SQL. Mantén esto temporal. Registrar todas las consultas lentas en producción se vuelve ruidoso rápido.

Después, prueba con peticiones que parezcan reales, no solo una ruta “hello world”. Un pequeño conjunto basta si coincide con lo que los usuarios harán: una llamada de lista con filtros y orden, una página de detalle con un par de joins, un create o update con validación y una consulta tipo búsqueda con coincidencias parciales.

Si generas endpoints a partir de una especificación (por ejemplo, con una herramienta de generación como Koder.ai), ejecuta el mismo puñado de peticiones repetidamente con entradas consistentes. Eso hace que cambios como índices, ajustes de paginación y reescrituras de consultas sean fáciles de medir.

Finalmente, elige un objetivo que puedas decir en voz alta. Ejemplo: “La mayoría de peticiones se mantienen por debajo de 200 ms p95 con 50 usuarios concurrentes, y los errores por debajo de 0.5%.” Los números exactos dependen del producto, pero un objetivo claro evita ajustes interminables.

Pool de conexiones que mantiene Postgres estable

Un pool de conexiones mantiene un número limitado de conexiones abiertas a la base de datos y las reutiliza. Sin pool, cada petición puede abrir una conexión nueva, y Postgres pierde tiempo y memoria gestionando sesiones en vez de ejecutar consultas.

La meta es mantener a Postgres ocupado haciendo trabajo útil, no cambiando contexto entre demasiadas conexiones. Este suele ser el primer beneficio significativo, especialmente para APIs generadas por IA que pueden convertirse en endpoints muy conversadores.

Ajustes iniciales sencillos

En Go normalmente ajustas max open connections, max idle connections y la vida de las conexiones. Un punto de partida seguro para muchas APIs pequeñas es un pequeño múltiplo de tus núcleos de CPU (a menudo 5 a 20 conexiones totales), con un número similar en idle, y reciclar conexiones periódicamente (por ejemplo, cada 30 a 60 minutos).

Si ejecutas varias instancias de la API, recuerda que el pool se multiplica. Un pool de 20 conexiones en 10 instancias son 200 conexiones contra Postgres, y así los equipos se topan inesperadamente con límites de conexión.

Cómo saber si el pool es el problema

Los problemas de pool se sienten distintos a una SQL lenta.

Si el pool es muy pequeño, las peticiones esperan antes incluso de llegar a Postgres. La latencia hace picos, pero la CPU de la base de datos y los tiempos de consulta pueden parecer normales.

Si el pool es demasiado grande, Postgres parece sobrecargado: muchas sesiones activas, presión de memoria y latencias desiguales entre endpoints.

Una forma rápida de separar ambos es cronometrar las llamadas a BD en dos partes: tiempo esperando una conexión vs tiempo ejecutando la consulta. Si la mayor parte del tiempo es “esperando”, el pool es el cuello de botella. Si la mayor parte es “en consulta”, enfócate en SQL e índices.

Comprobaciones rápidas útiles:

  • Registra estadísticas del pool (open, in-use, idle) y observa si in-use se queda atascado en el máximo.
  • Añade un timeout al adquirir una conexión para que las esperas fallen rápido en staging.
  • Monitoriza las conexiones activas en Postgres y cuán cerca estás de max_connections.
  • Confirma que cada petición cierra rows y libera conexiones puntualmente.
  • Haz pruebas de carga con el mismo número de instancias de aplicación que planeas ejecutar.

pgxpool vs database/sql

Si usas pgxpool, obtienes un pool orientado a Postgres con estadísticas claras y buenos valores predeterminados para comportamiento de Postgres. Si usas database/sql, tienes una interfaz estándar que funciona entre bases de datos, pero necesitas ser explícito sobre la configuración del pool y el comportamiento del driver.

Una regla práctica: si estás 100% con Postgres y quieres control directo, pgxpool suele ser más sencillo. Si dependes de librerías que esperan database/sql, quédate con él, configura el pool explícitamente y mide las esperas.

Ejemplo: un endpoint que lista órdenes puede tardar 20 ms, pero con 100 usuarios concurrentes salta a 2 s. Si los logs muestran 1.9 s esperando una conexión, tunear la consulta no ayudará hasta que el pool y el total de conexiones a Postgres estén dimensionados correctamente.

Planificación de consulta: lectura rápida de EXPLAIN

Cuando un endpoint se siente lento, revisa qué está haciendo Postgres. Una lectura rápida de EXPLAIN suele señalar la solución en minutos.

Ejecuta esto en el SQL exacto que envía tu API:

EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status, created_at
FROM orders
WHERE user_id = $1 AND status = $2
ORDER BY created_at DESC
LIMIT 50;

Unas pocas líneas importan más. Observa el nodo superior (lo que Postgres eligió) y los totales al final (cuánto tardó). Luego compara filas estimadas vs reales. Brechas grandes suelen indicar que el planero (planner) se equivocó.

Qué suelen significar las líneas clave

Si ves Index Scan o Index Only Scan, Postgres está usando un índice, lo cual suele ser bueno. Bitmap Heap Scan puede ser aceptable para coincidencias de tamaño medio. Seq Scan significa que leyó toda la tabla, lo cual solo está bien cuando la tabla es pequeña o casi todas las filas coinciden.

Señales de alerta comunes:

  • Seq Scan en una tabla grande
  • Filas estimadas vs reales muy separadas (por ejemplo, 10 estimadas vs 10.000 reales)
  • Sort que se lleva la mayor parte del tiempo (a menudo ligado a ORDER BY)
  • “Filter:” que elimina muchas filas después de un scan
  • Bloques de lectura compartida altos en BUFFERS (mucha data leída)

Por qué fallan los planes (y soluciones fáciles)

Los planes lentos suelen venir de unos pocos patrones:

  • Falta de índice para tu patrón WHERE + ORDER BY (por ejemplo, (user_id, status, created_at))
  • Tipos incompatibles (por ejemplo, comparar una columna UUID con un parámetro texto), lo que puede impedir el uso del índice
  • Funciones en WHERE (por ejemplo, WHERE lower(email) = $1), que pueden forzar scans a menos que añadas un índice de expresión coincidente

Si el plan parece raro y las estimaciones están muy fuera, las estadísticas suelen estar obsoletas. Ejecuta ANALYZE (o deja que autovacuum lo haga) para que Postgres aprenda los recuentos actuales y la distribución de valores. Esto importa después de grandes importaciones o cuando nuevos endpoints empiezan a escribir muchos datos rápidamente.

Indexación para las consultas que realmente ejecutas

Obtén recompensas por compartir
Crea contenido o refiere a otros y gana créditos para seguir construyendo en Koder.ai.
Ganar créditos

Los índices solo ayudan cuando coinciden con cómo consultas los datos. Si los creas por suposiciones, obtienes escrituras más lentas, mayor almacenamiento y poco o ningún beneficio en lecturas.

Una forma práctica de pensarlo: un índice es un atajo para una pregunta específica. Si tu API hace otra pregunta, Postgres ignora el atajo.

Construye índices en torno a filtros + orden

Si un endpoint filtra por account_id y ordena por created_at DESC, un índice compuesto suele superar a dos índices separados. Ayuda a Postgres a encontrar las filas correctas y devolverlas en el orden adecuado con menos trabajo.

Reglas prácticas que suelen funcionar:

  • Indexa las columnas que filtras más, luego añade la columna por la que ordenas.
  • Mantén los índices compuestos pequeños. Dos columnas es común; tres a veces está bien; más suele ser una señal.
  • Pon el filtro más selectivo primero (el que reduce más los resultados).
  • Evita índices separados que estén totalmente cubiertos por un índice compuesto mejor.
  • Prefiere un índice bien elegido sobre varios “quizá útiles”.

Ejemplo: si tu API tiene GET /orders?status=paid y siempre muestra lo más nuevo primero, un índice como (status, created_at DESC) encaja bien. Si la mayoría de consultas también filtra por cliente, (customer_id, status, created_at) puede ser mejor, pero solo si es así como el endpoint se ejecuta en producción.

Índices parciales para filtros comunes

Si la mayor parte del tráfico golpea una porción estrecha de filas, un índice parcial puede ser más barato y rápido. Por ejemplo, si tu app lee mayormente registros activos, indexar solo WHERE active = true mantiene el índice más pequeño y con más probabilidad de permanecer en memoria.

Para confirmar que un índice ayuda, haz comprobaciones rápidas:

  • Ejecuta EXPLAIN (o EXPLAIN ANALYZE en un entorno seguro) y busca un index scan que coincida con tu consulta.
  • Compara tiempos y filas leídas con y sin el índice.
  • Observa si “Rows Removed by Filter” sigue alto. A menudo significa que el índice no coincide con tu filtro.

Elimina índices no usados con cuidado. Revisa estadísticas de uso (por ejemplo, si un índice ha sido escaneado). Suprime uno a la vez en ventanas de bajo riesgo y ten un plan de reversión. Los índices no usados no son inofensivos: ralentizan inserts y updates en cada escritura.

Patrones de paginación que no se degradan con el tiempo

La paginación suele ser donde una API rápida empieza a sentirse lenta, aun cuando la base de datos está sana. Trata la paginación como un problema de diseño de consulta, no como un detalle UI.

Por qué LIMIT/OFFSET se vuelve más lento

LIMIT/OFFSET parece simple, pero las páginas profundas suelen costar más. Postgres aún tiene que saltarse (y a menudo ordenar) las filas que omites. La página 1 puede tocar unas pocas docenas de filas. La página 500 puede obligar al DB a escanear y descartar decenas de miles solo para devolver 20 resultados.

También puede crear resultados inestables cuando se insertan o eliminan filas entre peticiones. Los usuarios pueden ver duplicados o perder items porque el significado de “fila 10.000” cambia conforme la tabla cambia.

Paginación por keyset (cursor) con ejemplo de “último visto”

La paginación por keyset hace otra pregunta: “Dame las siguientes 20 filas después de la última que vi.” Eso mantiene a la BD trabajando sobre un trozo pequeño y consistente.

Una versión simple usa un id creciente:

SELECT id, created_at, title
FROM posts
WHERE id > $1
ORDER BY id
LIMIT 20;

Tu API devuelve un next_cursor igual al último id de la página. La siguiente petición usa ese valor como $1.

Para orden temporal, usa un orden estable y rompe empates. created_at solo no basta si dos filas comparten el mismo timestamp. Usa un cursor compuesto:

WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;

Algunas reglas para evitar duplicados y pérdidas:

  • Siempre incluye un desempate único en el ORDER BY (usualmente id).
  • Mantén el orden idéntico entre peticiones.
  • Haz el cursor opaco para los clientes (codifica created_at e id juntos).
  • Si los usuarios pueden filtrar, aplica los mismos filtros en cada página.
  • Prefiere campos de orden inmutables (fecha de creación) sobre mutables (estado, puntuación) cuando sea posible.

Modelado JSON: respuestas más rápidas con payloads más pequeños

Dimensiona bien tu pool de BD
Genera código que incluya límites y timeouts sensatos en el pool, luego prueba con carga con confianza.
Iniciar proyecto

Una razón sorprendentemente común por la que una API se siente lenta no es la base de datos, sino la respuesta. Un JSON grande tarda más en construirse, en enviarse y en parsearse en el cliente. La ganancia más rápida suele ser devolver menos.

Comienza por tu SELECT. Si un endpoint necesita solo id, name y status, pide esas columnas y nada más. SELECT * se hace más pesado con el tiempo a medida que las tablas ganan texto largo, blobs JSON y columnas de auditoría.

Otra ralentización frecuente es construir respuestas en N+1: obtienes una lista de 50 ítems y luego ejecutas 50 consultas más para adjuntar datos relacionados. Puede pasar las pruebas y luego colapsar con tráfico real. Prefiere una sola consulta que devuelva lo que necesitas (joins cuidadosos) o dos consultas donde la segunda agrupe por IDs.

Algunas formas de mantener payloads pequeños sin romper clientes:

  • Usa un flag include= (o una máscara fields=) para que las respuestas de lista sean ligeras y las de detalle opten por extras.
  • Limita arreglos anidados (por ejemplo, solo los últimos 10 eventos) y proporciona un endpoint separado para el historial completo.
  • No devuelvas columnas JSON internas sin procesar si los clientes solo necesitan un par de claves.
  • Usa códigos cortos en lugar de repetir etiquetas largas.

Construir JSON en Postgres o en Go?

Ambos pueden ser rápidos. Elige según lo que optimices.

Las funciones JSON de Postgres (jsonb_build_object, json_agg) son útiles cuando quieres menos viajes y formas predecibles desde una consulta. Modelar en Go es útil cuando necesitas lógica condicional, reutilizar structs o mantener SQL más fácil de mantener. Si tu SQL para construir JSON se vuelve difícil de leer, también se vuelve difícil de tunear.

Una buena regla: deja que Postgres filtre, ordene y agregue. Luego deja a Go encargarse de la presentación final.

Si generas APIs rápidamente (por ejemplo con Koder.ai), añadir flags de include temprano ayuda a evitar endpoints que se hinchan con el tiempo. También te da una forma segura de añadir campos sin hacer cada respuesta más pesada.

Un pase de afinamiento paso a paso antes de los primeros usuarios

No necesitas un laboratorio enorme para detectar la mayoría de problemas de rendimiento. Un pase corto y repetible saca a la luz los problemas que se convierten en outages cuando llega el tráfico, especialmente si el punto de partida es código generado que planeas enviar.

Antes de cambiar nada, anota una línea base pequeña:

  • p95 y p99 de latencia para tus endpoints más ocupados
  • tasa de errores y timeouts
  • CPU de la base de datos y conexiones activas
  • las 5 consultas más lentas por tiempo total (no solo la única peor)

El pase de afinamiento

Empieza pequeño, cambia una cosa a la vez y vuelve a probar tras cada cambio.

  1. Ejecuta una prueba de carga de 10 a 15 minutos que se parezca al uso real. Golpea los endpoints que verán tus primeros usuarios (login, listas, búsqueda, crear). Luego ordena rutas por latencia p95 y tiempo total gastado.

  2. Revisa presión de conexiones antes de tunear SQL. Un pool demasiado grande satura Postgres. Un pool demasiado pequeño crea esperas largas. Busca tiempo de espera creciente para adquirir una conexión y recuentos de conexiones que suben en ráfaga. Ajusta límites de pool e idle primero, luego vuelve a ejecutar la misma carga.

  3. EXPLAIN de las consultas más lentas y arregla la mayor señal de alarma. Los culpables habituales son scans completos en tablas grandes, sorts en grandes conjuntos de resultados y joins que explotan el recuento de filas. Elige la consulta peor y hazla aburrida.

  4. Añade o ajusta un índice, luego vuelve a probar. Los índices ayudan cuando coinciden con tu WHERE y ORDER BY. No añadas cinco a la vez. Si tu endpoint lento es “listar órdenes por user_id ordenadas por created_at”, un índice compuesto en (user_id, created_at) puede ser la diferencia entre instantáneo y doloroso.

  5. Reduce respuestas y paginación, luego vuelve a probar. Si un endpoint devuelve 50 filas con blobs JSON grandes, tu base de datos, la red y el cliente pagan el precio. Devuelve solo los campos que la UI necesita y prefiere paginación que no se vuelva más lenta al crecer las tablas.

Lleva un registro simple de cambios: qué cambió, por qué y qué movió en p95. Si un cambio no mejora tu línea base, reviértelo y sigue adelante.

Errores comunes y trampas a evitar

Ajusta endpoints desde el chat
Itera sobre endpoints lentos describiendo el problema y refinando consultas y handlers en el chat.
Probar Koder.ai

La mayoría de problemas de rendimiento en APIs Go sobre Postgres son auto-infligidos. La buena noticia es que unas pocas comprobaciones detectan muchos de ellos antes de que llegue tráfico real.

Una trampa clásica es tratar el tamaño del pool como un control de velocidad. Ponerlo “tan alto como sea posible” suele hacer todo más lento. Postgres pasa más tiempo gestionando sesiones, memoria y locks, y tu app empieza a hacer timeouts en oleadas. Un pool más pequeño y estable con concurrencia predecible suele ganar.

Otro error común es “indexar todo”. Índices extra pueden ayudar lecturas, pero también ralentizan escrituras y pueden cambiar planes de consulta de forma sorprendente. Si tu API inserta o actualiza con frecuencia, cada índice adicional añade trabajo. Mide antes y después, y revisa planes tras añadir un índice.

La deuda de paginación se cuela silenciosamente. La paginación por offset parece bien al principio, luego la p95 sube con el tiempo porque la BD tiene que avanzar por más filas.

El tamaño del payload JSON es otro impuesto oculto. La compresión ayuda con el ancho de banda, pero no elimina el coste de construir, asignar y parsear objetos grandes. Recorta campos, evita anidamientos profundos y devuelve solo lo que la pantalla necesita.

Si solo observas la media de tiempo de respuesta, te perderás donde empieza el dolor real del usuario. p95 (y a veces p99) es donde la saturación del pool, esperas por locks y planes lentos aparecen primero.

Un chequeo rápido pre-lanzamiento:

  • Observa tiempo de espera en el pool y recuentos de conexiones de Postgres durante una pequeña prueba de carga.
  • Compara media vs p95 en el mismo endpoint.
  • Verifica que la paginación no empeora cuando la tabla es 10x más grande.
  • Inspecciona tamaños de respuesta para endpoints de lista (los bytes importan).
  • Vuelve a ejecutar EXPLAIN después de añadir índices o cambiar filtros.

Lista rápida y siguientes pasos antes del lanzamiento

Antes de que lleguen usuarios reales, quieres evidencia de que tu API se mantiene predecible bajo estrés. La meta no son números perfectos: es detectar los pocos problemas que causan timeouts, picos o una base de datos que deja de aceptar trabajo.

Ejecuta comprobaciones en un entorno staging que se parezca a producción (rango de tamaño de BD similar, mismos índices, mismos ajustes de pool): mide la latencia p95 por endpoint clave bajo carga, captura tus consultas lentas principales por tiempo total, vigila el tiempo de espera en el pool, ejecuta EXPLAIN (ANALYZE, BUFFERS) en la peor consulta para confirmar que usa el índice esperado y verifica tamaños de payload en tus rutas más ocupadas.

Luego haz una corrida de peor caso que imite cómo se rompen los productos: solicita una página profunda, aplica el filtro más amplio y pruébalo con un arranque en frío (reinicia la API y golpea la misma petición primero). Si la paginación profunda se vuelve más lenta en cada página, cambia a paginación por cursor antes del lanzamiento.

Anota tus valores por defecto para que el equipo mantenga elecciones consistentes: límites y timeouts del pool, reglas de paginación (tamaño máximo de página, si se permite offset, formato de cursor), reglas de consulta (seleccionar solo columnas necesarias, evitar SELECT *, limitar filtros caros) y reglas de logging (umbral de consulta lenta, cuánto tiempo guardar muestras, cómo etiquetar endpoints).

Si generas y exportas servicios Go + Postgres con Koder.ai, hacer un pequeño pase de planificación antes del despliegue ayuda a mantener filtros, paginación y formas de respuesta intencionales. Una vez que empieces a afinar índices y formas de consulta, las snapshots y la reversión facilitan deshacer un “arreglo” que ayuda a un endpoint pero perjudica a otros. Si quieres un lugar único para iterar ese flujo de trabajo, Koder.ai en koder.ai está diseñado para generar y refinar esos servicios mediante chat y luego exportar el código cuando estés listo.

Preguntas frecuentes

¿Cómo puedo saber rápidamente si mi API en Go está lenta por Postgres o por mi código?

Comienza separando el tiempo de espera en BD del trabajo en la app.

  • Si la base de datos es la que está lenta, el handler pasa la mayor parte del tiempo esperando la consulta. La CPU de Go suele permanecer normal mientras las solicitudes se acumulan “en vuelo”.
  • Si la app es la lenta, las consultas responden rápido pero el tiempo se consume en construir objetos, ejecutar consultas extra por fila, serializar JSON grandes o en el logging. La CPU y la memoria de Go suelen subir con el tamaño de la respuesta.

Añade mediciones simples alrededor de “esperar conexión” y “ejecución de consulta” para ver qué lado domina.

¿Qué métricas debo seguir primero antes de tunear nada?

Usa una línea base pequeña que puedas repetir:

  • Latencia p95 por endpoint clave (no la media)
  • Tasa de errores (5xx, timeouts, cancelaciones)
  • Tiempo en BD por petición (tiempo esperando a Postgres)

Elige un objetivo claro como “p95 por debajo de 200 ms con 50 usuarios concurrentes, errores por debajo de 0.5%”. Cambia solo una cosa a la vez y vuelve a probar con la misma mezcla de peticiones.

¿Debería activar el registro de consultas lentas de Postgres y qué umbral es práctico?

Activa el logging de consultas lentas con un umbral bajo en pruebas pre-lanzamiento (por ejemplo, 100–200 ms) y registra la sentencia completa para poder copiarla a un cliente SQL.

Mantenlo temporal:

  • Se vuelve ruidoso en producción muy rápido.
  • Puede añadir overhead si registras demasiado.

Cuando identifiques los peores culpables, pasa a muestreo o sube el umbral.

¿Cuáles son buenos ajustes iniciales para el pool de conexiones en un API Go con Postgres?

Un valor práctico por defecto es un pequeño múltiplo de los núcleos de CPU por instancia de API, a menudo 5–20 conexiones abiertas máximas, con un número similar de conexiones inactivas y reciclando conexiones cada 30–60 minutos.

Dos fallos comunes:

  • Pool demasiado pequeño: las peticiones esperan por una conexión aunque el tiempo de consulta en Postgres sea correcto.
  • Pool demasiado grande: Postgres se sobrecarga con muchas sesiones activas y la latencia queda desigual.

Recuerda que los pools se multiplican entre instancias (20 conexiones × 10 instancias = 200 conexiones).

¿Cómo puedo confirmar que el pool de conexiones es el cuello de botella (y no el SQL)?

Mide las llamadas a la BD en dos partes:

  • Tiempo esperando una conexión (espera en el pool)
  • Tiempo ejecutando la consulta (trabajo en Postgres)

Si la mayor parte del tiempo es espera en el pool, ajusta el tamaño del pool, timeouts y el número de instancias. Si la mayor parte es ejecución de consulta, enfócate en EXPLAIN e índices.

También confirma que cierras y liberas conexiones puntualmente para devolverlas al pool.

¿Qué debo mirar primero en EXPLAIN cuando un endpoint está lento?

Ejecuta EXPLAIN (ANALYZE, BUFFERS) en el SQL exacto que envía tu API y busca:

  • Seq Scan en una tabla grande
  • Brecha enorme entre filas estimadas vs filas reales
¿Cómo elijo el índice adecuado para un endpoint de lista con filtros y orden?

Los índices deben coincidir con lo que realmente hace el endpoint: filtros + orden.

Enfoque por defecto recomendable:

  • Construye un índice compuesto para tu patrón común de WHERE + ORDER BY.
  • Mantenlo pequeño (2 columnas a menudo, 3 a veces).
¿Cuándo vale la pena un índice parcial en Postgres?

Usa un índice parcial cuando la mayor parte del tráfico accede a un subconjunto predecible de filas.

Patrón de ejemplo:

  • Muchas lecturas solo de active = true
  • Pocas consultas sobre filas inactivas

Un índice parcial como ... WHERE active = true se mantiene más pequeño, cabe más en memoria y reduce el overhead de escritura frente a indexarlo todo.

Confirma con que Postgres realmente lo usa para tus consultas de alto tráfico.

¿Por qué `LIMIT/OFFSET` se vuelve más lento con el tiempo y qué debería usar en su lugar?

LIMIT/OFFSET se vuelve más lento en páginas profundas porque Postgres aún tiene que avanzar (y a menudo ordenar) las filas que se saltan. La página 1 puede tocar unas decenas de filas; la página 500 puede obligar a escanear y descartar decenas de miles solo para devolver 20 resultados.

Prefiere paginación por keyset (cursor):

Mis consultas DB son rápidas pero las respuestas siguen siendo lentas: ¿debería reducir los payloads JSON?

Normalmente sí en endpoints de lista. La respuesta más rápida es la que no envías.

Ganancias prácticas:

  • Selecciona solo las columnas necesarias (evita SELECT *).
  • Añade o para que los clientes pidan campos pesados solo si los necesitan.
Contenido
Cómo se manifiesta lo “lento” en APIs Go sobre PostgresLínea base primero: los pocos números que importanPool de conexiones que mantiene Postgres establePlanificación de consulta: lectura rápida de EXPLAINIndexación para las consultas que realmente ejecutasPatrones de paginación que no se degradan con el tiempoModelado JSON: respuestas más rápidas con payloads más pequeñosUn pase de afinamiento paso a paso antes de los primeros usuariosErrores comunes y trampas a evitarLista rápida y siguientes pasos antes del lanzamientoPreguntas frecuentes
Compartir
Koder.ai
Crea tu propia app con Koder hoy!

La mejor manera de entender el poder de Koder es verlo por ti mismo.

Empezar gratisReservar demo
rows
  • Sort que domina el tiempo (habitualmente con ORDER BY)
  • “Rows Removed by Filter” muy alto
  • Muchas lecturas compartidas en BUFFERS (lecturas intensas)
  • Arregla la mayor señal de alarma primero; no intentes tunear todo a la vez.

  • Coloca primero el filtro más selectivo, luego la columna de orden.
  • Ejemplo: si filtras por user_id y ordenas por lo más nuevo, un índice (user_id, created_at DESC) suele marcar la diferencia entre p95 estable y picos.

    EXPLAIN
  • Usa un orden estable más un desempate único (a menudo id).
  • Mantén ORDER BY idéntico entre peticiones.
  • Codifica (created_at, id) o similar en un cursor.
  • Así el coste de cada página se mantiene aproximadamente constante a medida que la tabla crece.

    include=
    fields=
  • Limita arreglos anidados (por ejemplo, solo los 10 eventos más recientes) y ofrece un endpoint separado para el historial completo.
  • Evita patrones N+1 (50 filas + 50 consultas extra). Usa joins o consultas en batch.
  • Reducir el tamaño del payload suele bajar la CPU de Go, la presión de memoria y la latencia en la cola.