Saltar al contenido principal
DB-1301 • 36 Horas • Nivel Avanzado / Principal Distributed SQL Architect

SQL Distribuido (NewSQL) y Alta Disponibilidad Extrema: CockroachDB, Google Cloud Spanner, TiDB, Raft y PgBouncer

Arquitectura de motores NewSQL sobre KV transaccional (Pebble/RocksDB), grupos Multi-Raft y Leaseholders, relojes atómicos TrueTime vs Hybrid Logical Clocks (HLC), aislamiento Serializable sin locks de lectura y Connection Pooling con PgBouncer.

Módulo 1: Módulo 1: De RDBMS Monolítico a SQL Distribuido (NewSQL): Rangos, Multi-Raft y Almacenamiento KV

Cómo una Tabla SQL Se Convierte en un Espacio Clave-Valor Ordenado: Codificación Lexicográfica y Ranges de 512 MB (50 min)

Cómo una Tabla SQL Se Convierte en un Espacio Clave-Valor Ordenado: Codificación Lexicográfica y Ranges de 512 MB

1. Mapeo de Filas e Índices Secundarios a un Único Espacio Monolítico de Llaves

Motores NewSQL como CockroachDB, TiDB (TiKV) y YugabyteDB compilan consultas PostgreSQL/MySQL hacia un motor transaccional Clave-Valor subyacente basado en LSM-Trees (Pebble o RocksDB). Cada fila de tabla e índice secundario se codifica de forma determinista y lexicográficamente ordenable:

/TableID/IndexID/PrimaryKeyColumnValues/ColumnFamilyID@[HybridTimestamp] -> ValueBytes
Ejemplo Fila Primaria:   /104/1/"acc_8892"/[email protected] -> { balance: 15400.00, status: "ACTIVE" }
Ejemplo Índice Secund.:  /104/2/"ACTIVE"/"acc_8892"@1729550100.000001 -> Empty (Apunta a la PK)

2. Particionamiento Automático en Ranges (64 MB – 512 MB) y Multi-Raft

El espacio continuo de claves se divide dinámicamente en Ranges (en CockroachDB) o Regions (en TiKV) de $64\text{ MB}$ a $512\text{ MB}$. Cada Range constituye un grupo de consenso Raft independiente (típicamente con $3$ o $5$ réplicas distribuidas en distintas zonas de disponibilidad):

flowchart LR
    SQL["Capa SQL Stateless (Parser / Cost-Based Optimizer)"] --> KV["DistSender (Enrutador de Rangos)"]
    KV -->|"Range [A..M]"| L1["Leaseholder Raft Grupo 1 (AZ-1)"]
    L1 -->|"AppendEntries Quórum"| F1A["Follower Raft 1B (AZ-2)"]
    L1 -->|"AppendEntries Quórum"| F1B["Follower Raft 1C (AZ-3)"]

El Rol del Leaseholder en Multi-Raft, Follower Reads acotados y Parallel Commits en 1 RTT (50 min)

El Rol del Leaseholder en Multi-Raft, Follower Reads acotados y Parallel Commits en 1 RTT

1. Cómo Evitar Pasar por Quórum Raft en Cada Lectura: Range Leases

Si cada SELECT requiriera una ronda de consenso Raft entre 3 zonas de disponibilidad, la latencia de lectura sería prohibitiva. Por ello, una réplica del grupo Raft adquiere un Range Lease temporal exclusivo (Leaseholder): - Lecturas Fuertemente Consistentes en $0$ RTT de Raft: Mientras su lease esté vigente, el Leaseholder sirve lecturas serializables directamente desde su memoria/disco local sin consultar a los seguidores, sabiendo que ninguna escritura pudo ocurrir sin pasar por él. - Follower Reads (AS OF SYSTEM TIME '-5s'): Permite que cualquier réplica local sirva lecturas históricas inmutables con latencia sub-milisegundo sin contactar al Leaseholder remoto.

2. Parallel Commits: Reducción de 2 RTT a 1 RTT en Transacciones Distribuidas

