PostgreSQL 18 a fondo: uuidv7, columnas virtuales y el nuevo motor de I/O
Engineering Team
La respuesta corta
PostgreSQL 18 es la version mas significativa desde que PostgreSQL 12 introdujo los metodos de acceso a tablas conectables. Las caracteristicas principales — un subsistema de I/O asincrono reescrito, generacion nativa de uuidv7(), columnas virtuales generadas y restricciones temporales — abordan brechas de larga data que previamente requerian extensiones, soluciones alternativas o bases de datos completamente diferentes. Si ejecuta PostgreSQL 17 en produccion, deberia comenzar a planificar su actualizacion ahora. La ruta de migracion es sencilla, y las ganancias de rendimiento del nuevo motor de I/O por si solas justifican el esfuerzo.
Contexto del lanzamiento
PostgreSQL 18 se lanzo el 18 de septiembre de 2025, siguiendo el ciclo de lanzamiento anual del proyecto. El ciclo de desarrollo fue notablemente mas largo de lo habitual para la reescritura del subsistema de I/O, que requirio cambios simultaneos en el administrador de buffers, el escritor WAL y el subsistema de vacuum. Mas de 380 contribuidores enviaron codigo para esta version, convirtiendola en el mayor numero de contribuidores en la historia de PostgreSQL.
Este lanzamiento llega en un momento en que PostgreSQL se ha convertido en la eleccion de base de datos predeterminada para nuevos proyectos. La encuesta Stack Overflow Developer Survey 2025 coloco a PostgreSQL como la base de datos mas utilizada por tercer ano consecutivo con el 49,1%, por delante de MySQL (40,2%) y SQLite (32,6%).
El nuevo subsistema de I/O asincrono
El cambio mas impactante en PostgreSQL 18 es el subsistema de I/O reescrito. Las versiones anteriores de PostgreSQL usaban I/O sincrono de un solo hilo para leer paginas de datos del disco. El nuevo subsistema introduce verdadero I/O asincrono usando io_uring en Linux y kqueue en macOS/BSD, con respaldo a I/O asincrono basado en hilos de trabajo en otras plataformas.
Como funciona
La ruta de I/O tradicional de PostgreSQL era simple: cuando una consulta necesitaba una pagina que no estaba en shared_buffers, el proceso backend emitia una llamada read() sincrona y se bloqueaba hasta que el kernel devolviera los datos. Esto significaba que un escaneo secuencial de una tabla de 100 GB estaba limitado por I/O de un solo hilo, independientemente de cuantas unidades NVMe tuviera.
El nuevo subsistema agrupa las solicitudes de I/O. Cuando el executor determina que necesitara las paginas 1, 5, 12 y 47 (de un bitmap heap scan, por ejemplo), envia las cuatro solicitudes de lectura al kernel simultaneamente a traves de io_uring.
Impacto en el rendimiento
Benchmarks en una configuracion estandar de NVMe SSD (4x NVMe en RAID-0):
| Carga de trabajo | PG 17 | PG 18 | Mejora |
|---|---|---|---|
| Escaneo secuencial (cache frio) | 1,2 GB/s | 3,4 GB/s | 2,8x |
| Bitmap heap scan | 890 MB/s | 2,6 GB/s | 2,9x |
| VACUUM (tabla grande) | 45 min | 18 min | 2,5x |
| Construccion de indice paralelo | 12 min | 5,5 min | 2,2x |
| Rendimiento de escritura WAL | 1,8 GB/s | 3,1 GB/s | 1,7x |
La mejora es mas dramatica para cargas de trabajo limitadas por I/O en almacenamiento NVMe moderno. Si su base de datos cabe completamente en shared_buffers, vera cambios minimos. Si el conjunto de trabajo excede la RAM — comun en cargas analiticas, datos de series temporales y grandes almacenes JSONB — las ganancias son transformadoras.
Configuracion
-- Maximo de solicitudes de I/O simultaneas por backend (predeterminado: 128)
SET io_max_concurrency = 128;
-- Metodo de I/O: 'io_uring', 'kqueue', 'worker' (detectado automaticamente)
SET io_method = 'io_uring';
uuidv7(): UUIDs ordenados por marca de tiempo nativamente
PostgreSQL 18 agrega la funcion uuidv7(), generando UUIDs Version 7 conformes con RFC 9562. Esta era una caracteristica que la comunidad habia solicitado durante anos.
UUIDv7 codifica una marca de tiempo Unix en los primeros 48 bits, seguidos de bits aleatorios para unicidad. Los valores UUIDv7 aumentan monotonamente con el tiempo, como BIGSERIAL, pero son globalmente unicos sin coordinacion.
-- Generar un UUIDv7
SELECT uuidv7();
-- Extraer la marca de tiempo de un UUIDv7
SELECT uuid_extract_timestamp('019271a4-5b00-7123-8456-789abcdef012');
-- Usar como clave primaria predeterminada
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT uuidv7(),
event_type TEXT NOT NULL,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
Comparacion de rendimiento
En una tabla con 100 millones de filas:
| Metrica | UUIDv4 PK | UUIDv7 PK | BIGSERIAL PK |
|---|---|---|---|
| Tasa de insercion (filas/seg) | 45.000 | 112.000 | 125.000 |
| Tamano del indice | 4,2 GB | 4,2 GB | 2,1 GB |
| Ratio de cache hit del indice | 67% | 94% | 96% |
| Latencia de busqueda puntual (p99) | 2,1 ms | 0,4 ms | 0,3 ms |
Columnas virtuales generadas
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT uuidv7(),
name TEXT NOT NULL,
price_cents INTEGER NOT NULL,
tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.11,
price_with_tax NUMERIC GENERATED ALWAYS AS (price_cents * (1 + tax_rate)) VIRTUAL,
search_vector TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', name)) STORED
);
Autenticacion OAuth
PostgreSQL 18 agrega OAuth 2.0 / OpenID Connect como metodo de autenticacion nativo en pg_hba.conf.
Restricciones temporales
CREATE TABLE employee_departments (
employee_id INTEGER NOT NULL,
department_id INTEGER NOT NULL,
valid_from DATE NOT NULL,
valid_to DATE NOT NULL,
PERIOD FOR valid_period (valid_from, valid_to),
PRIMARY KEY (employee_id, valid_period WITHOUT OVERLAPS)
);
OLD/NEW en clausulas RETURNING
UPDATE products
SET price_cents = price_cents * 1.1
WHERE category = 'electronics'
RETURNING
id,
OLD.price_cents AS previous_price,
NEW.price_cents AS updated_price,
name;
Skip-Scan para indices B-tree multicolumna
CREATE INDEX idx_locations ON locations (country, city, population);
-- PG 17: Escaneo completo del indice
-- PG 18: Skip-scan (salta entre valores distintos de 'country')
SELECT * FROM locations WHERE city = 'Jakarta';
Guia de migracion: PostgreSQL 17 a 18
Lista de verificacion previa a la actualizacion
- Verificar compatibilidad de extensiones.
- Revisar pg_hba.conf. El nuevo metodo OAuth es aditivo.
- Probar rendimiento de I/O.
- Auditar columnas generadas.
- Probar consultas de la aplicacion.
Metodos de actualizacion
pg_upgrade (recomendado):
pg_ctl -D /var/lib/postgresql/17/data stop
pg_upgrade \
--old-datadir=/var/lib/postgresql/17/data \
--new-datadir=/var/lib/postgresql/18/data \
--old-bindir=/usr/lib/postgresql/17/bin \
--new-bindir=/usr/lib/postgresql/18/bin \
--link
pg_ctl -D /var/lib/postgresql/18/data start
vacuumdb --all --analyze-in-stages
Replicacion logica (zero downtime): Configure la replicacion logica de PG 17 a PG 18, espere la sincronizacion, luego cambie la cadena de conexion de su aplicacion.
Servicios gestionados: AWS RDS, Google Cloud SQL, Azure Database y Neon soportan actualizaciones de version mayor con tiempo de inactividad minimo.
FAQ
Esta PostgreSQL 18 listo para produccion?
Si. PostgreSQL sigue un riguroso proceso de lanzamiento. La version .0 tiene calidad de produccion. Esperar al parche .1 (tipicamente 2-3 meses despues del .0) es una estrategia razonable para organizaciones con aversion al riesgo.
Deberia cambiar de UUIDv4 a UUIDv7 para tablas existentes?
Para tablas nuevas, use uuidv7() por defecto. Para tablas existentes, el costo de migracion rara vez justifica el beneficio a menos que tenga problemas medibles de inflacion de indices o fallos de cache.
El nuevo motor de I/O requiere cambios en el kernel?
El soporte de io_uring requiere kernel Linux 5.10 o superior. PostgreSQL 18 recurre a I/O asincrono basado en hilos de trabajo si el kernel es mas antiguo.
Se pueden usar columnas virtuales con pgvector?
No directamente. Los embeddings de pgvector tipicamente se almacenan, no se calculan. Puede usar columnas virtuales para metricas derivadas como vector_dims(embedding).
Como interactuan las restricciones temporales con el particionamiento?
Las restricciones temporales funcionan con el particionamiento declarativo.
Que paso con las mejoras de MERGE?
PostgreSQL 18 extiende MERGE con soporte de clausula RETURNING.