42 puntos por GN⁺ 14 일 전 | 26 comentarios | Compartir por WhatsApp
  • Todas las bases de datos son, al final, conjuntos de archivos estructurados sobre un sistema de archivos, por lo que una aplicación en etapa inicial puede lograr rendimiento suficiente gestionando archivos directamente
  • Al implementar el mismo servidor en Go, Bun y Rust y comparar tres enfoques —escaneo de archivos, mapa en memoria y búsqueda binaria en disco—, se observó que incluso con acceso simple a archivos se puede lograr un alto rendimiento
  • El enfoque con mapa en memoria mostró el mejor rendimiento (hasta 169k req/s), mientras que SQLite fue estable con 25k req/s, aunque con cierto overhead
  • La mayoría de los servicios pueden manejar hasta alrededor de 90 millones de DAU con un solo archivo SQLite, por lo que en la etapa inicial del producto no hace falta una base de datos separada
  • La adopción de una base de datos se vuelve necesaria cuando el dataset supera la RAM o se requieren joins, búsquedas con múltiples condiciones, escrituras concurrentes o transacciones

¿De verdad necesitas una base de datos?

  • Una base de datos es, al final, un conjunto de archivos: SQLite es un solo archivo y PostgreSQL está compuesto por un directorio y procesos
    • Todas las bases de datos leen y escriben en el sistema de archivos, y funcionan de la misma forma que cuando el código llama a open()
    • Por eso, la pregunta clave no es “¿vas a escribir archivos?”, sino “¿vas a usar los archivos de una base de datos o vas a administrarlos tú mismo?
    • Muchas aplicaciones en etapa inicial pueden lograr rendimiento suficiente incluso administrándolos directamente

Configuración del experimento

  • Se implementó el mismo servidor HTTP en Go, Bun (TypeScript) y Rust, comparando dos estrategias de almacenamiento
    • Se usaron tres archivos JSONL: users.jsonl, products.jsonl, orders.jsonl
    • Creación con POST /users y consulta con GET /users/:id
    • Solo la ruta de consulta (GET) se tomó como objetivo del benchmark
  • Enfoque 1: leer el archivo en cada solicitud

    • En cada request se abre el archivo, se escanean todas las líneas, se parsea el JSON y se verifica si el ID coincide
    • En promedio hay que leer la mitad del archivo, así que la complejidad es O(n)
    • A medida que crecen los datos, la velocidad de procesamiento por request cae con fuerza
  • Enfoque 2: cargar todo en memoria

    • Al iniciar, se lee el archivo completo y se guarda en un hash map basado en ID
    • Las escrituras se reflejan tanto en el mapa como en el archivo, y las lecturas son una sola consulta al mapa, con complejidad O(1)
    • El archivo actúa como almacenamiento persistente y el mapa como índice
    • Go usa sync.RWMutex y Rust RwLock para permitir lecturas en paralelo
  • Enfoque 3: búsqueda binaria en disco

    • Una solución intermedia para consultas rápidas sin subir todos los datos a la RAM
    • Se genera un archivo de datos ordenado por ID y un archivo de índice de ancho fijo (58 bytes por registro)
    • Con ReadAt se busca en el índice en O(log n) y luego se lee un solo registro desde el offset correspondiente
    • Al agregar nuevos registros se rompe el orden, por lo que hace falta regenerar o fusionar periódicamente el índice
    • Ese patrón de fusión se parece al funcionamiento de un LSM-tree

Entorno del benchmark

  • Tamaño del dataset: 10k, 100k y 1M de registros
  • Herramienta de carga: wrk, con requests GET aleatorios durante 10 segundos, 4 hilos y 50 conexiones concurrentes
  • Pruebas en la misma máquina (Apple M1 Mac mini, macOS 15) con Go 1.26, Bun 1.3 y Rust 1.94
  • En Go también se comparó adicionalmente con búsqueda binaria (disco) y SQLite (modernc.org/sqlite)