En un protocolo 2PC tradicional sobre Raft, escribir los Write Intents toma 1 RTT de consenso y marcar el registro de transacción como COMMITTED toma un 2do RTT secuencial. Parallel Commits en CockroachDB ejecuta el consenso de los Write Intents y el registro STAGING de la transacción en paralelo en 1 solo RTT, logrando velocidad cercana al límite físico de la fibra óptica.

Prevención de Range Hotspots en NewSQL: Por Qué SERIAL/BIGSERIAL Destruye un Clúster Multi-Raft (45 min)

Prevención de Range Hotspots en NewSQL: Por Qué SERIAL/BIGSERIAL Destruye un Clúster Multi-Raft

1. El Anti-Patrón de las Claves Autoincrementales en SQL Distribuido

Dado que los Ranges se dividen por orden lexicográfico de la clave primaria, usar id BIGSERIAL PRIMARY KEY o un índice secundario sobre created_at dirige el $100\%$ de las escrituras concurrentes al último Range del clúster, saturando a un único Leaseholder mientras otros 30 nodos tienen $0\%$ de uso de CPU.

Soluciones de Producción en SQL Distribuido: 1. Claves Primarias UUID DEFAULT gen_random_uuid() o prefijadas con shard_bucket. 2. Hash-Sharded Indexes para columnas temporales monotónicas (CREATE INDEX ON orders (created_at) USING HASH WITH (bucket_count = 16)), que anteponen un prefijo crdb_internal_created_at_shard_16 = mod(fnv32(created_at), 16) para repartir el índice entre 16 Ranges físicos.

Módulo 2: Módulo 2: Sincronización de Tiempo Físico/Lógico: Google Spanner TrueTime vs. Relojes HLC

Google Cloud Spanner y la API TrueTime: Relojes Atómicos + GPS y la Garantía de Commit Wait (2ε) (55 min)

Google Cloud Spanner y la API TrueTime: Relojes Atómicos + GPS y la Garantía de Commit Wait (2ε)

1. Consistencia Externa (Linearizability Global) Sin Comunicación Entre Transacciones Disjuntas

Si la Transacción $T_1$ confirma en Tokio en el instante físico absoluto $t_{\text{abs}}(T_1)$ y un microsegundo después comienza la Transacción $T_2$ en Virginia, Consistencia Externa exige que los timestamps asignados cumplan $s_1 < s_2$. En servidores estándar con NTP, la deriva de reloj (clock skew) puede ser de $50\text{ ms} - 250\text{ ms}$, violando esta invariante.

2. La Ecuación de Commit Wait en TrueTime

Google Cloud Spanner equipa cada datacenter con receptores GPS y Relojes Atómicos de Rubidio. La API TrueTime.now() no devuelve un punto, sino un intervalo garantizado $[t_{\text{earliest}}, t_{\text{latest}}]$ con incertidumbre mitad $\varepsilon = \frac{1}{2}(t_{\text{latest}} - t_{\text{earliest}}) \in [1\text{ ms}, 7\text{ ms}]$.

Para garantizar que ninguna transacción futura en el planeta vea un timestamp menor que $s_i$: 1. El coordinador elige $s_i \ge TT.\text{now}().\text{latest}$. 2. Regla de Commit Wait: Antes de hacer visible el commit o responder al cliente, el coordinador espera hasta que $TT.\text{now}().\text{earliest} > s_i$ (una espera física promedio de $2\varepsilon$).

Hybrid Logical Clocks (HLC) en Hardware Estándar: Causalidad de Lamport + Ventanas de Incertidumbre (50 min)

Hybrid Logical Clocks (HLC) en Hardware Estándar: Causalidad de Lamport + Ventanas de Incertidumbre

1. Cómo Lograr Aislamiento Serializable Sin Relojes Atómicos Propietarios

CockroachDB y YugabyteDB operan sobre nubes públicas y servidores bare-metal usando NTP/Chronyd mediante Relojes Lógicos Híbridos (HLC - Kulkarni et al.), donde cada timestamp es una tupla $(pt, lc)$: - $pt$: el máximo tiempo físico conocido por el nodo. - $lc$: un contador lógico que se incrementa cuando ocurren eventos causales dentro del mismo milisegundo físico.

2. Read Refresh y Reinicios por Incertidumbre (ReadWithinUncertaintyIntervalError)

