¿Por qué necesitamos un sistema de archivos distribuido?
Un solo disco duro tiene tres límites: capacidad (no caben los petabytes), velocidad de lectura (leer 1 TB a 150 MB/s toma casi 2 horas) y confiabilidad (si el disco muere, los datos se pierden). La solución de la era Big Data es repartir los archivos entre muchas máquinas y coordinarlas para que el clúster entero se comporte como un único sistema de archivos gigante.
HDFS (Hadoop Distributed File System) es la implementación de referencia de esta idea, inspirada en el Google File System (GFS). Sus supuestos de diseño son clave para entender sus decisiones:
- El hardware barato falla con frecuencia: la tolerancia a fallos no es opcional, es el núcleo del diseño.
- Los archivos son enormes (de GB a TB), no millones de archivos pequeños.
- La carga de trabajo típica es lectura secuencial masiva (análisis), no acceso aleatorio.
Arquitectura: NameNode y DataNodes
HDFS sigue una arquitectura maestro/trabajador:
| Componente | Rol | Analogía |
|---|---|---|
| NameNode | Nodo maestro único. Guarda los metadatos: qué archivos existen, en qué bloques se dividen y en qué DataNodes está cada bloque. | El índice de una biblioteca |
| DataNode | Nodos trabajadores (puede haber miles). Almacenan los bloques de datos reales en sus discos locales y reportan su estado al NameNode con heartbeats periódicos. | Los estantes de la biblioteca |
| Cliente | Pregunta al NameNode dónde están los bloques y luego lee/escribe directamente contra los DataNodes. | El lector que consulta el índice y va a los estantes |
El NameNode no almacena datos de usuario: solo metadatos en memoria (con respaldo en disco mediante el FsImage y el EditLog). Esto lo hace muy rápido, pero también lo convierte en el punto crítico del clúster — por eso las versiones modernas añaden un NameNode en espera (standby) para alta disponibilidad.
Bloques de 128 MB y replicación
Cuando subes un archivo a HDFS, no se guarda como una unidad. Se divide en bloques de 128 MB (configurable; el valor histórico era 64 MB) y cada bloque se replica en 3 DataNodes distintos (factor de replicación por defecto).
¿Por qué bloques tan grandes, si en un disco local los bloques son de KB?
- Menos metadatos: con bloques de 128 MB, un archivo de 1 TB son solo ~8 000 bloques. El NameNode puede mantener todo el mapa en RAM.
- Menos costo de búsqueda: con discos magnéticos, el tiempo de seek domina; leer bloques grandes en secuencia amortiza ese costo.
- Paralelismo natural: cada bloque puede procesarse en una máquina distinta (esto lo aprovechará MapReduce en el módulo siguiente).
Y la replicación factor 3 garantiza que la pérdida de cualquier máquina — o incluso de un rack completo — no pierda datos. El NameNode detecta un DataNode caído (deja de recibir heartbeats) y ordena re-replicar sus bloques desde las copias sobrevivientes.
graph TD C["Cliente"] -->|"1. Solicita ubicación de bloques"| NN["NameNode<br/>(metadatos en memoria)"] NN -->|"2. Mapa: bloque B1 → DN1, DN2, DN5"| C C -->|"3. Lee/Escribe bloques"| DN1["DataNode 1<br/>B1 · B2"] C --> DN2["DataNode 2<br/>B1 · B3"] C --> DN3["DataNode 3<br/>B2 · B3"] DN1 -.->|"Heartbeats y reporte de bloques"| NN DN2 -.-> NN DN3 -.-> NN
Rack awareness: dónde poner las réplicas
Un clúster real se organiza en racks (bastidores) de servidores: las máquinas de un mismo rack comparten switch y fuente de alimentación, así que tienden a fallar juntas. HDFS aplica una política de colocación consciente del rack (rack awareness):
- Réplica 1: en el nodo local del cliente (o uno aleatorio del clúster).
- Réplica 2: en un nodo de otro rack (protege contra la caída del rack entero).
- Réplica 3: en otro nodo del mismo rack que la réplica 2 (equilibrio entre tolerancia y tráfico de red entre racks).
Esta política tolera el fallo de un rack completo sin perder datos, sin pagar el costo de escribir las tres réplicas a través de switches inter-rack.
graph LR subgraph RackA["Rack A"] A1["Nodo 1<br/>Réplica 1"] A2["Nodo 2"] end subgraph RackB["Rack B"] B1["Nodo 3<br/>Réplica 2"] B2["Nodo 4<br/>Réplica 3"] end W["Escritura del bloque"] --> A1 W --> B1 W --> B2
Write-once-read-many
HDFS no es un sistema de archivos de propósito general. Su modelo de acceso es write-once-read-many: un archivo se escribe una sola vez (en un único flujo, por un único escritor) y luego se lee muchas veces. No se puede editar un byte en medio del archivo; como mucho, se puede añadir al final (append).
¿Por qué esta restricción tan fuerte?
- Coherencia simple: si nadie modifica bloques existentes, todas las réplicas de un bloque son siempre idénticas. No hace falta un protocolo de concurrencia complejo entre cientos de escritores.
- Rendimiento: el análisis de Big Data lee datasets completos de forma secuencial; optimizar para ese caso rinde mucho más que soportar escrituras aleatorias.
- Streaming: la escritura se hace en pipeline (cliente → DataNode 1 → DataNode 2 → DataNode 3), lo que reparte el costo de red de las réplicas.
Consecuencia práctica: HDFS es ideal para data lakes, logs y datasets de análisis, pero no para una base de datos transaccional ni para archivos que cambian constantemente.
Ejercicio: simula la división en bloques y la replicación
🧪 Ejercicio
De un archivo a bloques replicados
Escribe una función `dividir_en_bloques(tamano_mb, bloque_mb=128)` que devuelva una lista con el tamaño de cada bloque de un archivo (el último puede ser más pequeño), y una función `asignar_replicas(bloques, datanodes, factor=3)` que asigne a cada bloque `factor` DataNodes distintos de forma rotatoria. Imprime el resultado para un archivo de 300 MB con 5 DataNodes.
🔍 Para dividir, usa divmod(tamano_mb, bloque_mb). Para asignar réplicas rotatorias, recorre los DataNodes con un índice módulo len(datanodes) empezando en una posición distinta por bloque.
def dividir_en_bloques(tamano_mb, bloque_mb=128):
completos, resto = divmod(tamano_mb, bloque_mb)
bloques = [bloque_mb] * completos
if resto > 0:
bloques.append(resto)
return bloques
def asignar_replicas(bloques, datanodes, factor=3):
asignacion = {}
for i, _ in enumerate(bloques):
replicas = []
for r in range(factor):
nodo = datanodes[(i + r) % len(datanodes)]
replicas.append(nodo)
asignacion[f"bloque_{i}"] = replicas
return asignacion
bloques = dividir_en_bloques(300)
print("Bloques:", bloques) # [128, 128, 44]
datanodes = ["dn1", "dn2", "dn3", "dn4", "dn5"]
for bloque, replicas in asignar_replicas(bloques, datanodes).items():
print(f"{bloque}: {replicas}")Comprueba lo aprendido
Comprueba que lo pillaste
Un archivo de 300 MB se sube a HDFS con bloques de 128 MB. ¿Cuántos bloques ocupa en disco contando la replicación factor 3?
El archivo se divide en 3 bloques lógicos (128 + 128 + 44 MB) y cada bloque se almacena 3 veces en DataNodes distintos: 9 bloques físicos en total.
Comprueba que lo pillaste
¿Cuál es la función principal del NameNode en HDFS?
El NameNode es el maestro: guarda en memoria el espacio de nombres y el mapa archivo → bloques → DataNodes. Los datos reales viven en los DataNodes.
Comprueba que lo pillaste
¿Por qué la política de rack awareness coloca la segunda réplica en un rack distinto al de la primera?
Los nodos de un rack comparten switch y alimentación, así que fallan juntos. Poner réplicas en racks distintos garantiza que la pérdida de un rack no pierda el bloque.
Resumen
- HDFS divide archivos grandes en bloques de 128 MB distribuidos entre muchos DataNodes.
- El NameNode guarda solo metadatos (mapa archivo → bloques → ubicaciones); los DataNodes guardan los datos y envían heartbeats.
- La replicación factor 3 con rack awareness (1 réplica local, 2 en otro rack) da tolerancia a fallos de nodos y de racks completos.
- El modelo write-once-read-many simplifica la coherencia y optimiza la lectura secuencial masiva, a costa de no permitir ediciones aleatorias.
- Ante un fallo, el NameNode re-replica automáticamente los bloques afectados desde las copias sobrevivientes.
📚 Lecturas y fuentes
| Recurso | Tipo | Por qué leerlo |
|---|---|---|
| Apache Hadoop (Wikipedia en español) | Artículo | Para situar HDFS dentro del ecosistema Hadoop. Lee la introducción y la sección “HDFS”. |
| HDFS Architecture — documentación oficial de Apache Hadoop | Docs | La referencia. Lee “Assumptions and Goals”, “NameNode and DataNodes” y “Data Replication” (con “Replica Placement”); salta el resto (en inglés). |
| The Google File System (Ghemawat, Gobioff, Leung — SOSP 2003) | Paper | El artículo que HDFS copia. Con leer la sección 2 (“Design Overview”, págs. 2-5) ya entiendes de dónde salen los bloques grandes y las 3 réplicas (en inglés). |
| Designing Data-Intensive Applications (Martin Kleppmann) | Libro | Cap. 10 “Batch Processing”, sección “MapReduce and Distributed Filesystems”: cómo se usa HDFS desde el motor de cómputo (en inglés). |