Resultados principales

  • Caída del rendimiento en escaneo lineal: con 1M de registros, Go bajó a 23 req/s y Bun a 19 req/s
  • Búsqueda binaria (disco): entre 10k y 1M de registros pasó de 45k a 38k req/s, una caída de solo 15%
    • Gracias al page cache del sistema operativo, la parte superior del índice permanece siempre en memoria
  • SQLite: mantuvo rendimiento consistente con 25k req/s y latencia promedio de 2 ms
  • La búsqueda binaria fue aproximadamente 1.7 veces más rápida que SQLite; en consultas simples por PK hay overhead en SQLite
  • El mapa en memoria fue el más rápido: entre 97k y 169k req/s, con latencia menor a 0.5 ms
  • Bun fue más rápido que Go: Bun 106k req/s frente a Go 97k req/s
    • Bun se basa en JavaScriptCore + Zig (uWebSockets) y evita libuv
  • Rust dominó en escaneo lineal: fue entre 3 y 6 veces más rápido que Go, probablemente por eficiencia de parsing JSON e I/O
  • Mejor opción según el caso de uso

    • Máximo throughput absoluto: mapa en memoria en Rust (169k req/s)
    • Mejor sin cargar todo en RAM: búsqueda binaria en Go (~40k req/s)
    • Si necesitas SQL: SQLite (25k req/s)
    • Implementación más simple: escaneo lineal en Go (~20 líneas de código)

Qué significan 25,000 req/s

  • En tráfico web normal se asume una proporción pico:promedio = 2:1
    • Promedio de 12,500 req/s → pico de 25,000 req/s
  • Suponiendo que un usuario activo hace 10 consultas por hora y que en el pico hay una tasa de concurrencia del 10%
    • Fórmula de requests en pico: DAU × 0.000278
  • Resultado del cálculo de DAU de saturación para cada enfoque
    • Escaneo lineal en Go: 2.8M
    • Búsqueda binaria en Go: 144M
    • SQLite: 90M
    • Mapa en memoria en Go: 349M
    • Mapa en memoria en Bun: 381M
    • Mapa en memoria en Rust: 608M
  • La mayoría de los productos nunca llegan a esas cifras
    • Ejemplo: 10,000 clientes SaaS → 3 req/s, una app con 100,000 DAU → 30 req/s
  • En conclusión, la mayoría de los productos en etapa inicial no necesitan una base de datos
    • Y si la necesitas, un solo archivo SQLite puede manejar hasta 90 millones de DAU

Cuándo sí necesitas una base de datos

  • Cuando el dataset ya no cabe en RAM

    • Con decenas de millones de registros, solo el índice ya puede requerir varios GB
    • Hace falta paginación de datos, y una base de datos se encarga de eso automáticamente
  • Cuando necesitas consultar por campos distintos al ID

    • Las búsquedas con múltiples condiciones requieren escaneo de archivos o mapas adicionales
    • Mantener varios mapas es, en la práctica, implementar tu propio motor de consultas
  • Cuando necesitas joins

    • Hay que leer y combinar varios archivos, y SQL resulta más eficiente
  • Cuando hay escrituras concurrentes desde múltiples procesos

    • Los mapas en memoria de cada instancia quedan separados y se pierde consistencia
    • Hace falta una única fuente externa de verdad → ese es el rol de la base de datos
  • Cuando necesitas escrituras atómicas entre entidades

    • Es necesario garantizar el éxito o fracaso conjunto de la creación de un pedido y el descuento de inventario
    • Habría que implementar un log de transacciones por separado, y la DB lo resuelve con ACID
    • En cambio, herramientas internas, side projects y productos iniciales sin esas restricciones
    • pueden funcionar perfectamente dentro de la RAM de un solo servidor
    • y luego los archivos JSONL pueden migrarse fácilmente a una base de datos

Apéndice y código disponible

  • Incluye código del servidor en Go, Bun y Rust
  • También se entregan los datos de seed y el script para ejecutar benchmarks (run_bench.sh)
  • El archivo ZIP incluye go-server/, bun-server/, rust-server/ y seed.ts
  • El script genera datos en tres escalas, corre pruebas de carga con wrk y luego termina

Información relacionada con DB Pro

  • DB Pro** es un cliente de bases de datos para Mac, Windows y Linux**

    • Integra funciones de consulta, exploración y administración
    • Incluye una plataforma web colaborativa y soporte de IA incorporado
    • En la versión más reciente, agrega soporte para conectar bases de datos SQLite de Val Town
    • En la v1.3.0 se añadieron creación de bases de datos, editor de múltiples consultas y conexión con PlanetScale Vitess

26 comentarios

 
happing94 14 일 전

¿Qué demonios es esta estupidez? ¿Creen que la base de datos se usa por rendimiento?

 
unknowncyder 14 일 전

Sí, yo también pensé que quizá habría alguna idea nueva, así que revisé el texto original, pero ¿qué es esto...

En vez de arrancar con cosas básicas como que la memoria es cara y por eso se usa disco, o la estabilidad operativa en producción, o la atomicidad, se ponen de golpe a comparar velocidades, y la verdad da hasta risa.

“Vendemos DB, pero eso no significa que siempre necesites una DB”. No sé si quieren hacer marketing al dejar un artículo así y decir esto con tanta soltura... Aunque intente verlo de forma positiva, a veces me pongo algo cínico.

Supongo que al menos nos quedó un benchmark.

 
myc0058 6 일 전

Es el típico código de boquilla.

 
botplaysdice 13 일 전

Me parece un texto muy bueno. En especial, este tipo de material con esos 'números' es valioso. Estamos en una época en la que no es fácil encontrar desarrolladores que al menos tengan una idea aproximada del overhead que tienen el código que hacemos y el stack tecnológico que traemos para usar, así que lo leí con gusto.

 
foriequal0 12 일 전

Yo también estoy de acuerdo. Creo que es un material que aporta una intuición importante sobre la mechanical sympathy y sobre cómo modular el ritmo del desarrollo. Como Latency Numbers Every Programmer Should Know.
Y tampoco me pareció que este texto dijera que una dirección específica sea incondicionalmente mejor. Más bien, como las cifras que mostraron todos los enfoques mencionados en el texto se veían como un “rendimiento que sobra para la gran mayoría de los negocios”, lo leí como una invitación a elegir el enfoque que mejor se adapte a la situación del problema.

 
botplaysdice 13 일 전

Y las joyitas de las respuestas son un extra.

 
white9s 13 일 전

Si hay una razón para hacerlo así, entonces valdría la pena considerarlo, ¿no? Por ejemplo, si las limitaciones de rendimiento fueran realmente muy severas.
Pero en la mayoría de los casos, ¿realmente hay una razón para elegir esto a la fuerza? No es como si la base de datos no ofreciera ventajas...

 
m00nlygreat 13 일 전

Solo se siente como un cambio de perspectiva, pero todos están muy sensibles.

 
tazuya 13 일 전

Así es. Se puede ver como una propuesta de que, al inicio del negocio, cuando todavía no hay muchos usuarios, en lugar de comprar una base de datos o complicarse demasiado, podría ser posible llegar hasta que el negocio se estabilice solo con I/O básico de archivos.

 
smash8106 13 일 전

Yo también estoy de acuerdo. A veces, en los servicios, se le da a la BD más importancia de la necesaria y, en ocasiones, incluso se invierte demasiado en el diseño, como si romper la normalización fuera a causar un gran problema.
No se trata de no usar una BD, sino de refrescar un poco la perspectiva sobre por qué la usamos y cuál es realmente la base del servicio.
Al final, el balance siempre es lo importante.

 
cafedead 13 일 전

Desde el momento en que eliges SQLite para un servidor de producción, tienes que estar pensando constantemente en cuándo migrar a otra cosa.
Antes valía la pena pensarlo porque el costo del DB en sí (compra de servidores, IDC, licencias, etc.) era alto,
pero hoy en día supuestamente se puede montar con un simple clic, así que ¿de verdad hace falta pensarlo tanto?

 
roxie 8 일 전

Incluso ahora, las bases de datos son caras.

 
csjune 12 일 전

Claro, si es un "proyecto en etapa inicial o una aplicación pequeña", puede que no necesites una base de datos. No solo la base de datos, también puedes resolver más o menos cualquier otro elemento como sea. El problema es cuando escala. Es solo un artículo para ver números por diversión.

 
carnoxen 13 일 전

https://hackers.pub/@gnh1201/2025/…

A veces no hace falta instalar una base de datos aparte. Aunque está limitado a Windows...

 
roxie 8 일 전

Con solo ver el título, me solté a carcajadas.

 
kuthia 13 일 전

A veces pienso si realmente es necesario que las entidades principales garanticen la persistencia mediante un RDBMS. También hay bastantes tecnologías alternativas para proporcionar una SSOT.

 
neptune 13 일 전

Si se rompe SQLite, no hay nada que hacer...

 
okxrr 13 일 전

¿Hay casos en los que sqlite se corrompa? Me da curiosidad. Excluyendo traslados o eliminaciones anormales de archivos.

 
GN⁺ 14 일 전
Comentarios de Hacker News
  • Me gusta mucho este artículo. Muestra muy bien lo rápidas que son las computadoras
    Aun así, no estoy de acuerdo con la conclusión de la parte final. El autor dijo que no aplica a apps con muchas restricciones, como aquellas donde “varios procesos necesitan escribir al mismo tiempo”, pero en la práctica incluso en productos en etapa inicial suele pasar que workers separados, como cron o una message queue, necesiten escribir simultáneamente
    Se puede hacer que solo escriba el servidor principal, pero eso aumenta la complejidad de la arquitectura
    Así que, desde una perspectiva puramente de escala, estoy de acuerdo con el autor, pero en un sentido más amplio creo que es mejor usar una base de datos. En particular, SQLite es una opción razonable
    Si necesitas escalar, puedes cachear en memoria los datos a los que accedes con frecuencia. La combinación que uso es SQLite + caché en memoria

    • A mí también me pasa algo parecido muy seguido. Aunque un solo servidor sea suficiente, en cuanto necesitas redundancia de servidores, hace falta almacenamiento en red y al final uno termina inclinándose por una DB accesible por red
      A veces S3 funciona, pero sigue teniendo muchas limitaciones para usarlo como reemplazo completo
    • Hoy en día, cuando empiezo un proyecto nuevo, por defecto uso SQLite. El rendimiento es muy rápido, y si más adelante crece en tamaño, también es fácil migrar a Postgres
      Es mucho más simple y barato porque no hace falta administrar ni respaldar un servidor de DB aparte
    • Después de ver el benchmark de Rust 1M, volví a darme cuenta de lo rápidas que son las computadoras
  • Me encanta SQLite, pero me di cuenta de que no es la respuesta para todos los problemas
    Al crear una app de diccionario del lado del cliente, probé el port wasm de SQLite, pero el archivo de la DB era más grande de lo esperado, no comprimía bien y además cargaba lento
    Al final cambié a un enfoque donde genero índices directamente desde el archivo TSV original y lo comprimo con zstd, para luego descomprimirlo cada vez en wasm. Esto fue mucho más rápido que SQLite
    El tamaño del módulo también bajó de 800KB a 52KB, y no había problema aunque levantara varias instancias al mismo tiempo
    Para la búsqueda de cadenas usé stringzilla, y es absurdamente rápido
    SQLite es excelente, pero no es la respuesta correcta en todos los casos

  • El benchmark de SQLite está poco optimizado
    Con solo agregar

    db.SetMaxOpenConns(runtime.NumCPU())
    db.SetMaxIdleConns(runtime.NumCPU())
    

    el rendimiento en mi máquina saltó de 27,700 r/s a 89,687 r/s
    Probé prepared statements y cambiar timestamp a int, pero no hubo una gran diferencia

  • El artículo estaba bien, pero la parte de que “todas las DB acceden al filesystem con open()” no es precisa
    Apps como SQLite usan mmap para mapear directamente el archivo al espacio de memoria. Con este método se pueden saltar syscalls y acceder mucho más rápido
    Más adelante el artículo explica el proceso de leer todo el archivo en memoria, pero habría sido mejor usar mmap

    • Es cierto que el artículo simplificó la explicación del IO de una DB
      Aun así, no necesariamente diría que mmap siempre es mejor. Hay gente que prefiere manejarlo directamente en la lógica de la aplicación en vez de depender de la API del OS
      Como referencia, está el estudio de CMU sobre mmap
    • El backend store que usa mmap al final también es un archivo del filesystem
      Decir que “funciona como open()” es una simplificación, pero técnicamente no está mal
  • Hace mucho hice una pequeña web app de ventas en Perl, pero como no podía instalar nada en el servidor del ISP, usé un hash basado en archivos
    El cliente lo siguió usando tal cual por más de 20 años hasta que falleció, y cuando su familia tomó el control lo cambiaron por Wordpress
    La última vez que lo revisé tenía cientos de miles de pedidos y el rendimiento seguía siendo aceptable
    Gracias a la evolución del hardware, esta estructura medio hack aguantó mucho más de lo esperado. Si fuera hoy, probablemente SQLite también habría sido suficiente

    • Me da curiosidad qué producto vendía ese sitio
  • Si implementas el storage por tu cuenta, puedes entender cómo funciona una DB
    Tienes que manejar índices y estructuras de datos de forma eficiente, y al final llegas a la conclusión de que “si no era un juguete, debiste haber usado una DB desde el principio”

  • Relational Databases Aren’t Dinosaurs, They’re Sharks En comparación con la pequeña ganancia que obtienes en una app chica, es muchísimo mayor la pérdida de tiempo por reinventar la rueda

    • La analogía de tiburones vs dinosaurios es realmente adecuada
      En la época del Cretácico los tiburones ya tenían casi la misma forma que hoy, y sobrevivieron después sin grandes cambios
      En cambio, dinosaurios, pterosaurios y mosasaurios desaparecieron, mientras que tiburones, cocodrilos y serpientes grandes siguen existiendo casi igual hasta hoy gracias a un diseño optimizado
      Creo que las DB relacionales son algo así
  • Disfruto leer este tipo de artículos
    Aun así, en el 99% de los casos sigo usando una DB con SQL y transacciones
    Pero en un proyecto personal reciente probé manejar los datos con un filesystem simple basado en archivos YAML, y a mi escala no hubo ningún problema de rendimiento
    Que fuera legible por humanos y permitiera diff era más importante que el rendimiento
    De todas formas, en la mayoría de los casos elegiría una DB con lenguaje de consultas y consistencia garantizada

  • Al final siempre terminas necesitando las funciones de una DB y las garantías ACID
    Cada vez que me toca usar un store legacy de archivos planos, sufro tratando de pegarle consistencia, transacciones y un lenguaje de consultas a la fuerza. Al final es reinventar la rueda

  • En el momento en que necesitas atomicidad, una DB se vuelve indispensable
    Implementar escrituras atómicas sobre un filesystem es muy frágil
    Por eso muchas DB sufren problemas de corrupción de datos cuando hay crashes. Antes pasaba con RocksDB en Windows

    • Si necesitara cambios atómicos en archivos, simplemente usaría SQLite
      Implementarlo por mi cuenta me parece una locura. Estaría bien aprender a escribir de forma segura usando la API del OS, pero hoy en día eso ya es una habilidad demasiado de nicho
      Además, es muy probable que quien venga después no pueda mantenerlo. Al final terminarías migrando a una DB
    • El código del artículo en algún momento se va a quedar en un archivo vacío si hay un corte de luz
      Como mínimo, hay que escribir en un archivo temporal dentro del mismo filesystem, hacer fsync y luego reemplazar con rename
    • En casos simples no es tan frágil
      Si escribes toda la DB a un archivo temporal, haces flush y luego lo reemplazas con move, en Unix eso es atómico
      Pero esto no escala en absoluto. Incluso una actualización pequeña obliga a reescribir el archivo completo, y además necesitas manejar locks. Solo resuelve una parte de ACID
    • Viéndolo así, en realidad ya estás lidiando con la A de ACID
      Como referencia, DuckDB, que es una DB OLAP, también funciona excelentemente en workloads out-of-core
    • A partir de 2025, Linux + ext4 soporta escrituras atómicas de uno o varios bloques
      Enlace a la documentación oficial
 
mstorm 14 일 전

Se puede vivir sin refrigerador, pero sería incómodo.
No hay razón para no usarlo si puedes hacerlo.

 
foobarman 13 일 전

¿Eres un usuario de Ilbe o qué?

 
alfenmage 6 일 전

¿Si digo que no, entonces ya soy de Ilbe? ¿Y yo que soy de Gyeongsang-do qué?

 
foobarman 5 일 전

Quiero reportarlo, pero no sé cómo hacerlo. Uf.

 
okxrr 13 일 전

Parece que este comentario refleja la forma de pensar tan encerrada de los desarrolladores coreanos y el nivel de GeekNews.

 
alfenmage 6 일 전

Explica cuál es ese nivel, por qué evaluaste el nivel de esa manera, y dilo usando al menos dos entre lógica/hechos/ciencia/estadística, sí.

 
foobarman 5 일 전

Jaja, con solo ver las palabras ya se nota que es de DC Inside, Ilbe o Fmkorea, así que no le hagas caso.