Query First Data Modeling and Partition Design
Query-First Schema Design
Wide-column stores require query-first design: you create separate tables for each access pattern, storing denormalized (pre-joined) data optimized for specific queries. This is the opposite of relational design where you normalize data (eliminate duplication) and join at query time. Wide-column stores cannot perform joins, so every anticipated query needs its own table with a carefully chosen partition key.
Example: a messaging app needs "get messages for channel" and "get user profile." Create messages_by_channel (partition key: channel_id) and users (partition key: user_id). Store user names denormalized in messages rather than joining at read time.
Partition Key Design
The partition key is the only efficient lookup path. Queries without partition key cause scatter-gather: the coordinator sends requests to every node in the cluster, waits for all responses, then merges results. With 100 nodes, p99 latency becomes the slowest node (often 10-100x worse than single-partition queries). Always include partition key in queries.
Design partitions to be bounded. Target 100-200MB compressed per partition. Time-series workloads bucket by time: partition key userId_hourBucket instead of just userId prevents unbounded growth from active users.
Hot Partition Problems
Hot partitions occur when poor key choices concentrate traffic. Monotonic keys (sequential IDs, timestamps as partition key) route all writes to one node. Celebrity accounts with millions of followers create read hotspots. Solutions include:
Salting: Add random prefix (0-9) to partition key. Distributes across 10 partitions but requires scatter-read to fetch all. Composite keys: Add time bucket to high-cardinality keys. Pre-sharding: Create multiple partitions before traffic spikes.