Dado que el desfase máximo configurado entre nodos es $\text{max\_offset}$ (ej. $250\text{ ms}$), cuando una transacción con timestamp $t_{\text{read}}$ encuentra en otro nodo un valor escrito en el intervalo de incertidumbre $(t_{\text{read}}, ; t_{\text{read}} + \text{max\_offset}]$: - No puede saber si esa escritura ocurrió físicamente antes o después de que iniciara la transacción. - El motor adelanta el timestamp de lectura (Timestamp Bump / Read Refresh) y reintenta la lectura internamente de forma transparente para preservar Serializability.'

Topologías Geo-Distribuidas y Soberanía de Datos: Regional-by-Row, Global Tables y Survival Goals (45 min)

Topologías Geo-Distribuidas y Soberanía de Datos: Regional-by-Row, Global Tables y Survival Goals

1. Patrones de Localidad de Tabla en Clústeres Multi-Región

1. REGIONAL BY ROW (Domiciliación Automática por Fila): Añade una columna oculta o explícita crdb_region que fija las réplicas y el Leaseholder de cada fila en la jurisdicción del usuario (eu-central-1 para GDPR, sa-east-1 para LGPD), logrando lecturas y escrituras locales en $< 10\text{ ms}$. 2. GLOBAL Tables (Catálogos de Alta Lectura y Baja Escritura): Utiliza el protocolo Non-Voting Replicas + Closed Timestamps para permitir lecturas consistentes a $0\text{ ms}$ en todas las regiones del mundo a cambio de una pequeña espera en las escrituras.

Módulo 3: Módulo 3: Alta Disponibilidad en PostgreSQL (Patroni/Raft) y Connection Pooling a Escala (PgBouncer)

Por Qué 2,000 Conexiones Directas Destruyen PostgreSQL: Procesos por Conexión, Memoria y Lock Contention (50 min)

Por Qué 2,000 Conexiones Directas Destruyen PostgreSQL: Procesos por Conexión, Memoria y Lock Contention

1. El Modelo Process-Per-Connection de PostgreSQL

Cada conexión TCP directa a postmaster lanza un proceso del sistema operativo independiente (postgres backend process) que consume entre $5\text{ MB}$ y $15\text{ MB}$ de RAM privada (work_mem, catálogos cacheados, tabla de descriptores) y compite por Spinlocks y LWLocks sobre shared_buffers.

2. La Fórmula Empírica de Dimensionamiento del Pool de Conexiones Activas

Contrario a la intuición de abrir cientos de conexiones, el máximo throughput transaccional de un servidor PostgreSQL con $C_{\text{cores}}$ núcleos físicos y discos SSD NVMe se alcanza con un pool sorprendentemente pequeño:

$N_{\text{connections\_optimal}} = (C_{\text{cores}} \times 2) + N_{\text{effective\_spindle\_queues}}$

Para un servidor de 16 vCPUs, el número óptimo de conexiones ejecutando consultas simultáneamente en el motor es de $32$ a $48$ conexiones. Superar las $500$ conexiones directas activas reduce los TPS hasta en un $60\%$ por CPU Context Switching y contención de ProcArrayLock.

Arquitectura de PgBouncer: Transaction Pooling vs. Session Pooling y Soporte de Prepared Statements (50 min)

Arquitectura de PgBouncer: Transaction Pooling vs. Session Pooling y Soporte de Prepared Statements

1. Multiplexación de 10,000 Clientes Microservicio sobre 40 Conexiones Reales

flowchart LR
    MS["200 Pods de Microservicios (10,000 Conexiones TCP Ligeras)"] --> PGB["PgBouncer Pooler (Modo: pool_mode = transaction)"]
    PGB -->|"Reutiliza en cada COMMIT/ROLLBACK"| PG["PostgreSQL Primary (Solo 40 Backend Processes Reales)"]

