0Pricing
Coding Interview Prep · Lección

Almacenamiento de datos escalable: SQL frente a NoSQL

Elija entre bases de datos relacionales, almacenes clave-valor, bases de datos documentales y almacenes de columnas anchas según los patrones de acceso, la consistencia y los requisitos de escalabilidad.

Almacenamiento de datos escalable: SQL frente a NoSQL es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.

La elección del almacenamiento es un compromiso

Elegir un almacén de datos es una de las decisiones más trascendentales en el diseño de sistemas. Ninguna base de datos es universalmente la mejor: cada tipo está optimizado para distintos patrones de acceso, garantías de consistencia y características de escalabilidad. Equivocarse en producción puede provocar meses de complejas tareas de migración.

En las entrevistas, los entrevistadores evalúan si comprende las diferencias fundamentales y si puede asociar un motor de almacenamiento con los requisitos de un problema. La pregunta nunca es «¿cuál es mejor?», sino «¿cuál es mejor para esta carga de trabajo específica?». Justifique siempre su elección con requisitos concretos.

# Storage decision matrix summary
factors = [
    'Data structure (tabular, documents, key-value, graph, time-series)',
    'Read vs write ratio (read-heavy, write-heavy, balanced)',
    'Query patterns (point lookups, range scans, aggregations, joins)',
    'Consistency requirements (ACID vs eventual consistency)',
    'Scale requirements (single node, sharding, global distribution)',
    'Latency requirements (milliseconds vs microseconds)',
    'Team familiarity and operational complexity',
]
print('Key factors for storage selection:')
for f in factors:
    print(f'  - {f}')

Ventajas de las bases de datos relacionales (SQL)

Las bases de datos relacionales (PostgreSQL, MySQL, SQLite) almacenan los datos en tablas con esquemas fijos y admiten transacciones ACID: Atomicidad, Consistencia, Aislamiento y Durabilidad. Son excelentes para consultas complejas con joins, agregaciones y filtros, por lo que resultan ideales para datos estructurados con relaciones bien definidas.

Ventajas principales: consultas complejas entre varias tablas mediante SQL, restricciones de clave externa para garantizar la integridad de los datos, potentes índices (B-tree, hash y texto completo) y un ecosistema maduro con replicación y copias de seguridad. Utilice SQL cuando sus datos sean altamente relacionales, la consistencia sea crítica y las consultas sean complejas y variadas.

# SQL excels at: complex queries, joins, transactions

# Example: find top 5 products by revenue this month
sql_query = '''
SELECT p.name, SUM(oi.quantity * oi.price) AS revenue
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.created_at >= DATE_TRUNC('month', NOW())
GROUP BY p.id, p.name
ORDER BY revenue DESC
LIMIT 5;
'''
print('SQL shines for relational queries with JOINs:')
print(sql_query)

print('ACID guarantees example (transfer $100 between accounts):')
transfer_sql = '''
BEGIN;
  UPDATE accounts SET balance = balance - 100 WHERE id = 1;
  UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;  -- either both succeed or neither does
'''  
print(transfer_sql)

Desafíos de escalabilidad de SQL

Las bases de datos SQL escalan verticalmente de forma natural (un servidor más grande, con más CPU/RAM), pero la escalabilidad horizontal es compleja. Las réplicas de lectura gestionan las cargas de trabajo con muchas lecturas al dirigirlas a nodos réplica y enviar las escrituras al nodo primario. Sin embargo, el rendimiento de escritura está limitado a un único nodo primario, a menos que se añada sharding.

El sharding divide los datos entre varias instancias de base de datos mediante una clave de shard (por ejemplo, user_id mod N). Esto permite escalar las escrituras, pero impide los joins y las transacciones entre shards, dos de las funcionalidades más importantes de SQL. La mayoría de las aplicaciones web superan las capacidades de SQL de un solo nodo cuando alcanzan aproximadamente entre 5 y 10 TB de datos o unas 100.000 escrituras por segundo.

