
PostgreSQL 18 al Descubierto: I/O Asíncrona, UUIDv7 y Actualizaciones sin Dolor
Como cada año por estas fechas, la comunidad global de PostgreSQL nos sorprende con una nueva versión mayor, y esta vez no es la excepción. El 25 de septiembre el grupo de desarrollo mundial PostgreSQL presentaba oficialmente PostgreSQL 18, y te adelanto que es una de las actualizaciones más interesantes de los últimos años, especialmente si te obsesiona el rendimiento y la eficiencia en tus operaciones.
En este artículo vamos a analizar el anuncio oficial para desgranar las novedades que realmente van a cambiar nuestra forma de trabajar, desde el desarrollo de software a medida hasta el despliegue de arquitecturas de alta disponibilidad. Olvídate de los pequeños ajustes, aquí hay cambios de calado. ¡Vamos a verlos!
Las Novedades Clave de PostgreSQL 18
He analizado el comunicado y estas son las 4 áreas que considero más revolucionarias.

🚀 El Gran Titular: I/O Asíncrona (AIO) para un Rendimiento Brutal
Esta es la joya de la corona de PostgreSQL 18. Hasta ahora, Postgres dependía de los mecanismos de readahead
del sistema operativo para acelerar la lectura de datos, pero esto no siempre es eficiente.
PostgreSQL 18 introduce un nuevo subsistema de E/S Asíncrona (AIO). En lugar de solicitar un bloque de datos y esperar, ahora puede lanzar múltiples peticiones de I/O en paralelo.
- ¿Qué significa esto? Para operaciones que leen muchos datos del disco (escaneos secuenciales,
VACUUM
, etc.), el impacto es masivo. Las pruebas de la comunidad han mostrado mejoras de rendimiento de hasta 3 veces en ciertos escenarios. - ¿Cómo se usa? Se controla con el nuevo parámetro
io_method
, que permite elegir entre diferentes implementaciones comoio_uring
(en Linux) o mantener el comportamiento síncrono clásico.
En resumen: Si tu carga de trabajo es intensiva en lectura (I/O-bound), como en sistemas de analítica o aplicaciones con grandes datasets, vas a notar un antes y un después.
😌 Actualizaciones (Upgrades) por Fin sin Dolores de Cabeza
Cualquiera que haya administrado una base de datos en producción conoce el miedo post-actualización: las consultas van lentas hasta que el ANALYZE
reconstruye las estadísticas del planificador. PostgreSQL 18 ataca este problema de raíz.
- Conservación de Estadísticas: ¡Por fin! Ahora es posible mantener las estadísticas del planificador durante una actualización de versión mayor. Esto significa que tu base de datos rendirá a su nivel óptimo casi inmediatamente después de actualizar, eliminando esa fase de degradación del rendimiento.
pg_upgrade
más rápido: La propia utilidad de actualización ha sido optimizada. Ahora puede ejecutar comprobaciones en paralelo (--jobs
) y tiene una nueva opción (--swap
) que intercambia los directorios de datos en lugar de copiarlos, acelerando drásticamente el proceso.
En resumen: El proceso de actualizar a una nueva versión mayor será mucho más rápido y menos disruptivo para tus aplicaciones.
💻 Joyas que Enamorarán a los Desarrolladores
Esta versión viene cargada de funcionalidades que nos facilitan la vida y nos permiten escribir código más moderno y eficiente.
- ¡Hola,
uuidv7()
!: Se añade una función nativa para generar UUIDs v7. A diferencia de los UUIDs v4 (aleatorios), los v7 están ordenados por tiempo. Usarlos como claves primarias mejora drásticamente el rendimiento de los índices B-Tree, ya que reduce la fragmentación y mejora la localidad de los datos. ¡Un cambio fundamental para tablas con alta inserción! - Columnas Generadas Virtuales: Ahora las columnas generadas (
GENERATED COLUMNS
) son virtuales por defecto. Esto significa que el valor se calcula en tiempo de consulta en lugar de almacenarse en disco, ahorrando espacio para valores que no se necesitan constantemente. RETURNING
conOLD
yNEW
: El DML (INSERT
,UPDATE
,DELETE
,MERGE
) ahora permite acceder a los valores antiguos (OLD
) y nuevos (NEW
) directamente en la cláusulaRETURNING
. Esto es fantástico para crear logs de auditoría o implementar lógica de "change data capture" en una sola consulta.
En resumen: Herramientas nativas para patrones de diseño modernos que hasta ahora requerían soluciones más complejas.
🛡️ Autenticación, Seguridad y Cambios Importantes a Notar
- Soporte nativo para OAuth 2.0: Esta es una de las joyas ocultas de la release, especialmente para entornos profesionales. En la práctica, esto significa que ahora puedes autenticar a los usuarios de la base de datos a través de proveedores de identidad externos como Auth0, Okta, Microsoft Entra ID (Azure AD) o Google. Técnicamente, esto se habilita directamente en el fichero
pg_hba.conf
con un nuevo método de autenticación:oauth
. Se configuran parámetros clave como elissuer
(la URL de tu proveedor de identidad) y elscope
(los permisos que el token de acceso debe tener). La validación de dicho token es modular y se delega en librerías especializadas, lo que da una gran flexibilidad al sistema. El resultado es que, en lugar de gestionar contraseñas locales, puedes delegar la autenticación a tu sistema central de Single Sign-On (SSO). Esto no solo simplifica radicalmente la gestión de usuarios, sino que también eleva la seguridad al permitir el uso de políticas centralizadas como la autenticación multifactor (MFA). - El Fin de una Era: La Desaprobación (Deprecation) de MD5: Aunque la autenticación segura con SCRAM lleva disponible desde hace varias versiones, PostgreSQL 18 da el paso definitivo: desaconseja oficialmente el uso de MD5 para contraseñas. El soporte se eliminará por completo en una futura versión, por lo que ya no hay excusas. El mensaje de la comunidad es claro: si todavía usas MD5, la migración a SCRAM es una tarea prioritaria e inminente.
- Checksums por defecto: Los nuevos clústeres creados con
initdb
ahora tendrán los checksums de página habilitados por defecto. Esto mejora la integridad de los datos, pero es algo muy importante a tener en cuenta al planificar una actualización desde un clúster sin ellos.
Mi Perspectiva Crítica: ¿Qué Significa Esto para Tus Proyectos?
- ¿Es la I/O Asíncrona la solución a todo? No. Es una herramienta potentísima para cargas de trabajo I/O-bound. Si tu cuello de botella es la CPU, no verás una mejora mágica. Pero para el despliegue de aplicaciones de analítica o servicios de IA que procesan grandes volúmenes de datos, el impacto será enorme.
- El verdadero "Game Changer" Operacional: Aunque AIO acapara los titulares, creo que la conservación de estadísticas en los upgrades es la funcionalidad más importante para sistemas en producción. Elimina uno de los mayores dolores de cabeza y riesgos asociados a una actualización. Reduce la incertidumbre y facilita mantener tu stack tecnológico al día.
- Para el desarrollo de software a medida: Mi recomendación es empezar a usar
uuidv7()
como clave primaria por defecto en todas las tablas nuevas que lo permitan. Es una victoria de rendimiento fácil y gratuita. Las mejoras enRETURNING
también simplificarán mucho la lógica de auditoría en los backends que construyo. - El As en la Manga para Proyectos Profesionales (OAuth 2.0): No subestimes esta funcionalidad. Si como yo, construyes software a medida para empresas, ofrecer integración con su sistema de SSO (como Microsoft 365 o Google Workspace) ya no es una pesadilla. Es una característica de nivel enterprise que ahora viene de serie y que te permite vender proyectos más seguros y mejor integrados en el ecosistema de tus clientes. Es un diferenciador brutal.
- Llamada a la acción inmediata: Audita tus sistemas. Si todavía usas autenticación MD5, planifica la migración a SCRAM ahora. No es una sugerencia, es una necesidad de seguridad inminente.
🛠️ Manos a la Obra: Prueba PostgreSQL 18 con Docker y Ejemplos
Para entender por qué la desaprobación de MD5 es una noticia tan importante, no hay nada como verlo en acción. Vamos a usar Docker para levantar un laboratorio con dos instancias de PostgreSQL 18 corriendo en paralelo: una configurada para usar el método antiguo y ahora obsoleto MD5, y otra usando el moderno y seguro SCRAM, que ha sido el método recomendado durante años. Así podrás visualizar la diferencia de seguridad por ti mismo.
Requerimientos
Para poder lanzar los ejemplos necesitaras tener instalado Docker.

