↑ top
← Back to blog
EN | ES

KV vs D1: Cuándo Usar Cada Uno en Cloudflare Workers

KV es un almacén clave-valor global con consistencia eventual. D1 es SQLite con consistencia fuerte pero escrituras regionales. Elegir mal te cuesta después.

✦ AI Summary

Cloudflare Workers te da dos primitivas de almacenamiento: KV (almacén clave-valor) and D1 (base de datos) KV es una caché distribuida globalmente. D1 is una base of datos with una única región de escritura.

KV vs D1: Cuándo Usar Cada Uno en Cloudflare Workers

Cloudflare Workers te da dos primitivas de almacenamiento que parecen similares pero se comportan de manera muy diferente: KV (almacén clave-valor) y D1 (base de datos SQLite). Ambas son accesibles como bindings del Worker. Ambas almacenan datos persistentes. La elección entre ellas no es obvia leyendo la documentación.

Aquí está el modelo mental que hace que la decisión sea directa.

La Diferencia Central

KV es una caché distribuida globalmente. Las escrituras se propagan a todas las ubicaciones edge en 60 segundos. Las lecturas son rápidas en todo el mundo, pero puedes leer un valor desactualizado justo después de escribir. Es eventualmente consistente.

D1 es una base de datos SQLite con una única región de escritura. Las lecturas se sirven desde réplicas regionales. Las escrituras son fuertemente consistentes — una lectura después de una escritura siempre ve el valor escrito — pero las escrituras tienen mayor latencia porque van a la región primaria.

Esta única distinción guía la mayoría de la decisión de "cuándo usar cuál".

Usa KV Para

Contenido que se escribe poco y se lee mucho. Contenido de posts, configuración, resultados de IA cacheados. El cuerpo de un post de blog se escribe una vez (al publicar) y se lee miles de veces. El rendimiento de lectura global de KV es ideal. La consistencia eventual no importa porque el contenido rara vez cambia.

javascript// Se escribe una vez al publicar
await env.POSTS_KV.put(`post:${slug}`, markdownContent);

// Se lee en cada visita — rápido globalmente
const content = await env.POSTS_KV.get(`post:${slug}`, "text");

Entradas de caché con TTL. KV soporta expiración por clave. Rate limiting, tokens de sesión, flags temporales — cualquier cosa con una expiración natural.

javascript// Clave de rate limit que expira a los 60 segundos
await env.POSTS_KV.put(`ratelimit:${ip}:${slug}`, "1", { expirationTtl: 60 });

Valores grandes. KV soporta valores de hasta 25MB. Las filas de D1 no están diseñadas para blobs grandes.

Usa D1 Para

Datos que necesitan reflejar escrituras de inmediato. Contadores de visitas, preferencias de usuario, cualquier cosa donde un usuario toma una acción y espera ver el resultado enseguida.

Un contador de visitas en KV funcionaría, pero un visitante que incrementa el contador podría leer el valor previo al incremento en la siguiente request (consistencia eventual). En D1, la lectura después de una escritura siempre devuelve el valor actualizado.

javascript// D1: upsert + lectura en la misma request — siempre devuelve el conteo actualizado
await env.DB.prepare(
  "INSERT INTO post_views (slug, views) VALUES (?, 1) ON CONFLICT(slug) DO UPDATE SET views = views + 1"
).bind(slug).run();
const r = await env.DB.prepare("SELECT views FROM post_views WHERE slug = ?").bind(slug).first();

Datos consultables. ¿Necesitas ordenar posts por número de visitas? ¿Filtrar por rango de fechas? ¿Hacer joins entre tablas? SQL maneja esto limpiamente. KV requiere traer todos los valores y filtrar en JavaScript.

sqlSELECT slug, views FROM post_views ORDER BY views DESC LIMIT 10

Datos relacionales. Si necesitas imponer relaciones entre entidades — tags pertenecientes a posts, comentarios pertenecientes a usuarios — los constraints y joins de SQL son la herramienta correcta.

La Zona de Solapamiento

Ambas pueden almacenar pares clave-valor simples. La pregunta es qué tradeoffs prefieres.

KV D1
Latencia de lectura ~5ms globalmente ~10-20ms (réplica)
Consistencia de escritura Eventual (~60s) Fuerte
Latencia de escritura ~10ms ~20-50ms
Tamaño máximo de valor 25MB ~1MB por fila
Lenguaje de consulta Ninguno (get/put/list) SQL completo
Soporte de TTL Manual (delete + cron)

En este blog: el contenido de los posts y los resúmenes de IA viven en KV (se escriben una vez, se leen mucho, valores grandes). Los contadores de visitas viven en D1 (necesitan consistencia fuerte, necesitan ordenarse por conteo).

El Error a Evitar

Usar KV para datos que necesitan reflejar mutaciones de inmediato. El modo de fallo clásico: el usuario hace clic en un botón de reacción, escribes en KV, luego lees para mostrar el conteo actualizado — y obtienes el conteo viejo porque KV todavía no propagó.

La solución es D1 (fuertemente consistente) o UI optimista (mostrar el valor esperado en el cliente sin leer de vuelta desde el servidor).

La consistencia eventual de KV no es un bug — es el tradeoff que hace posible las lecturas globales de baja latencia. Solo no la uses cuando necesitas consistencia inmediata de lectura-después-de-escritura.

Was this helpful?