# SQL scaling strategies
strategies = {
    'Read replicas': {
        'how': 'One primary (writes), multiple replicas (reads)',
        'scales': 'Read throughput (10x+)',
        'limit': 'Write throughput still bounded by single primary',
    },
    'Connection pooling (PgBouncer)': {
        'how': 'Pool of persistent DB connections shared among app servers',
        'scales': 'Connection count (PostgreSQL max ~500 connections)',
        'limit': 'Does not increase query throughput',
    },
    'Horizontal sharding': {
        'how': 'Partition rows by shard key across N database instances',
        'scales': 'Both reads and writes (N×)',
        'limit': 'Cross-shard joins and transactions broken; complex routing',
    },
    'CQRS': {
        'how': 'Separate write model (SQL) from read model (denormalised/NoSQL)',
        'scales': 'Optimise each path independently',
        'limit': 'Eventual consistency between write and read models',
    },
}
for strategy, info in strategies.items():
    print(f'{strategy}:\n  How: {info["how"]}\n  Scales: {info["scales"]}\n  Limit: {info["limit"]}\n')

Almacenes clave-valor: Redis y DynamoDB

Los almacenes clave-valor guardan los datos como pares clave → valor y ofrecen lecturas y escrituras O(1) por clave. Sacrifican flexibilidad de consulta a cambio de un rendimiento extremo y una gran escalabilidad horizontal. Redis, que funciona en memoria, alcanza una latencia de microsegundos; DynamoDB, que es un servicio gestionado, alcanza una latencia de milisegundos de un solo dígito y escala automáticamente a cualquier rendimiento.

Utilice almacenes clave-valor para: almacenamiento de sesiones, caché, feature flags, asociaciones de acortadores de URL, carritos de compra y tablas de clasificación en tiempo real. No los utilice cuando necesite: consultas complejas, relaciones entre entidades o filtrado ad hoc; solo puede buscar mediante la clave exacta.

# Key-value store use cases
kv_operations = {
    'GET key':        'O(1) point lookup — the core operation',
    'SET key value':  'O(1) insert or update',
    'DEL key':        'O(1) delete',
    'EXPIRE key ttl': 'Set time-to-live; key auto-deleted after ttl seconds',
    'INCR key':       'Atomic increment — useful for counters and rate limiting',
    'LPUSH/LRANGE':   'List operations — useful for queues and recent-items feeds',
    'ZADD/ZRANGE':    'Sorted set — leaderboards, rate limiting with sliding window',
}
print('Redis operation set:')
for op, desc in kv_operations.items():
    print(f'  {op:25s}: {desc}')

print('\nDynamoDB vs Redis:')
print('  Redis:    microsecond latency, in-memory, needs persistence config')
print('  DynamoDB: single-digit ms, managed, auto-scaling, durable by default')

Almacenes de documentos: MongoDB

Los almacenes de documentos (MongoDB, Couchbase) almacenan los datos como documentos similares a JSON, lo que permite utilizar esquemas flexibles: los campos pueden variar entre documentos de una misma colección. Admiten índices en cualquier campo y consultas bastante completas (filtros, proyecciones y agregaciones), aunque las transacciones entre varios documentos son más limitadas que en SQL.

Los almacenes de documentos son adecuados para aplicaciones en las que: la estructura de los datos varía según la entidad (perfiles de usuario con atributos diferentes), se espera una evolución rápida del esquema (startups que cambian con frecuencia sus modelos de datos) o los patrones de lectura se basan principalmente en recuperar entidades completas en lugar de hacer joins entre tablas.

# MongoDB document example
user_doc = {
    '_id': 'user123',
    'name': 'Alice',
    'email': 'alice@example.com',
    'preferences': {
        'theme': 'dark',
        'language': 'en',
        'notifications': ['email', 'push']
    },
    'addresses': [
        {'type': 'home', 'city': 'Berlin', 'country': 'DE'},
        {'type': 'work', 'city': 'Munich', 'country': 'DE'}
    ],
    'subscription_tier': 'pro',
    # Note: not all users have all fields -- flexible schema!
}

import json
print('Document structure (flexible schema):')
print(json.dumps(user_doc, indent=2))

print('\nDocument store strengths:')
print('  - Nested/array fields without joins')
print('  - Flexible schema (different fields per document)')
print('  - Scales horizontally by sharding on _id')

Almacenes de columnas anchas: Cassandra

