Loading...
Loading...
Document stores ended with the embed-versus-reference rule: embed bounded profile data read together, reference anything growing without bound, because one viral document swells past the 16 MB cap and each one-line edit rewrites megabytes. That rule assumes records worth fetching whole. Thermometers phone home every second until a million devices produce a billion rows a month. Nobody ever asks for reading number 4,000,002 by identifier. Everyone asks for the average temperature last Tuesday or every spike over 90 degrees this week, which are slices across time touching millions of rows at once. Row-by-row storage reads whole rows to answer questions about one column, fetching far more bytes than the question needs.
Partitioning the relational table by time helps into the terabytes but fails past sustained millions of writes per second with multi-region availability, because each write still pays row overhead and each scan still crosses row boundaries. A second tempting fix is monthly document buckets, one parcel per device per month, and it fails on size with numbers: a device phoning home every second produces 2.6 million readings a month, so the bucket swells past the 16 MB document ceiling within days, range scans deserialize megabytes to answer one average, and quorum writes still funnel through one primary. The layout itself must change.
Wide-column stores, which group data by column families, sort rows by key, and spread partitions across machines, are built for scanning oceans of time-ordered data and counting inside them. Think of a post office that files mail into dated bins per neighborhood rather than one pile per sender: one analogy for the whole idea, where each bin holds one neighborhood's week contiguously so range scans read sequentially.
One sentence version: rows fetch one thing whole, while columns count across everything. Pick the layout matching the dominant question.
A row key, which is the address deciding sort order and placement, determines which disk serves a range. A column family, which is a declared group of related columns stored together physically, fixes the neighborhood layout while individual columns stay flexible. A timestamp on every cell preserves history across overwrites instead of destroying it.
| Row key | profile (family) | activity (family) | ||
|---|---|---|---|---|
| name | last_login | pages_viewed | ||
| user:123 | Alice | a@b.com | 2024-01-15 | 1542 |
| user:456 | Bob | - | 2024-02-20 | - |
Bob simply lacks columns Alice has, with no nulls stored and no space wasted. Rows sharing a time bucket sit together on disk, so scanning one week reads sequentially.
Fluency needs only the address scheme plus what history costs. Design the address around the hottest query, keep neighborhoods declared while facts stay sparse, and remember that every overwrite keeps its predecessor until merging and expiry clear it.
Rows sort and spread by it, so build it around the hottest query, usually the entity plus a time bucket rather than raw time.
Related columns share physical storage and tuning. Declared up front while individual columns stay flexible.
Name plus value added anytime. Sparse data costs nothing because absent columns occupy no space.
Every cell carries one, so overwrites accumulate versions until compaction and expiry reclaim them.
Cassandra, which is the masterless distributed store where every node accepts writes with no single point of failure, suits always-on ingestion at the cost of modeling every query up front. HBase, which is the strongly consistent wide-column store running atop the Hadoop file system, suits data already living in that ecosystem. Bigtable, which is the original managed wide-column service behind planet-scale search, maps, and mail products, suits teams wanting single-digit-millisecond reads without operating the fleet.
No masters and no single point of failure, with writes that never block on a leader. The price is modeling around known queries, because ad-hoc questions scan painfully.
Stronger consistency than masterless designs, married to distributed file storage. Pick it when the data and tooling already live there.
The original design behind petabyte-scale consumer products, offering single-digit-millisecond reads without operating servers yourself.
Sensor streams, metrics, and logs arrive timestamped forever and are scanned by range rather than fetched by identifier. Chat histories and feeds are written constantly and read back in order. Event funnels aggregate behavior where counting is the whole job, and watch histories feed recommenders that never sleep. Each workload scans contiguous time buckets far more than it fetches single rows.
Sensor readings, metrics, and logs stamped with time, endless in volume, and scanned by range.
Chat histories and feeds written constantly and replayed sequentially per conversation.
Events and behavior steps counted across millions of users rather than inspected singly.
Watch histories and preferences scanned continuously to refresh suggestions.
Count the capacity: a billion sensor rows a month at 100 bytes each need 1,000,000,000 times 100 bytes, which is 100,000,000,000 bytes or 100 GB a month, reaching 1.2 TB a year per metric. Because rows sort by key, a key of device plus day keeps one device's week contiguous so a range scan reads sequentially. A key starting with raw timestamp does the opposite: every current write lands on the same leading-edge partition while older partitions idle, which is a hotspot. Partitions cap in practice near 100 MB and single-digit millions of cells, past which merging strains and repairs crawl. The checklist is partition by the queried entity such as device or tenant, cluster by time within it, and size time buckets by day or week so no partition grows without bound.
Good: ((device_id, day), timestamp) spreads writes while reads scan one partition
Hot: (timestamp, device_id) hammers the newest partition with every write
Too wide: (device_id) alone for a decade-long sensor grows one giant partition
With a replication factor of 3, which means three copies of each partition, writing with a quorum of 2 acknowledgments and reading with a quorum of 2 responses guarantees overlap, because reads plus writes exceed total copies, so reads observe the latest write. Loosening both sides to 1 acknowledgment buys single-digit-millisecond speed with genuinely eventual visibility, which is the standing tension between availability and freshness in large messaging fleets. The unprepared-operator failure caps the lesson: a node stays down past the hinted-handoff window, which is the bounded time neighbors hold its missed edits, returns needing full repair, and deleted data resurrects when tombstones, which are deletion markers, expire before repair completes. Budget tombstone grace periods and repair schedules alongside quorum levels, not instead of them.
Sensor data has a shelf life: raw seconds matter for days, rollups matter for months, and older detail serves only compliance. Time-to-live, which is per-cell expiry set at write time, deletes through tombstones instead of a midnight batch job that doubles merging load. The edge case is mass expiry, where a billion cells expiring the same hour create a tombstone storm that slows reads until merging clears them. Stagger expiry with daily partitions aging out one at a time, and pre-aggregate before expiry so dashboards never need the expired raw past.
Columns count oceans beautifully by keeping time contiguous on disk, with quorum arithmetic deciding when reads see the latest write and tombstone grace periods deciding when deleted data stays dead. Counting assumes the question is about aggregates. Some questions are not about counting but about connections: who follows whom three hops out, which accounts share a device with a known scammer, where trouble hides past depth three. For those, rows and columns both miss the point, because the shape of the data is the question itself.