Saltar al contenido principal

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 OFFSET en 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