Los almacenes de columnas anchas (Apache Cassandra, HBase) almacenan los datos en filas y columnas, pero permiten que cada fila tenga un conjunto diferente de columnas. Están diseñados para ofrecer un rendimiento masivo de escritura distribuido entre muchos nodos, con consistencia eventual de forma predeterminada. Cassandra consigue una escalabilidad lineal de escritura: duplicar el número de nodos duplica el rendimiento de escritura.

El compromiso es el siguiente: las consultas deben diseñarse en torno a la clave de partición. No se puede filtrar ni ordenar de forma eficiente por columnas arbitrarias; primero debe definir el patrón de consulta y después diseñar la tabla para adaptarla. Esto es lo contrario del enfoque de SQL: «diseñar los datos y después escribir cualquier consulta».

# Cassandra table design for time-series events
# Design around the query: 'give me all events for user X, most recent first'

cassandra_table = '''
CREATE TABLE user_events (
    user_id    UUID,
    event_time TIMESTAMP,
    event_type TEXT,
    metadata   MAP<TEXT, TEXT>,
    PRIMARY KEY (user_id, event_time)
) WITH CLUSTERING ORDER BY (event_time DESC);

-- Query (matches partition key exactly):
SELECT * FROM user_events WHERE user_id = ? LIMIT 100;
'''
print('Cassandra wide-column design:')
print(cassandra_table)
print('Properties:')
print('  - user_id = partition key (all rows for one user on same node)')
print('  - event_time = clustering key (sorted within partition)')
print('  - Very fast writes: append-only, no locking')
print('  - Cannot query by event_type alone (no partition key)')

Teorema CAP: consistencia, disponibilidad y tolerancia a particiones

El teorema CAP establece que un sistema distribuido puede garantizar como máximo dos de los siguientes elementos: Consistencia (cada lectura devuelve la escritura más reciente), Adisponibilidad (cada solicitud recibe una respuesta que no es un error) y Ptolerancia a particiones (el sistema continúa funcionando a pesar de las particiones de red).

Como las particiones de red siempre ocurren en los sistemas distribuidos, la elección real es entre CP (priorizar la consistencia y poder rechazar solicitudes durante una partición) y AP (priorizar la disponibilidad, aunque se puedan devolver datos obsoletos). Las bases de datos SQL suelen ser CP; Cassandra y DynamoDB son AP (consistencia eventual de forma predeterminada). Redis Cluster es CP.

# CAP theorem applied to common databases
databases = {
    'PostgreSQL (single node)': {'C': True,  'A': True,  'P': False, 'note': 'Not distributed; CA'},
    'PostgreSQL (multi-AZ)':    {'C': True,  'A': False, 'P': True,  'note': 'CP: primary fails over, brief downtime'},
    'MySQL Cluster':            {'C': True,  'A': False, 'P': True,  'note': 'CP'},
    'Cassandra':                {'C': False, 'A': True,  'P': True,  'note': 'AP: eventual consistency default'},
    'DynamoDB (default)':       {'C': False, 'A': True,  'P': True,  'note': 'AP: eventual consistency'},
    'DynamoDB (strong read)':   {'C': True,  'A': False, 'P': True,  'note': 'CP: strongly consistent reads'},
    'Redis Cluster':            {'C': True,  'A': False, 'P': True,  'note': 'CP'},
    'MongoDB (default)':        {'C': True,  'A': False, 'P': True,  'note': 'CP: reads from primary'},
}
for db, caps in databases.items():
    c_str = 'C' if caps['C'] else '-'
    a_str = 'A' if caps['A'] else '-'
    p_str = 'P' if caps['P'] else '-'
    print(f'{db:35s} [{c_str}{a_str}{p_str}] {caps["note"]}')

Marco de decisión para elegir el almacenamiento

Un marco de decisión práctico para elegir el almacenamiento en las entrevistas:

  • ¿Necesita transacciones ACID? → Base de datos relacional (PostgreSQL, MySQL)
  • ¿Necesita búsquedas inferiores a un milisegundo o caché? → Almacén clave-valor (Redis)
  • ¿Necesita un esquema flexible o datos orientados a documentos? → Almacén de documentos (MongoDB)
  • ¿Necesita un rendimiento masivo de escritura (>100K/segundo) con datos de series temporales o eventos? → Almacén de columnas anchas (Cassandra)
  • ¿Necesita distribución global con escalado gestionado? → DynamoDB o Cosmos DB
  • ¿Necesita recorridos por grafos? → Base de datos de grafos (Neo4j)