- pool_mode = session: Retiene la conexión de servidor durante toda la vida del cliente (solo ahorra el handshake TCP/TLS, no multiplexa clientes inactivos). - pool_mode = transaction (Estándar de Producción): Asigna una conexión real de PostgreSQL únicamente durante los milisegundos que dura BEGIN ... COMMIT y la devuelve inmediatamente al pool. > Regla Crítica en Modo Transacción: Queda prohibido usar estado de sesión persistente (SET search_path fuera de SET LOCAL, LISTEN/NOTIFY, advisory locks de sesión). En PgBouncer 1.21+, activar max_prepared_statements = 200 permite multiplexar Prepared Statements con protocolo binario entre distintos clientes de forma transparente.

Alta Disponibilidad sin Split-Brain: Patroni + etcd/Raft, Replicación Síncrona Quorum y Fencing (STONITH) (45 min)

Alta Disponibilidad sin Split-Brain: Patroni + etcd/Raft, Replicación Síncrona Quorum y Fencing (STONITH)

1. Prevención Matemática de Split-Brain con Leases en etcd y Watchdog de Hardware

En un clúster HA de PostgreSQL gestionado por Patroni: 1. El nodo Líder debe renovar su llave de liderazgo en etcd (consenso Raft) cada loop_wait = 10s dentro de ttl = 30s. 2. Si el Líder pierde conectividad con el quórum de etcd, degrada su instancia local a solo-lectura inmediatamente; y si el proceso de Patroni se congela, el dispositivo /dev/watchdog del kernel Linux fuerza un reinicio físico instantáneo (STONITH) antes de que expire el TTL y otra réplica sea promovida. 3. Para RPO = 0 (Cero Pérdida de Datos), se configura synchronous_commit = remote_write (o on) con synchronous_standby_names = 'ANY 1 (node2, node3)'.

Módulo 4: Módulo 4: Laboratorio de Dimensionamiento PgBouncer y Evaluación de Certificación

Read/Write Splitting Consciente del LSN (Log Sequence Number): Evitando Lecturas Obsoletas en Réplicas (45 min)

Read/Write Splitting Consciente del LSN (Log Sequence Number): Evitando Lecturas Obsoletas en Réplicas

Cuando un usuario actualiza su perfil en el nodo Primario, la transacción retorna el LSN de commit (pg_current_wal_insert_lsn(), ej. 0/16B3748). Para garantizar Read-Your-Own-Writes al enrutar la siguiente lectura hacia una réplica de lectura, el proxy verifica que pg_last_wal_replay_lsn() >= '0/16B3748'; si la réplica tiene un retraso de $50\text{ ms}$, espera unos milisegundos o enruta esa lectura puntual al primario.

Laboratorio Práctico: Auditoría de Multiplexación PgBouncer y Dimensionamiento Óptimo por Cores (60 min)

Laboratorio Práctico: Auditoría de Multiplexación PgBouncer y Dimensionamiento Óptimo por Cores

Contexto del Desafío de Arquitectura DB

La tabla db_cluster_pool_audit registra la configuración actual de 4 clústeres de bases de datos transaccionales: - cluster_name (TEXT) - cpu_cores (INTEGER: núcleos físicos del servidor primario) - app_client_connections (INTEGER: conexiones entrantes desde los pods de microservicios) - current_server_pool_size (INTEGER: conexiones reales abiertas hacia PostgreSQL)

Según nuestra fórmula de ingeniería de capacidad para SSD NVMe ($2$ colas efectivas de disco), el tamaño óptimo del pool de servidor es: $\text{recommended\_pool\_size} = (\text{cpu\_cores} \times 2) + 2$

Objetivo de la Consulta SQL

Escribe una consulta SQL que devuelva para cada clúster: 1. cluster_name 2. recommended_pool_size: (cpu_cores * 2) + 2 3. multiplex_ratio: razón de compresión de conexiones ROUND(1.0 * app_client_connections / current_server_pool_size, 2) 4. pool_diagnosis: 'OVERSIZED_CONTENTION_RISK' si current_server_pool_size > ((cpu_cores 2) + 2) 2, o 'OPTIMAL_POOL' en caso contrario. Ordena el resultado por cluster_name ASC.

Evaluación de Certificación: SQL Distribuido (NewSQL), TrueTime/HLC y Alta Disponibilidad (45 min)

Evaluación de Certificación: DB-1301

Demuestra tu dominio sobre motores NewSQL (Spanner, CockroachDB), relojes TrueTime vs HLC, Multi-Raft y PgBouncer.