<!-- Canonical: https://lucioduran.com/es/blog/kafka-vs-redpanda-event-streaming-benchmarks
Language: es
Content-SHA256: 5a0413d7dff95a37df61c598f5667b8ff3f6a55b986438cdbf7656466193acb9
-->

Infrastructure

# Kafka vs Redpanda: Benchmarks de Event Streaming y Análisis de Migración

2026-01-229 min lectura

Lucio Durán
Engineering Manager & AI Solutions Architect

También disponible en: [English](<https://lucioduran.com/blog/kafka-vs-redpanda-event-streaming-benchmarks>)

## La Diferencia Arquitectónica Que Importa

Las diferencias arquitectónicas fundamentales son el punto de partida, ya que todas las características de rendimiento se derivan de ellas.

**[Apache Kafka](<https://kafka.apache.org/>)** es una aplicación JVM. Está escrito en Scala y Java, corre en el JVM con su garbage collector asociado, y hasta hace poco requería ZooKeeper como servicio de coordinación separado. El modo KRaft (el consenso Raft built-in de Kafka) está production-ready desde Kafka 3.5, y ZooKeeper está oficialmente deprecado desde la 3.7.

**[Redpanda](<https://redpanda.com/>)** es una aplicación C++ construida sobre el framework Seastar, el mismo framework de I/O asíncrono que utiliza ScyllaDB. Sin JVM, sin garbage collector, sin ZooKeeper. Usa Raft para consenso nativamente, con cada partición teniendo su propio grupo Raft.

Esto no es solo una diferencia de lenguaje. Es una diferencia fundamental en cómo interactúan con el hardware.

### Thread-Per-Core vs Thread Pool

El modelo thread-per-core de Seastar significa que cada core de CPU corre un único thread que es dueño de su memoria, sus conexiones de red y su I/O de disco. Sin locks, sin context switches entre threads, sin memoria compartida entre cores.

```
Redpanda (Seastar/thread-per-core):
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Core 0 │ │ Core 1 │ │ Core 2 │ │ Core 3 │
│ Shard 0 │ │ Shard 1 │ │ Shard 2 │ │ Shard 3 │
│ Mem prop │ │ Mem prop │ │ Mem prop │ │ Mem prop │
│ I/O prop │ │ I/O prop │ │ I/O prop │ │ I/O prop │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
 ↕ message passing (sin estado compartido)

Kafka (JVM/thread pool):
┌───────────────────────────────────────────────────┐
│ JVM Heap (compartido) │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │Thread 1│ │Thread 2│ │Thread 3│ │Thread N│ │
│ └────┬───┘ └────┬───┘ └────┬───┘ └────┬───┘ │
│ └──────────┴──────────┴──────────┘ │
│ Acceso sincronizado │
│ Las pausas de GC afectan a TODOS │
└───────────────────────────────────────────────────┘
```

Los threads de Kafka comparten memoria del heap y coordinan a través de locks y el modelo de memoria del JVM. Cuando el collector G1 o ZGC arranca, todos los threads pausan (o al menos se ralentizan durante las fases concurrentes). Redpanda simplemente no tiene este problema, no hay GC porque no hay managed heap.

## Los Benchmarks

Las siguientes pruebas se corrieron en hardware idéntico: clusters de 3 nodos, cada nodo con 16 cores (AMD EPYC 7313), 64GB RAM, 2x NVMe SSDs en RAID 0, red de 25Gbps. Esto representa un setup "serio pero no extravagante".

### Test de Throughput

Configuración de producer: mensajes de 1KB, batching habilitado (` linger.ms=5 `, ` batch.size=65536 `), ` acks=all `, 6 particiones, factor de replicación 3.

```bash
# Benchmark Kafka
bin/kafka-producer-perf-test.sh \
 --topic bench-topic \
 --num-records 10000000 \
 --record-size 1024 \
 --throughput -1 \
 --producer-props \
 bootstrap.servers=kafka1:9092,kafka2:9092,kafka3:9092 \
 acks=all \
 linger.ms=5 \
 batch.size=65536

# Benchmark Redpanda (la misma herramienta funciona, API compatible)
bin/kafka-producer-perf-test.sh \
 --topic bench-topic \
 --num-records 10000000 \
 --record-size 1024 \
 --throughput -1 \
 --producer-props \
 bootstrap.servers=redpanda1:9092,redpanda2:9092,redpanda3:9092 \
 acks=all \
 linger.ms=5 \
 batch.size=65536
```

Resultados (10M mensajes, 1KB cada uno):

| Métrica | Kafka 3.7 (KRaft) | Redpanda 24.3 |
| --- | --- | --- |
| Throughput (MB/s) | 850 | 920 |
| Latencia avg | 3.2ms | 1.8ms |
| Latencia p50 | 2.1ms | 1.4ms |
| Latencia p99 | 28ms | 4.2ms |
| Latencia p99.9 | 145ms | 8.1ms |

Notemos el número de p99.9. **145ms vs 8.1ms**. Eso no es un error de redondeo, es el G1GC del JVM haciendo una mixed collection. Y esto fue después de un extenso tuning de flags del JVM:

```bash
# Tuning de JVM para Kafka (KAFKA_HEAP_OPTS)
-Xms12g -Xmx12g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=20
-XX:G1HeapRegionSize=16M
-XX:InitiatingHeapOccupancyPercent=35
-XX:G1ReservePercent=25
-XX:+ParallelRefProcEnabled
-XX:+ExplicitGCInvokesConcurrent
```

Sin ese tuning, el p99.9 de Kafka pasaba los 400ms. El JVM es poderoso, pero lo pagás en la cola.

### Throughput de Consumer

Para consumo, se testeó un grupo de 3 consumers leyendo del mismo topic:

| Métrica | Kafka 3.7 | Redpanda 24.3 |
| --- | --- | --- |
| Throughput consumer (MB/s) | 1,100 | 1,250 |
| Tiempo de rebalanceo (3→4 consumers) | 12s | 3s |
| Latencia end-to-end (produce→consume) | 8ms avg | 3ms avg |

La diferencia en tiempo de rebalanceo es significativa en la práctica. Cada rebalanceo significa un período donde las particiones no están asignadas y no se procesan mensajes. El cooperative sticky assignor de Kafka mejoró las cosas, pero la asignación de particiones de Redpanda es directamente más rápida porque no hay coordinación con un servicio de metadata separado.

## La Migración: Un Caso de Estudio

Consideremos un cluster de 5 nodos de Kafka manejando unos 2TB de eventos por día, analytics de usuarios, eventos de órdenes, updates de inventario, todo el paquete. La decisión de probar Redpanda vino de dos cosas: el equipo de on-call pasaba el 30% del tiempo de incidentes en problemas de ZooKeeper o tuning de JVM, y el SLO de latencia p99 se estaba poniendo cada vez más difícil de cumplir.

### Paso 1: Cluster Shadow

Primero se levantó un cluster Redpanda de 3 nodos y usamos MirrorMaker 2 para replicar todos los topics:

```properties
# mm2.properties
clusters = kafka, redpanda
kafka.bootstrap.servers = kafka1:9092,kafka2:9092,kafka3:9092
redpanda.bootstrap.servers = rp1:9092,rp2:9092,rp3:9092

kafka->redpanda.enabled = true
kafka->redpanda.topics = .*

sync.topic.configs.enabled = true
sync.topic.acls.enabled = false
replication.factor = 3

# Crítico: configurar esto para evitar que manee los nombres de topic
replication.policy.class = org.apache.kafka.connect.mirror.IdentityReplicationPolicy
```

Esto funcionó out of the box. MirrorMaker 2 no sabe ni le importa que el target es Redpanda, simplemente habla el protocolo Kafka.

### Paso 2: Validación de Consumers

Se corrieron consumers shadow en el cluster Redpanda y comparamos conteos de mensajes, orden y contenido contra los consumers de Kafka. Esto detectó dos problemas:

1. **Manejo de timestamps**: El ` LogAppendTime ` de Redpanda tenía diferencias de precisión en microsegundos respecto a Kafka. La lógica de deduplicación que usaba comparaciones de timestamp se rompió.
2. **Marcadores de transacción**: Algunos producers estaban usando transacciones (` enable.idempotence=true ` + ` transactional.id `). Los marcadores de commit de transacción aparecían distinto en la representación interna de Redpanda. Los consumers usando aislamiento ` read_committed ` funcionaban bien, pero una herramienta custom de tracking de offsets que parseaba los datos crudos de la partición se rompió.

### Paso 3: El Switchover

Se realizó una migración rolling, moviendo un consumer group a la vez:

```python
# Script de migración simplificado
import confluent_kafka

def migrate_consumer_group(group_id, kafka_brokers, redpanda_brokers, topics):
 # 1. Obtener offsets committed actuales de Kafka
 admin = confluent_kafka.admin.AdminClient({
 'bootstrap.servers': kafka_brokers
 })

 committed = admin.list_consumer_group_offsets([
 confluent_kafka.admin.ConsumerGroupTopicPartitions(group_id)
 ])

 # 2. Mapear offsets a Redpanda (MM2 preserva offsets con IdentityReplicationPolicy)
 rp_admin = confluent_kafka.admin.AdminClient({
 'bootstrap.servers': redpanda_brokers
 })

 # 3. Pre-commitear offsets a Redpanda antes de arrancar consumers
 offsets = []
 for tp, offset_meta in committed[group_id].result().topic_partitions:
 offsets.append(confluent_kafka.TopicPartition(
 tp.topic, tp.partition, offset_meta.offset
 ))

 rp_admin.alter_consumer_group_offsets([
 confluent_kafka.admin.ConsumerGroupTopicPartitions(group_id, offsets)
 ])

 print(f"Migrado {group_id}: {len(offsets)} offsets de partición committeados a Redpanda")
```

### Paso 4: Switchover de Producers

Se cambiaron los bootstrap servers de los producers durante una ventana de bajo tráfico:

```yaml
# Antes
kafka:
 bootstrap-servers: kafka1:9092,kafka2:9092,kafka3:9092

# Después
kafka: # mantuvimos el nombre del config key para minimizar cambios de código
 bootstrap-servers: rp1:9092,rp2:9092,rp3:9092
```

Funcionó sin problemas. Sin cambios de código necesarios. La librería cliente de Kafka se conectó a Redpanda y empezó a producir. Producers idempotentes, producers transaccionales, producers con particiones, todos.

## Schema Registry: La Historia de Compatibilidad

Redpanda incluye su propio schema registry que es API-compatible con el de Confluent. La migración cubrió ~200 schemas Avro:

```bash
# Exportar schemas del Confluent Schema Registry
for subject in $(curl -s http://kafka-sr:8081/subjects | jq -r '.[]'); do
 schema=$(curl -s "http://kafka-sr:8081/subjects/${subject}/versions/latest")
 echo "$schema" > "schemas/${subject}.json"
done

# Importar en el schema registry de Redpanda
for file in schemas/*.json; do
 subject=$(basename "$file" .json)
 schema=$(cat "$file" | jq -c '{schema: .schema, schemaType: .schemaType}')
 curl -X POST "http://rp1:8081/subjects/${subject}/versions" \
 -H "Content-Type: application/vnd.schemaregistry.v1+json" \
 -d "$schema"
done
```

Los modos de compatibilidad (BACKWARD, FORWARD, FULL) funcionaron todos. El único gotcha fue que el schema registry de Redpanda guarda datos internamente en un topic ` _schemas `, mientras que Confluent usa ` _schemas ` también, pero el formato de los mensajes difiere. No es posible simplemente replicar el topic, teners que re-registrar.

## Diferencias Operacionales Que Importan Día a Día

### Monitoreo

Redpanda expone métricas Prometheus en el puerto 9644 por defecto. Sin JMX, sin exporters JMX-to-Prometheus, sin andar peleando con autenticación de JMX remoto:

```yaml
# prometheus.yml
scrape_configs:
 - job_name: redpanda
 static_configs:
 - targets:
 - rp1:9644
 - rp2:9644
 - rp3:9644
 metrics_path: /public_metrics
```

Con Kafka, obtener buenas métricas requiere un sidecar de JMX exporter, que en sí consume recurse es y agrega complejidad. Ese setup no se extraña.

### Uso de Disco

Redpanda usa alrededor de 20-30% menos de disco para los mismos datos, principalmente porque no tiene el overhead de archivos de índice y timeindex que Kafka crea por segmento. Redpanda mantiene su índice en memoria usando su propio motor de storage.

### Memoria

Acá resulta interesante. Kafka necesita tuning del heap del JVM, muy poco y las pausas de GC te matan, demasiado y las pausas de GC también te matan (pero de otra forma). Con Redpanda, le decís cuánta memoria usar y la maneja solo:

```bash
rpk redpanda config set --set redpanda.memory=48G
rpk redpanda config set --set redpanda.reserve_memory=4G
```

Sin tuning de heap, sin tuning de GC, sin flags ` -XX: `. El allocator de Seastar maneja todo.

## Donde Kafka todavía gana

Kafka sigue siendo una opción sólida, y existen razones sustanciales para continuar utilizándolo:

1. **Kafka Streams y ksqlDB**: El ecosistema de procesamiento de streams no tiene rival. Redpanda no tiene equivalente, lo combinarías con Flink o tus propios consumers.
2. **Kafka Connect**: Si bien Redpanda puede correr Kafka Connect (es solo el protocolo Kafka), el ecosistema de conectores testeados en producción es más profundo con Kafka.
3. **Madurez de KRaft**: El modo KRaft elimina ZooKeeper, y está estable desde la 3.5. La brecha operacional se está achicando.
4. **Comunidad y contratación**: Encontrar ingenieros de Kafka es más fácil. La experiencia en Redpanda es más rara.
5. **Tiered storage**: El tiered storage de Kafka (escribir segmentos fríos a S3) está más maduro que la implementación de Redpanda.

## Los Números Después de 8 Meses

Después de migrar a Redpanda:

- **Latencia p99 de producción**: Bajó de 28ms a 4ms (consistente, no solo al principio)
- **Incidentes on-call relacionados a streaming**: Bajaron 70%
- **Costo de infraestructura**: Bajó 40% (3 nodos Redpanda reemplazaron 5 Kafka + 3 ZooKeeper)
- **Tiempo dedicado a tuning de JVM**: Cero, porque no hay JVM

La migración valió la pena para este caso de uso. Sin embargo, no es universalmente recomendable. Si un cluster de Kafka está estable, la latencia es aceptable, y se usa Kafka Streams a fondo, quedarse con Kafka puede ser la mejor opción. La migración tiene costos reales y riesgos reales.

Para proyectos greenfield, sin embargo, Redpanda merece fuerte consideración. La simplicidad operacional sola justifica la elección.

## Preguntas frecuentes

### ¿Redpanda es un reemplazo directo de Kafka?

A nivel de API, mayormente sí. Redpanda implementa el protocolo wire de Kafka, así que la mayoría de los clientes, conectores y herramientas de Kafka funcionan sin cambios. Sin embargo, comportamientos internos como timing de replicación, compactación y semántica de transacciones tienen diferencias sutiles.

### ¿Redpanda realmente no necesita JVM?

Correcto. Redpanda está escrito en C++ usando el framework Seastar, lo que significa que no hay JVM, no hay pausas de GC, y no hay tuning de JVM. Esta es su mayor ventaja arquitectónica para workloads sensibles a la latencia.

### ¿Debería migrar de Kafka a Redpanda?

Depende. Si tus puntos de dolor son la complejidad operacional (ZooKeeper, tuning de JVM, pausas de GC) y la tail latency, Redpanda es tentador. Si se está profundamente metido en el ecosistema de Kafka Streams o se necesita features específicas de KRaft, quedarte en Kafka puede tener más sentido.

### ¿Cómo maneja Redpanda el schema registry?

Redpanda incluye un schema registry que es compatible con la API del Confluent Schema Registry. Soporta Avro, Protobuf y JSON Schema con los mismos endpoints REST API.

## Tips

1. **Tip:** Al comparar Kafka vs Redpanda, medir siempre la latencia de *cola* (p99/p99.9), no solo el throughput. Las pausas del GC del JVM de Kafka crean picos de latencia que los promedios ocultan completamente. Utilizar ` kafka-producer-perf-test.sh --print-metrics ` para obtener datos de percentiles.
2. **Tip:** El CLI ` rpk ` de Redpanda es un reemplazo directo para la mayoría de los comandos ` kafka-topics.sh `. Iniciar con ` rpk topic list ` y ` rpk cluster info `, son más rápidos y la salida es legible.
3. **Tip:** Si se está migrando de Kafka a Redpanda, utilizar MirrorMaker 2 con ` sync.topic.configs.enabled=true `. La compatibilidad de API Kafka de Redpanda es lo suficientemente buena como para que MM2 funcione sin modificaciones, pero conviene probar los producers ` exactly_once `, la semántica de transacciones tiene diferencias sutiles.
4. **Tip:** El flag ` --smp ` de Redpanda controla cuántos cores de CPU utiliza. En una máquina de 16 cores, iniciar con ` --smp 14 ` y dejar 2 cores para el sistema operativo y el monitoreo con ` rpk `. Si se sobresuscriben CPUs, la latencia p99 se degrada significativamente.

- kafka
- redpanda
- event-streaming
- benchmarks
- infraestructura
- raft
- zookeeper
- p99-latency

[Volver al Blog](<https://lucioduran.com/es/blog>)
