Introducción
En fintech, cada decisión de arquitectura impacta directamente en la velocidad de innovación y el costo operativo. Un caso paradigmático es el de una empresa B2B de pagos y cashback que migró su base de datos de PostgreSQL 12 a MariaDB 11 en un año, sin downtime y con un ahorro del 23% en TCO. La clave no fue solo cambiar de motor, sino resolver un problema de arquitectura: un monólito relacional que ya no escalaba para 1.000 microservicios y mezclaba cargas OLTP con consultas analíticas puntuales.
El desafío técnico era claro: 80–100 TB de datos, picos de 10 millones de consultas por día durante campañas promocionales, y una arquitectura que requería sharding horizontal por períodos temporales (semanas o meses). PostgreSQL 12 seguía funcionando, pero el cuello de botella era la rigidez del diseño. La migración no podía ser un «big bang» —el SLA exigía 99.95% de disponibilidad— ni un simple upgrade de versión. Necesitaban flexibilidad, escalabilidad por servicio y, sobre todo, tiempo para iterar sin congelar el desarrollo de producto.
Qué ocurrió
La empresa ya había comenzado a descomponer su base de datos monolítica con microservicios, pero seguía atada a un único motor PostgreSQL. Aunque PostgreSQL 12 podía almacenar los datos, el diseño relacional centralizado generaba:
- Acceso concurrente a tablas críticas: múltiples servicios leían/escribían en las mismas tablas, complicando operaciones y escalado.
- Mezcla de cargas: 60% de lecturas y 40% de escrituras en el servicio principal, pero con consultas analíticas semanales/mensuales en tablas grandes.
- Fricción operativa: sharding parcial en aplicación, pero sin separación clara de cargas OLTP vs. OLAP.
El equipo evaluó opciones como PostgreSQL + ClickHouse (rechazado por falta de expertise en ClickHouse) o Citus (que no resolvía el componente analítico). La solución llegó con MariaDB 11, que permitía:
- Galera Cluster para alta disponibilidad con replicación síncrona.
- ColumnStore para consultas analíticas sin necesidad de otro motor.
- Soporte nativo de JSON y funciones de ventana sin migrar schemas complejos.
- Compatibilidad con el ecosistema existente: el equipo ya conocía MySQL/MariaDB, lo que redujo la curva de aprendizaje.
La migración no fue lineal. En lugar de mover todo de golpe, adoptaron un enfoque gradual por objetos (tablas, particiones o schemas poco acoplados), usando CDC (Change Data Capture) con Otterstax como capa de enrutamiento y proxy. Así, una misma tabla podía coexistir en PostgreSQL y MariaDB durante semanas, con writes duales (ambos motores) y validación de consistencia en tiempo real.
Impacto para DevOps / Infraestructura / Cloud
Para equipos de DevOps
- Reducción de 23% en TCO: el ahorro vino de consolidar cargas en un solo motor (MariaDB 11) en lugar de mantener PostgreSQL + herramientas adicionales. El equipo estimó que un upgrade a PostgreSQL 15 habría requerido 2 años de ajustes en queries y schemas.
- Escalado independiente por servicio: al separar OLTP (InnoDB) de OLAP (ColumnStore), los servicios críticos ya no competían por recursos con consultas batch.
- Menor complejidad operativa: eliminaron la necesidad de mantener pgpool/pgbouncer para PostgreSQL, reemplazados por Otterstax como capa unificada de enrutamiento.
Para equipos de Infraestructura
- Migración sin downtime: el SLA de 99.95% se mantuvo durante todo el proceso. La estrategia de dual writes (escribir en ambos motores y validar consistencia) permitió cambios incrementales en producción.
- Sharding por tiempo: la estrategia de particionar datos por períodos (semanas/meses) facilitó la migración por bloques lógicos, no por tamaño. Esto evitó reorganizaciones masivas de datos.
- Bare metal vs. cloud: aunque el caso usó bare metal, el equipo evaluó Kubernetes (EKS) para futuras expansiones, pero optó por bare metal por latencia en transacciones financieras.
Para equipos de Seguridad
- Control de acceso granular: MariaDB 11 permitió implementar roles específicos por microservicio (ej: un servicio de cashback solo podía escribir en tablas de descuentos).
- Auditoría integrada: la capa de Otterstax registraba todos los writes duales y cross-database joins, facilitando trazas para cumplimiento normativo (ej: PCI DSS).
- Reducción de superficie de ataque: al consolidar motores, se eliminaron puntos de integración expuestos entre PostgreSQL y otros sistemas (como ClickHouse).
- Volumen migrado: 80–100 TB del servicio principal + 10 TB por servicio secundario.
- Tiempo de migración: 12 meses (desde planificación hasta corte final).
- Mejoras en time-to-market: 20% más rápido en despliegue de nuevas features post-migración (por menor complejidad en cambios de schema).
- Rendimiento: los picos de 10 millones de consultas/día se manejaron con latencias estables gracias a ColumnStore para consultas analíticas y Galera para alta disponibilidad.
Detalles técnicos
Arquitectura original (PostgreSQL 12)
- Topología: 1 primario + 2 standby (1 en mismo nodo, 1 en nodo secundario).
- Tamaño de instancia: bare metal con 512 GB RAM y 40 TB NVMe en RAID 10.
- Componentes:
– pgbouncer 1.20 (pool de conexiones).
– Sharding parcial: en aplicación, por períodos temporales (ej: transacciones de enero 2023 en un shard, febrero en otro).
- Workload:
– OLAP: consultas semanales/mensuales en tablas de 50+ GB por meses (usando pg_partman para particionado automático).
Arquitectura objetivo (MariaDB 11)
- Motor principal: MariaDB 11.1 (con Galera Cluster 4.x para replicación síncrona).
- Componentes:
– ColumnStore: para consultas analíticas (reportes de marketing, KPIs).
– Otterstax 2.4: capa de enrutamiento y proxy con:
– Soporte para dual writes y CDC (vía OTTERSTAX CDC connector).
– Cross-database joins: para consultas que necesitaban datos de ambos motores.
– Write-ahead log interno: para garantizar consistencia durante la transición.
– Validación de datos: comparaciones hash periódicas entre fuentes.
- Topología:
– Sharding: mantenido por períodos temporales, pero ahora con particiones físicas en MariaDB (usando SYSTEM VERSIONING).
Tecnologías clave y versiones
| Componente | Versión | Rol |
|---|---|---|
| MariaDB | 11.1 | Motor principal (OLTP + OLAP) |
| ColumnStore | 11.1 | Motor analítico embebido |
| Galera Cluster | 4.11 | Replicación síncrona multi-master |
| Otterstax | 2.4 | Proxy + CDC + enrutamiento |
| pgpool-II | 4.3 | Reemplazado durante migración |
| pgbouncer | 1.20 | Reemplazado durante migración |
| Python (servicios) | 3.10 | Lógica de negocio (con adaptaciones) |
| Go (servicios) | 1.21 | Lógica de negocio (con adaptaciones) |
- Diferencias en SQL dialecto:
ILIKE; MariaDB usa LIKE con COLLATE utf8mb4_general_ci.– Solución: Otterstax implementó un parser SQL adaptativo que normalizaba queries antes de enviarlas a cada motor.
– Ejemplo:
-- PostgreSQL original
SELECT * FROM users WHERE email ILIKE '%@example.com';
-- Adaptado para MariaDB
SELECT * FROM users WHERE email LIKE '%@example.com' COLLATE utf8mb4_general_ci;
- Funciones de ventana:
OVER (PARTITION BY ...); MariaDB requiere OVER (PARTITION BY ... ORDER BY ...).– Solución: Otterstax interceptaba queries con WINDOW y las reescribía dinámicamente.
- JSON:
->>; MariaDB usa JSON_UNQUOTE(JSON_EXTRACT(...)).– Solución: se normalizaron todos los queries a usar JSON_VALUE (MariaDB 11.1).
- JOINs complejos:
MERGE JOIN; en MariaDB, el optimizador elegía HASH JOIN o NESTED LOOP.– Solución: se añadieron hints en queries críticas (ej: /*+ HASH_JOIN(t1, t2) */).
- Consistencia durante dual writes:
– Mecanismo:
– Otterstax escribía en ambos motores con un token de secuencia (basado en LSN de PostgreSQL).
– Validaba que COUNT(*) y SUM(amount) coincidieran en ambas fuentes cada 5 minutos.
Qué deberían hacer los administradores y equipos técnicos
Paso 1: Evaluar la arquitectura actual (no solo el motor)
Antes de migrar, analicen:
- ¿Hay un solo punto de falla? (Ej: un monólito relacional).
- ¿Qué porcentaje de consultas son analíticas? Si supera el 20%, evalúen ColumnStore o un motor dedicado.
- ¿Cuántos servicios acceden a las mismas tablas? Si son >10, consideren sharding por dominio o período.
# Para PostgreSQL: analizar queries lentas
SELECT query, calls, total_time
FROM pg_stat_statements
ORDER BY total_time DESC LIMIT 20;
# Para MariaDB: identificar tablas grandes
SELECT table_name, data_length, index_length
FROM information_schema.tables
WHERE table_schema = 'nombre_bd'
ORDER BY data_length DESC;Paso 2: Diseñar la migración gradual
Recomendación: migren por objetos poco acoplados (tablas independientes o particiones). Ejemplo de orden lógico:- Tablas de referencia (ej:
countries,currencies). - Tablas de logs o históricos (ej:
transaction_log_2023). - Tablas de usuarios y transacciones (empezando por las menos críticas).
-- En MariaDB (usando SYSTEM VERSIONING)
ALTER TABLE transactions
ADD COLUMN sys_period DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
ADD PERIOD FOR SYSTEM_TIME (sys_period);Paso 3: Implementar la capa de enrutamiento (Otterstax u alternativa)
Si no usan Otterstax, consideren alternativas como:
- ProxySQL: para balanceo de carga y reescritura de queries.
- pgCat: si prefieren mantener un enfoque PostgreSQL-centrico pero con sharding.
# /etc/proxysql.cnf
datadir="/var/lib/proxysql"
admin_variables:
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
mysql_variables:
monitor_enabled=1
mysql_connect_timeout=2000
mysql_read_only=1
mysql_servers =
(hostgroup=10, hostname=10.0.0.1, port=3306) # MariaDB primario
(hostgroup=20, hostname=10.0.0.2, port=3306) # MariaDB réplicaPaso 4: Migrar datos con CDC
Opción A: Usando Otterstax CDC:- Configuren el conector en ambos motores:
# En PostgreSQL (para capturar cambios)
otterstax-cdc --source postgresql --host 10.0.0.1 --user cdc_user --password XXX
# En MariaDB (para aplicar cambios)
otterstax-cdc --target mariadb --host 10.0.0.3 --user cdc_user --password YYY
- Validación automática cada hora:
# Script de Python para validar consistencia
import hashlib
def validate_table(table_name):
pg_hash = hashlib.sha256(execute_query("SELECT * FROM {} ORDER BY 1".format(table_name)).encode()).hexdigest()
mariadb_hash = hashlib.sha256(execute_query("SELECT * FROM {} ORDER BY 1".format(table_name)).encode()).hexdigest()
assert pg_hash == mariadb_hash, f"Inconsistencia en {table_name}"
Opción B: Sin CDC (para tablas pequeñas):# Exportar a CSV y cargar en MariaDB
psql -h pg-primary -U user -d db -c "\COPY (SELECT * FROM users WHERE id < 100000) TO STDOUT WITH CSV HEADER" > users_1.csv
mariadb -h mariadb-primary -u user -p -e "LOAD DATA INFILE '/tmp/users_1.csv' INTO TABLE users FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n' IGNORE 1 ROWS;"Paso 5: Validar y promover al nuevo motor
Checklist post-migración:- [ ] Ejecutar
EXPLAIN ANALYZEen las 100 queries más frecuentes y comparar planes de ejecución. - [ ] Verificar índices: en MariaDB, algunos índices de PostgreSQL (ej: GIN para JSON) no tienen equivalencia directa.
- [ ] Probar transacciones distribuidas: si usan XA (ej: en pagos), validen que
COMMITfuncione en ambos motores durante el período de dual writes.
SHOW INDEX FROM transactions WHERE Table = 'transactions';Paso 6: Optimizar y modernizar
Una vez migrados, aprovechen las capacidades de MariaDB 11:
- Para OLAP: activar ColumnStore en tablas específicas:
ALTER TABLE marketing_reports ENGINE=ColumnStore;
- Para alta disponibilidad: configurar Galera Cluster con
wsrep_sst_method=rsyncpara evitar bloqueos. - Para JSON: usar
JSON_TABLEpara queries complejas:
SELECT j.id, j.data->>'$.amount' AS amount
FROM json_data j, JSON_TABLE(j.data, '$' COLUMNS (id INT PATH '$.id')) AS t;
Conclusión
La migración de PostgreSQL 12 a MariaDB 11 en este caso de fintech no fue un cambio de motor, sino una evolución arquitectónica. El equipo logró:
- Reducir el TCO en un 23% sin sacrificar rendimiento.
- Separar cargas OLTP y OLAP en un solo motor (gracias a ColumnStore).
- Mantener el SLA de 99.95% con una estrategia de dual writes y validación incremental.
- Liberar al equipo de desarrollo para iterar más rápido (20% menos tiempo en despliegues).
- No migren solo por versión: evalúen si el problema es de arquitectura, no de tecnología.
- Migrar por objetos, no por tamaño: tablas pequeñas o históricas son ideales para empezar.
- Usen una capa de enrutamiento: Otterstax, ProxySQL o similares simplifican dual writes y validación.
- Aprovechen las capacidades nativas: ColumnStore para analytics, Galera para HA, y JSON para flexibilidad.
Para equipos en situaciones similares, el enfoque gradual combinado con herramientas de CDC es la opción más segura. Y recuerden: la migración no termina cuando el último dato se mueve; termina cuando el nuevo sistema demuestra ser más estable y flexible que el anterior.
