Acceso a datos
Acceder a datos significa leer, escribir y manipular información que no reside únicamente en la memoria temporal del programa, sino en recursos persistentes o externos: ficheros, bases de datos, APIs de otros servicios, almacenamiento en la nube, etc. Es una de las competencias fundamentales en cualquier aplicación real: desde guardar preferencias de usuario hasta consulta y procesamiento de grandes volúmenes de información.
Tipos de fuentes de datos
- Ficheros locales: TXT, CSV, JSON, XML, imágenes, binarios.
- Ventaja: sencillez.
- Desventaja: no son buenos para acceso concurrente/consultas complejas.
- Bases de datos:
- Relacionales (RDBMS): MySQL, PostgreSQL. Datos en tablas, SQL. Buenas para integridad y consultas complejas.
- No relacionales (NoSQL): MongoDB (documentos), Redis (clave-valor), Cassandra (column-family), Neo4j (grafos). Adecuadas para modelos flexibles, escalado horizontal, latencia baja.
- Recursos externos (Internet): APIs REST/GraphQL, archivos alojados (CSV/JSON), servicios cloud (S3), feeds.
- Streams / colas de mensajes: Kafka, RabbitMQ. Para datos en movimiento y arquitectura basada en eventos.
- Sistemas operativos / sensores / dispositivos: logs, ficheros de sistema, IoT.
Modelos de interacción (cómo se accede)
- Lectura/escritura directa (ficheros): abrir (open), leer (read), escribir (write), cerrar (close).
- Cliente–servidor (DBMS): conectar mediante driver, ejecutar consultas, manejar conexiones y transacciones.
- Petición HTTP (APIs): GET, POST, PUT, DELETE. Manejo de cabeceras, autenticación, paginación.
- ORM / Abstracciones: usar una capa que mapea tablas a objetos (por ejemplo: Eloquent, Prisma) para evitar SQL explícito.
- Streaming: procesar datos en flujo (útil para ficheros grandes o eventos en tiempo real).
Formatos de datos habituales
- CSV: simple, tabular, muy interoperable.
- JSON: formato de facto para APIs. Legible y flexible.
- XML: pesado pero estándar en ciertos dominios (SOAP, config).
- Binarios / Protobuf / Avro: compactos y eficientes en red/almacenamiento (por ejemplo: microservicios a gran escala).
A la hora de elegir un formato, se debe considerar elegir formato según tamaño de datos, necesidad de esquema, compatibilidad y velocidad de parseo.
Ejemplo JSON (objeto único):
{
"id": 12,
"nombre": "Ana",
"roles": ["admin", "editor"]
}
Ejemplo CSV (línea):
id,nombre,email
12,Ana,ana@example.com
Integridad y consistencia (ACID vs BASE)
- ACID (RDBMS tradicional):
- Atomicity: una transacción se aplica por completo o no se aplica.
- Consistency: las reglas/constraints mantienen la base en un estado válido.
- Isolation: transacciones concurrentes no interfieren (niveles de aislamiento).
- Durability: una vez confirmada, la transacción persiste incluso tras fallos.
- BASE / eventual consistency (sistemas distribuidos/NoSQL):
- Basically Available, Soft state, Eventual consistency: alta disponibilidad y particionado a costa de consistencia inmediata.
- Teorema CAP: en presencia de partición (P) hay que elegir entre Consistency (C) y Availability (A).
Concurrencia y control de acceso
- Problemas típicos: condiciones de carrera (race conditions), actualizaciones perdidas (lost updates), dirty reads.
- Mecanismos:
- Bloqueos (locks): pesimista.
- Control optimista (versioning / timestamp): detecta conflictos en commit.
- Niveles de aislamiento: read uncommitted → serializable.
Ejemplo de race condition:
- Dos procesos leen un balance, ambos suman y sobreescriben.
- Si no hay transacción con bloqueo, se pierde actualización.
Seguridad y buenas prácticas
- Validación y sanitización de entradas (evitar inyección SQL/XSS).
- Consultas parametrizadas (prepared statements) en vez de concatenar strings.
- Principio de menor privilegio: cada conexión/usuario DB solo con permisos necesarios.
- Cifrado: TLS para datos en tránsito; cifrado en reposo cuando hay datos sensibles.
- Gestión de secretos: no hardcodear credenciales (escribir contraseñas directamente en los ficheros). En su lugar, usar gestores (Vault, variables de entorno seguras).
- Registro y auditoría: quién accede, cuándo, y qué cambios se hicieron.
- Rate limiting y protección API keys para recursos externos.
Rendimiento y escalabilidad
- Indexado: mejora lecturas con coste en escrituras; diseñar índices según consultas frecuentes.
- Consultas seleccionadas: evitar
SELECT *, limitar columnas y filas. - Paginación: evitar
OFFSETen tablas grandes; usar cursors o keyset pagination. - Caché: Redis/Memcached para respuestas frecuentes.
- Batching / bulk operations: agrupar inserts/updates para reducir overhead.
- Connection pooling: evitar crear/concluir conexiones por cada operación.
- Denormalización cuando la velocidad de lectura es prioritaria (con control del costo de mantener consistencia).
Fiabilidad y resiliencia
- Backups y plan de recuperación (testear restauración).
- Retries con backoff exponencial en llamadas a externos. Hacer idempotentes las operaciones cuando sea posible.
- Timeouts: fijar límites de tiempo de respuesta razonables en llamadas remotas.
- Circuit breaker: evitar sobrecargar servicios caídos.
- Monitoreo y alertas: latencia, ratios de error (error rate), uso de recursos.
Diseño de datos y modelado
- Modelado relacional: tablas, claves primarias/foráneas, normalización (1NF, 2NF, 3NF).
- Modelado documental: decidir la granularidad del documento (anidar vs referenciar).
- Decisión tecnológica: elegir SQL o NoSQL en función de:
- Patrones de acceso (lecturas vs. escrituras).
- Necesidad de transacciones fuertes.
- Flexibilidad del esquema.
- Volumen y escalado esperado.
Ejemplos
SQL (selección y transacción):
BEGIN;
UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1;
UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2;
COMMIT;
Llamada a API (curl, paginación simple):
curl "https://api.example.com/users?page=1&per_page=50" -H "Authorization: Bearer <token>"
File streaming (pseudocódigo):
open file for reading
while chunk = read_next_chunk():
process(chunk)
close file