Paso 1: Levantar el Entorno Comparativo
Crea un archivo llamado docker-compose.yml
. Fíjate que definimos las dos bases de datos (db_md5
y db_scram
), la interfaz web adminer
, y un contenedor extra llamado pg_client
que actuará como nuestra "caja de herramientas" con psql
ya instalado.
docker-compose.yml
(Comparativa MD5 vs. SCRAM)
services:
db_md5:
image: postgres:18
container_name: postgres18_md5
restart: always
command: postgres -c password_encryption=md5 -c io_method=sync
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: testdb
ports:
- "5432:5432"
volumes:
- postgres_data_md5:/var/lib/postgresql/data
db_scram:
image: postgres:18
container_name: postgres18_scram
restart: always
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: testdb
ports:
- "5433:5432"
volumes:
- postgres_data_scram:/var/lib/postgresql/data
pg_client:
image: postgres:18
container_name: pg_client
# Este comando mantiene el contenedor corriendo para que podamos entrar en él
command: tail -f /dev/null
adminer:
image: adminer
container_name: postgres18_adminer
restart: always
ports:
- "8080:8080"
volumes:
postgres_data_md5:
postgres_data_scram:
En una terminal, ejecuta: docker-compose up -d
o docker compose up -d
. Ahora tienes todo el entorno listo, sin necesidad de instalar nada más en tu máquina.
Nota: La contraseña para eluser
en todas las conexiones que la requieran (psql, Adminer) espassword
, tal como se define en el ficherodocker-compose.yml
.
Paso 2: Ejecuta los Ejemplos y Compara
Para los ejemplos de terminal, usaremos el contenedor pg_client
.
Cómo usar la terminal:
- Abre una sesión de terminal dentro del contenedor cliente con este comando:
docker exec -it pg_client bash
- Una vez dentro (verás un prompt tipo
root@...:/#
), ya puedes lanzar los comandospsql
contra las bases de datos usando sus nombres de servicio (db_scram
odb_md5
).
Conéctate a la instancia SCRAM (segura):
- Terminal: Dentro del contenedor
pg_client
, ejecuta:
psql -h db_scram -U user -d testdb
- Adminer (http://localhost:8080): Servidor:
db_scram

Ejemplo 1: El nuevo uuidv7()
-- Compara los resultados de los dos tipos de UUID
SELECT uuidv4(), uuidv7();
Ejecuta la consulta varias veces. Notarás que los uuidv4()
son completamente aleatorios, mientras que los uuidv7()
empiezan con una secuencia similar que cambia ligeramente. Esa parte inicial es el timestamp, ¡lo que permite que el índice los ordene eficientemente!
testdb=# SELECT uuidv4(), uuidv7();
uuidv4 | uuidv7
--------------------------------------+--------------------------------------
a3c361d2-4aa2-49fe-84fd-247d249484e0 | 019999e7-70ce-7e41-ad73-97cd6e42f2a8
(1 row)
testdb=# SELECT uuidv4(), uuidv7();
uuidv4 | uuidv7
--------------------------------------+--------------------------------------
3c8115a7-8667-47e2-971c-b7ce61504572 | 019999e7-7713-75db-a7ff-e7c2ebbfb47f
(1 row)
testdb=# SELECT uuidv4(), uuidv7();
uuidv4 | uuidv7
--------------------------------------+--------------------------------------
1fc5216d-a874-418b-abcb-0fd389823abc | 019999e7-7c0f-7a0a-a364-6a3a536a0b6c
(1 row)
Ejemplo 2: Columnas Generadas Virtuales
CREATE TABLE products (
name TEXT,
price_eur NUMERIC,
price_usd NUMERIC GENERATED ALWAYS AS (price_eur * 1.07) VIRTUAL
);
INSERT INTO products (name, price_eur) VALUES ('Teclado Mecánico', 80);
SELECT * FROM products;
La columna price_usd
no ocupa espacio en disco, se calcula cada vez que la pides.
testdb=# CREATE TABLE products (
name TEXT,
price_eur NUMERIC,
price_usd NUMERIC GENERATED ALWAYS AS (price_eur * 1.07) VIRTUAL
);
CREATE TABLE
testdb=# INSERT INTO products (name, price_eur) VALUES ('Teclado Mecánico', 80);
INSERT 0 1
testdb=# SELECT * FROM products;
name | price_eur | price_usd
------------------+-----------+-----------
Teclado Mecánico | 80 | 85.60
(1 row)
Ejemplo 3: RETURNING
con datos antiguos y nuevos
CREATE TABLE price_audit (
product_name TEXT,
old_price NUMERIC,
new_price NUMERIC,
changed_at TIMESTAMPTZ DEFAULT now()
);
WITH updated AS (
UPDATE products
SET price_eur = 85
WHERE name = 'Teclado Mecánico'
RETURNING OLD.name, OLD.price_eur AS old_price, NEW.price_eur AS new_price
)
INSERT INTO price_audit (product_name, old_price, new_price)
SELECT name, old_price, new_price FROM updated;
SELECT * FROM price_audit;
¡Magia! Sin triggers complejos ni dobles consultas.
testdb=# CREATE TABLE price_audit (
product_name TEXT,
old_price NUMERIC,
new_price NUMERIC,
changed_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE
testdb=# WITH updated AS (
UPDATE products
SET price_eur = 85
WHERE name = 'Teclado Mecánico'
RETURNING OLD.name, OLD.price_eur AS old_price, NEW.price_eur AS new_price
)
INSERT INTO price_audit (product_name, old_price, new_price)
SELECT name, old_price, new_price FROM updated;
INSERT 0 1
testdb=# SELECT * FROM price_audit;
product_name | old_price | new_price | changed_at
------------------+-----------+-----------+-------------------------------
Teclado Mecánico | 80 | 85 | 2025-09-30 09:16:50.260192+00
(1 row)
Paso 3: Experimentando con I/O Asíncrona (AIO)
Demostrar una mejora de rendimiento como la de AIO en un simple ejemplo es complicado, ya que depende mucho de tu hardware (especialmente de la velocidad de tus discos). Sin embargo, podemos crear un pequeño laboratorio para que puedas experimentar y medir el impacto en tu propio sistema.
Con nuestra configuración de Docker, tenemos el laboratorio perfecto: el servicio db_md5
arranca con la I/O síncrona (io_method=sync
), y el servicio db_scram
usa la configuración por defecto de PostgreSQL 18, que es con AIO activado.
1. Prepara un set de datos grande
Vamos a usar pgbench para crear datos de prueba en ambas instancias.
- Desde tu terminal, ejecuta estos dos comandos. Cada uno tardará un par de minutos en poblar su respectiva base de datos con ~1.5 GB de datos:
# Poblar la base de datos SIN AIO (db_md5)
docker exec pg_client bash -c "PGPASSWORD=password pgbench -i -s 100 -h db_md5 -U user testdb"
# Poblar la base de datos CON AIO (db_scram)
docker exec pg_client bash -c "PGPASSWORD=password pgbench -i -s 100 -h db_scram -U user testdb"
2. Lanza las pruebas y compara
Para una comparación justa, primero reiniciaremos ambos contenedores para limpiar las cachés de memoria.
- Desde tu terminal, ejecuta:
docker restart postgres18_md5 postgres18_scram
Espera unos segundos a que las bases de datos se inicien.
- Ahora, entra en el contenedor cliente para lanzar las pruebas:
docker exec -it pg_client bash
- Prueba en el servidor SIN AIO:
# Conecta a db_md5 y mide el tiempo
PGPASSWORD=password psql -h db_md5 -U user -d testdb -c "\timing on" -c "SELECT count(*) FROM pgbench_accounts;"
Anota el tiempo que te devuelve.
root@5dfdfaef5e1a:/# PGPASSWORD=password psql -h db_md5 -U user -d testdb -c "\timing on" -c "SELECT count(*) FROM pgbench_accounts;"
Timing is on.
count
----------
10000000
(1 row)
Time: 828.820 ms
- Prueba en el servidor CON AIO:
# Conecta a db_scram y mide el tiempo
PGPASSWORD=password psql -h db_scram -U user -d testdb -c "\timing on" -c "SELECT count(*) FROM pgbench_accounts;"
Anota este segundo tiempo.
root@5dfdfaef5e1a:/# PGPASSWORD=password psql -h db_scram -U user -d testdb -c "\timing on" -c "SELECT count(*) FROM pgbench_accounts;"
Timing is on.
count
----------
10000000
(1 row)
Time: 539.800 ms
3. Analiza los Resultados
Compara el tiempo de la segunda prueba (CON AIO) con el de la primera (SIN AIO). En un sistema con discos rápidos (SSD/NVMe), deberías ver una reducción notable en el tiempo de ejecución. En mi caso pasamos de 828.820 ms a 539.800 ms una mejora del 34.87%.
Importante: La mejora que observes dependerá enormemente de tu hardware, tu sistema operativo y la configuración de Docker. El objetivo de este experimento es darte las herramientas para que puedas medirlo en tu propio entorno.
Paso 4: La Comparativa de Autenticación Definitiva
Ahora, vamos a crear un usuario en cada base de datos para ver la diferencia crucial.
1. En la instancia MD5 (insegura):
Conéctate a ella:
- Terminal: Dentro del contenedor
pg_client
, ejecuta:
psql -h db_md5 -U user -d testdb
- Adminer: Servidor:
db_md5
Y ejecuta el siguiente SQL:
CREATE USER test_md5_user WITH PASSWORD 'a_simple_password';
SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'test_md5_user';
El resultado mostrará el hash MD5, fácil de identificar.
testdb=# CREATE USER test_md5_user WITH PASSWORD 'a_simple_password';
SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'test_md5_user';
WARNING: setting an MD5-encrypted password
DETAIL: MD5 password support is deprecated and will be removed in a future release of PostgreSQL.
HINT: Refer to the PostgreSQL documentation for details about migrating to another password type.
CREATE ROLE
rolname | rolpassword
---------------+-------------------------------------
test_md5_user | md5fb0260dca3cede73533e7b3a2878807b
(1 row)
2. En la instancia SCRAM (segura):
Conéctate a ella:
- Terminal: Dentro del contenedor
pg_client
, ejecuta:
psql -h db_scram -U user -d testdb
- Adminer: Servidor:
db_scram
Y ejecuta este otro SQL:
CREATE USER test_scram_user WITH PASSWORD 'a_better_password';
SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'test_scram_user';
El resultado es radicalmente diferente y mucho más robusto.
testdb=# CREATE USER test_scram_user WITH PASSWORD 'a_better_password';
SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'test_scram_user';
CREATE ROLE
rolname | rolpassword
-----------------+---------------------------------------------------------------------------------------------------------------------------------------
test_scram_user | SCRAM-SHA-256$4096:aZ2+SHpETCGDXKENOKupcA==$RlOpd+bI6rtWQmrqsQg69v7m1A1WPeL+tYqG1IUz9wk=:hJfldXgHIIhsZR35pzJ5EiGbp7MPGEKkljho2Ju2dxY=
(1 row)
Esta comparación lado a lado no deja lugar a dudas. Con un único comando docker-compose up
hemos creado un laboratorio perfecto para visualizar por qué la comunidad ha decidido finalmente dar carpetazo a MD5 y por qué SCRAM es el estándar que ya deberías estar usando.
Conclusión
PostgreSQL 18 es una versión madura y contundente. No se centra en añadir cientos de pequeñas funciones, sino en resolver problemas fundamentales a gran escala: el rendimiento de la I/O, el dolor de las actualizaciones y la eficiencia en patrones de desarrollo modernos.
Es una declaración de intenciones que consolida a PostgreSQL no solo como una base de datos robusta y fiable, sino como una plataforma de datos de altísimo rendimiento preparada para los desafíos del futuro.
Ahora te toca a ti, ¿qué funcionalidad te ilusiona más? ¿Vas a empezar a probar ya la I/O asíncrona? ¡Te leo en los comentarios!
Fuente y Lectura Adicional
- Anuncio Oficial