# Decision tree in code form
def choose_storage(needs_acid, high_write_throughput, flexible_schema,
                   sub_ms_latency, graph_queries, global_scale):
    if sub_ms_latency:
        return 'Redis (in-memory key-value)'
    if graph_queries:
        return 'Neo4j (graph database)'
    if needs_acid:
        return 'PostgreSQL / MySQL (relational)'
    if high_write_throughput and not flexible_schema:
        return 'Cassandra (wide-column, write-optimised)'
    if flexible_schema:
        return 'MongoDB (document store)'
    if global_scale:
        return 'DynamoDB or Cosmos DB (managed global KV/document)'
    return 'PostgreSQL (safe default for most web apps)'

# Example scenarios
scenarios = [
    {'needs_acid': True,  'high_write_throughput': False, 'flexible_schema': False,
     'sub_ms_latency': False, 'graph_queries': False, 'global_scale': False},
    {'needs_acid': False, 'high_write_throughput': True,  'flexible_schema': False,
     'sub_ms_latency': False, 'graph_queries': False, 'global_scale': False},
    {'needs_acid': False, 'high_write_throughput': False, 'flexible_schema': False,
     'sub_ms_latency': True,  'graph_queries': False, 'global_scale': False},
]
for s in scenarios:
    print(f'{choose_storage(**s)}')

Persistencia políglota: uso de varios almacenes

Los sistemas de producción rara vez utilizan una única base de datos para todo. La persistencia políglota consiste en utilizar la tecnología de almacenamiento adecuada para cada parte del sistema. Una aplicación web típica podría utilizar: PostgreSQL para los datos de usuarios y pedidos que constituyen la fuente de verdad, Redis para el almacenamiento de sesiones y la caché, Elasticsearch para la búsqueda de texto completo, S3 para el almacenamiento de archivos y Cassandra para los registros de eventos y los análisis.

El compromiso es que la complejidad operativa aumenta con cada tipo de base de datos. El equipo debe mantener, supervisar y crear copias de seguridad de varios sistemas. Justificación del diseño: a gran escala, las mejoras de rendimiento y escalabilidad que se obtienen al asociar cada carga de trabajo con el almacenamiento adecuado compensan el coste operativo.

# Polyglot persistence in an e-commerce system
components = {
    'User accounts, orders, payments': {
        'storage': 'PostgreSQL',
        'reason': 'ACID transactions (payment integrity), complex queries',
    },
    'Product catalogue': {
        'storage': 'MongoDB or PostgreSQL with JSONB',
        'reason': 'Flexible product attributes vary by category',
    },
    'Session tokens': {
        'storage': 'Redis (with TTL)',
        'reason': 'O(1) lookup, automatic expiry, high throughput',
    },
    'Product search': {
        'storage': 'Elasticsearch',
        'reason': 'Full-text search, faceted filtering, relevance scoring',
    },
    'Activity/event log': {
        'storage': 'Cassandra or Kafka + S3',
        'reason': 'High write throughput, append-only, time-series queries',
    },
    'Product images / videos': {
        'storage': 'S3 + CloudFront CDN',
        'reason': 'Cheap object storage, global distribution via CDN',
    },
}
for component, info in components.items():
    print(f'{component}:\n  {info["storage"]}: {info["reason"]}\n')

Estrategia de indexación en distintos tipos de almacenamiento

Todos los sistemas de almacenamiento utilizan índices para acelerar las lecturas, a costa de ralentizar las escrituras y requerir almacenamiento adicional. Comprender la indexación en los distintos tipos de almacenamiento es fundamental para el diseño de sistemas:

  • SQL: índice B-tree en cualquier columna; índices compuestos para consultas con varias columnas; índices de cobertura para evitar consultas a la tabla
  • MongoDB: índice en cualquier campo; índices compuestos; índices TTL para la caducidad automática
  • Cassandra: solo la clave de partición y las columnas de agrupación se indexan de forma nativa; los índices secundarios son costosos
  • Redis: conjuntos ordenados como índices para consultas por rangos; no se necesita indexación tradicional para clave-valor
# Indexing examples across storage types

# PostgreSQL: B-tree composite index
postgres_index = '''
CREATE INDEX idx_orders_user_created
ON orders(user_id, created_at DESC);
-- Optimises: SELECT * FROM orders WHERE user_id=? ORDER BY created_at DESC
'''

# MongoDB: compound index
mongo_index = '''
db.products.createIndex({ category: 1, price: -1 })
// Optimises: db.products.find({category:'Electronics'}).sort({price:-1})
'''

# Cassandra: cluster key ordering (built into table design)
cassandra_index = '''
-- No separate index needed; clustering key IS the index:
PRIMARY KEY (user_id, event_time) WITH CLUSTERING ORDER BY (event_time DESC)
'''

print('PostgreSQL:', postgres_index)
print('MongoDB:', mongo_index)
print('Cassandra:', cassandra_index)

SQL frente a NoSQL en las entrevistas: qué decir

Cuando le pregunten «¿SQL o NoSQL?» en una entrevista de diseño de sistemas, nunca responda con una sola palabra. En su lugar, siga esta estructura:

  1. Indique la carga de trabajo: 'Esta carga tiene muchas lecturas y filtros complejos, así que…'
  2. Indique el requisito: 'Necesitamos una consistencia fuerte para las transacciones financieras, así que…'
  3. Indique la elección: 'Usaría PostgreSQL con réplicas de lectura'
  4. Indique el compromiso: 'El compromiso es que el escalado horizontal de las escrituras requiere sharding, lo que añade complejidad'
  5. Mencione una alternativa: 'Si hubiera más escrituras, podríamos considerar DynamoDB'
# Sample answer structure for 'SQL or NoSQL?'
def answer_storage_question(workload, consistency_need, scale):
    print(f'Workload: {workload}')
    print(f'Consistency: {consistency_need}')
    print(f'Scale: {scale}')
    print()
    if 'financial' in workload.lower() or consistency_need == 'strong':
        choice = 'PostgreSQL (ACID, strong consistency)'
        tradeoff = 'Horizontal write scaling requires sharding'
        alternative = 'Google Spanner for global transactions'
    elif 'event' in workload.lower() or 'log' in workload.lower():
        choice = 'Cassandra (high write throughput, time-series)'
        tradeoff = 'Eventual consistency; queries limited to partition key'
        alternative = 'Kafka + S3 for long-term event archival'
    else:
        choice = 'DynamoDB (managed, auto-scale, low latency)'
        tradeoff = 'Limited query flexibility; cross-item transactions limited'
        alternative = 'PostgreSQL if complex queries emerge'
    print(f'Choice: {choice}\nTrade-off: {tradeoff}\nAlternative: {alternative}')

answer_storage_question('Social media feed', 'eventual', '10M users')

Comprobación rápida

Compruebe sus conocimientos sobre los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.

Resumen de la lección

En esta lección ha aprendido: las bases de datos SQL proporcionan transacciones ACID y consultas complejas, pero tienen dificultades para escalar las escrituras, mientras que las bases de datos NoSQL sacrifican consistencia o flexibilidad de consulta a cambio de una escala y disponibilidad extremas, el teorema CAP obliga a los sistemas distribuidos a elegir entre consistencia y disponibilidad durante las particiones de red, y los sistemas de producción suelen utilizar persistencia políglota, asociando cada carga de trabajo con el motor de almacenamiento adecuado. A continuación, exploraremos las capas de caché, las CDN y el balanceo de carga para escalar aún más los sistemas con muchas lecturas.

Preguntas frecuentes

¿La lección «Almacenamiento de datos escalable: SQL frente a NoSQL» es gratis?

Sí — el texto completo de «Almacenamiento de datos escalable: SQL frente a NoSQL» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.

¿Qué aprenderé en «Almacenamiento de datos escalable: SQL frente a NoSQL»?

Elija entre bases de datos relacionales, almacenes clave-valor, bases de datos documentales y almacenes de columnas anchas según los patrones de acceso, la consistencia y los requisitos de escalabili… Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Coding Interview Prep?

No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Almacenamiento de datos escalable: SQL frente a NoSQL»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?

Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Marco de la entrevista de diseño de sistemas
  2. Almacenamiento de datos escalable: SQL frente a NoSQL
  3. Caché, CDN y balanceo de carga
  4. Diseñar un limitador de solicitudes y un feed de Twitter
← Volver a Coding Interview Prep