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).
Datos cuantitativos:
  • 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:
pgpool-II 4.3 (balanceo de carga).

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:
OLTP principal: 1 millón de queries/día promedio (hasta 10 millones en campañas).

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:
InnoDB: para cargas OLTP (transacciones financieras).

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:
3 nodos Galera: 1 primario + 2 réplicas (misma configuración de hardware que PostgreSQL).

Sharding: mantenido por períodos temporales, pero ahora con particiones físicas en MariaDB (usando SYSTEM VERSIONING).

Tecnologías clave y versiones

ComponenteVersiónRol
MariaDB11.1Motor principal (OLTP + OLAP)
ColumnStore11.1Motor analítico embebido
Galera Cluster4.11Replicación síncrona multi-master
Otterstax2.4Proxy + CDC + enrutamiento
pgpool-II4.3Reemplazado durante migración
pgbouncer1.20Reemplazado durante migración
Python (servicios)3.10Lógica de negocio (con adaptaciones)
Go (servicios)1.21Lógica de negocio (con adaptaciones)
### Problemas técnicos comunes y soluciones
  1. Diferencias en SQL dialecto:
– PostgreSQL usa 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;
     
  1. Funciones de ventana:
– PostgreSQL usa OVER (PARTITION BY ...); MariaDB requiere OVER (PARTITION BY ... ORDER BY ...).

Solución: Otterstax interceptaba queries con WINDOW y las reescribía dinámicamente.

  1. JSON:
– PostgreSQL usa ->>; MariaDB usa JSON_UNQUOTE(JSON_EXTRACT(...)).

Solución: se normalizaron todos los queries a usar JSON_VALUE (MariaDB 11.1).

  1. JOINs complejos:
– En PostgreSQL, joins entre tablas grandes podían usar 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) */).

  1. Consistencia durante dual writes:
– Se usó Otterstax CDC para capturar cambios en PostgreSQL y aplicarlos en MariaDB con orden secuencial.

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.
Herramientas útiles:
# 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:
  1. Tablas de referencia (ej: countries, currencies).
  2. Tablas de logs o históricos (ej: transaction_log_2023).
  3. Tablas de usuarios y transacciones (empezando por las menos críticas).
Comando para sharding por período:
-- 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.
Ejemplo de configuración con ProxySQL (para MariaDB):
# /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éplica

Paso 4: Migrar datos con CDC

Opción A: Usando Otterstax CDC:
  1. 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
   
  1. 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 ANALYZE en 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 COMMIT funcione en ambos motores durante el período de dual writes.
Comando para verificar índices en MariaDB:
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=rsync para evitar bloqueos.
  • Para JSON: usar JSON_TABLE para 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).
Lecciones clave:
  1. No migren solo por versión: evalúen si el problema es de arquitectura, no de tecnología.
  2. Migrar por objetos, no por tamaño: tablas pequeñas o históricas son ideales para empezar.
  3. Usen una capa de enrutamiento: Otterstax, ProxySQL o similares simplifican dual writes y validación.
  4. 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.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *