Under the Hood: The Three Storage Abstractions
Enterprise infrastructure categorizes persistent storage into three distinct fundamental primitives: Block Storage, File Storage, and Object Storage. Choosing the wrong primitive leads to catastrophic scaling bottlenecks and astronomical cloud bills.
Storage Primitive Comparison
Detailed Architectural Differences
| Feature | Relational / NoSQL Database | Object Storage (S3 / GCS) |
|---|---|---|
| Data Granularity | Fine-grained: Mutate single columns, rows, or key-value fields. | Coarse-grained: Entire blobs (bytes 0 to N). Partial in-place mutation is not supported; an object can only be replaced or deleted as a whole. |
| Access Protocol | Binary database protocols (TCP sockets, prepared statements, gRPC). | HTTP/1.1 and HTTP/2 REST APIs (PUT /bucket/key, GET /bucket/key). |
| Latency Profile | Sub-millisecond to low single-digit milliseconds (100µs - 5ms). | Tens to low hundreds of milliseconds time-to-first-byte (AWS cites roughly 100-200 ms for small objects on S3 Standard; S3 Express One Zone offers single-digit milliseconds). |
| Throughput & Scaling | Bound by cluster instance size and replication topologies. | Virtually infinite: Scale to millions of concurrent requests across petabytes. |
| Cost per Gigabyte | High (~$0.10 - $0.50 / GB / month on managed SSD volumes). | Ultra-Low (~$0.015 - $0.023 / GB / month on standard tiers). |
Worked example: 50 million photos
50 million images averaging 500 KB is 25 TB.
| Option | Approximate monthly storage cost | Notes |
|---|---|---|
| S3 Standard, about USD 0.023 per GB-month | About USD 575 | Durability across multiple AZs is included |
| Database on gp3 SSD, about USD 0.08 per GB-month, 3 replicas | About USD 6,000 before instance costs | Plus larger backups and slower replica rebuilds |
Request costs matter too. At roughly USD 0.005 per 1,000 PUTs and USD 0.0004 per 1,000 GETs, 50 million uploads cost about USD 250 once and 100 million monthly reads cost about USD 40; a CDN in front usually removes most reads. Prices vary by region and change over time, so treat these as orders of magnitude.
The standard pattern
- Client asks the API for an upload; the API creates a pending row
(photo_id, owner, status=pending)and returns a presigned PUT URL for keyphotos/ab/photo_id. - Client uploads directly to object storage, bypassing the application servers.
- An upload-complete event or client callback verifies size and checksum, then marks the row
active. - A periodic sweeper deletes objects with no active row older than a day, and alerts on active rows whose object is missing.
When a database blob is reasonable
- Values are small (kilobytes) and read together with the row.
- You need the blob to commit or roll back atomically with other rows.
- The total volume is modest and the operational simplicity of one system wins.
Since December 2020, S3 provides strong read-after-write consistency for PUTs and LISTs, which removes an old class of "row says active but GET returns 404" races.