CQL Practice in Cassandra
Roll up your sleeves and start coding! CQL practice helps you learn how to create tables, insert data, and run powerful queries — like learning the language your database speaks.
📖 The Story: Tom's Production Nightmare
Tom launched his social media app with 1 million users. Everything worked in development. But on launch day, the entire system CRASHED within 2 hours. Here's what went wrong...
❌ The Disasters: What Tom Did Wrong
Mistake #1: No Partition Key Strategy
Mistake #2: SELECT * Everywhere
Mistake #3: ALLOW FILTERING on Everything
The Result:
- 💥 System crashes every 2 hours (OOM errors)
- ⏱️ Query timeouts everywhere (30-90 seconds)
- 🔥 Hot partitions cause node failures
- 📉 App rating drops from 4.8 to 1.2 stars
- 💸 AWS bill skyrockets to $50,000/month
✅ The Fix: Following Best Practices!
Fix #1: Proper Partition Key Design
Fix #2: Always Specify Partition Key
Fix #3: Query-Driven Design
The Results:
- ✅ Zero crashes: 99.99% uptime for 6 months!
- ✅ Blazing fast: All queries < 10ms
- ✅ Scalable: Handles 10M users easily
- ✅ Happy users: Rating back to 4.9 stars!
- ✅ Cost savings: AWS bill down to $8,000/month
Tom learned: Following best practices = Production success! 🎉
🔍 Query Best Practices
Write fast, efficient queries that scale to billions of rows!
DO's
- Always Use Partition Key: WHERE pk = value
- Use LIMIT: Prevent massive result sets
- Select Specific Columns: SELECT a, b, c (not *)
- Use Prepared Statements: Better performance + security
- Batch Wisely: Only same partition, max 100 statements
- Use Paging: fetchSize for large results
- TTL for Temporary Data: Auto-cleanup
DON'Ts
- Never SELECT *: Without partition key
- Avoid ALLOW FILTERING: Full table scan!
- Don't Use IN Heavily: Max 10-20 values
- No Cross-Partition Batches: Kills performance
- Don't Query All Partitions: Use token ranges if needed
- Avoid Large Collections: Max 100-1000 elements
- No Range Scans on PK: Use clustering columns
Pro Tips
- Use Token Function: For parallel scans
- TRACING ON: See query execution path
- Async Queries: Better throughput
- Retry Logic: Handle timeouts gracefully
- Consistency Tuning: ONE/LOCAL_ONE for reads
- Lightweight Transactions: Use sparingly (Paxos)
- Monitor Slow Queries: Log queries > 100ms
Query Examples: Good vs Bad
🎨 Data Modeling Best Practices
The foundation of performance: Model your data right from day one!
1. Query-Driven Design (Not Entity-Driven!)
Design tables around queries, not entities. One table per query pattern!
2. Keep Partitions Small (< 100MB)
Large partitions cause hot spots, slow reads, and memory issues!
Partition Size Limits
- Soft Limit: 100MB per partition
- Row Limit: 100,000 rows recommended
- Hard Limit: 2GB (but you'll have problems before this!)
3. Choose the Right Partition Key
The partition key determines data distribution - choose wisely!
4. Use Clustering Columns for Sorting
Clustering columns provide automatic sorting - use them!
5. Denormalize for Performance
Duplicate data across tables - storage is cheap, JOINs are impossible!
🏗️ Schema Design Best Practices
Design schemas that are maintainable, scalable, and performant!
1. Use Meaningful Naming Conventions
2. Choose Appropriate Data Types
3. Use TTL for Temporary Data
4. Set Compaction Strategy Wisely
5. Configure Table Properties
✍️ Write & Read Best Practices
Optimize your writes and reads for maximum performance!
Write Best Practices
- Use Prepared Statements: 10x faster + prevents injection
- Batch Same Partition: Atomic updates to denormalized data
- Async Writes: Better throughput for bulk operations
- Set Consistency Wisely: LOCAL_ONE for writes (fast!)
- Use Lightweight Transactions Sparingly: 4x slower (Paxos)
- Avoid Hot Partitions: Distribute writes evenly
- Use TTL: Auto-expire temporary data
Read Best Practices
- Always Use PK: Single-partition reads are fastest
- Use LIMIT: Prevent accidentally reading millions of rows
- Select Specific Columns: Don't SELECT * unnecessarily
- Use Paging: fetchSize for large result sets
- Consistency ONE/LOCAL_ONE: Fastest reads
- Cache Hot Data: Application-level or row cache
- Avoid ALLOW FILTERING: Full table scan = slow
Write Examples: Best Practices
Read Examples: Best Practices
⚡ Performance Optimization Best Practices
Tune your cluster and queries for maximum performance!
1. Choose Right Consistency Level
Consistency Level Guide
- LOCAL_ONE (Fastest): Reads/writes to closest node. Use for non-critical data, caching.
- LOCAL_QUORUM (Recommended): Majority in local datacenter. Best balance of performance and consistency.
- QUORUM (Strong Consistency): Majority across all datacenters. Slower but ensures consistency.
- ALL (Slowest): All replicas. Use only for critical data. Fails if any node is down!
2. Monitor and Tune Compaction
3. Use Caching Strategically
4. Optimize Compression
5. Monitor Query Performance
Performance Checklist
- ✅ Partitions < 100MB each
- ✅ Queries always use partition key
- ✅ No ALLOW FILTERING in production
- ✅ Consistency level: LOCAL_QUORUM or LOCAL_ONE
- ✅ Prepared statements for all queries
- ✅ Compaction strategy matches workload
- ✅ Caching enabled for hot data
- ✅ Slow query monitoring enabled
- ✅ Regular nodetool repair scheduled
- ✅ Replication factor ≥ 3
🚫 Anti-Patterns to Avoid
Learn from common mistakes that break production systems!
❌ Anti-Pattern #1: The "God Partition"
Storing unbounded data in a single partition.
❌ Anti-Pattern #2: Using Collections Like Arrays
Storing thousands of items in a single collection.
❌ Anti-Pattern #3: Using Cassandra Like SQL
Trying to JOIN, GROUP BY, or aggregate in CQL.
❌ Anti-Pattern #4: Reading Before Writing
Checking if data exists before inserting (unnecessary in Cassandra).
❌ Anti-Pattern #5: Logged Batches for Performance
Using BATCH for bulk inserts (actually slower!).
🖥️ Interactive Best Practices Console
Test CQL best practices in our simulator!
Enter a CQL query and I'll analyze it for best practices...
Available Examples:
• Good Example: Optimized query
• Bad Example: Anti-pattern query
• Anti-Pattern: Common mistakes
💼 Interview Questions & Expert Answers
Master CQL best practices for your next interview!
Answer: Query-driven design, partition key management, and avoiding ALLOW FILTERING.
1. Query-Driven Data Modeling
Design tables around queries, not entities. One table per query pattern.
- Why: Cassandra is optimized for fast reads when you know the partition key
- Example: Need to query users by email? Create users_by_email table
- Trade-off: Data duplication (acceptable - storage is cheap!)
2. Keep Partitions Small (< 100MB)
Use bucketing strategies to prevent unbounded partition growth.
- Why: Large partitions cause hot spots, slow reads, memory issues
- Example: Bucket time-series data by date: (sensor_id, date_bucket)
- Rule: Max 100,000 rows or 100MB per partition
3. Never Use ALLOW FILTERING in Production
ALLOW FILTERING scans the entire table - creates massive performance problems.
- Why: Coordinator reads from ALL nodes, filters millions of rows
- Solution: Create denormalized table or use secondary index (carefully)
- Exception: OK for small tables in dev/testing only
Answer: Use BATCH only for maintaining consistency across denormalized tables in the same partition. Avoid for bulk inserts.
✅ GOOD Use Cases for BATCH:
- Denormalized Data Consistency: Updating the same data in multiple tables atomically
- Same Partition Writes: Multiple updates to same partition
- Transactional Semantics: All-or-nothing writes for related data
❌ BAD Use Cases for BATCH:
- Bulk Inserts: Actually SLOWER than individual async inserts!
- Cross-Partition Batches: Coordinator becomes bottleneck
- Performance Optimization: BATCH is NOT for speed
Key Principle: BATCH is for atomicity, NOT performance. For bulk operations, use async parallel writes instead.
Answer: Denormalization duplicates data in multiple tables for different query patterns. Secondary indexes allow querying non-PK columns but have performance limitations.
Denormalization:
- What: Create separate tables for each query pattern
- Pros: Fastest reads, predictable performance, scales infinitely
- Cons: Data duplication, write complexity, storage overhead
- When: Known query patterns, write-heavy workloads, best performance needed
Secondary Indexes:
- What: Index on non-PK column, allows WHERE on that column
- Pros: Simple to create, flexible for ad-hoc queries, no data duplication
- Cons: Scatter-gather query (contacts ALL nodes), slower than denormalization
- When: High cardinality, read-heavy, unpredictable queries, selective results
Decision Matrix:
| Factor | Denormalization | Secondary Index |
|---|---|---|
| Performance | ⚡ Fastest | 🐌 Slower |
| Cardinality | Any | High only |
| Storage | More (duplicated) | Less |
| Write Complexity | Higher | Lower |
| Best For | Known queries | Ad-hoc queries |
Answer: Cardinality is the number of unique values. High cardinality ensures even data distribution; low cardinality causes hot partitions.
What is Cardinality?
The number of distinct values a column can have.
- High Cardinality: Millions of unique values (user_id, email, UUID)
- Low Cardinality: Few unique values (gender, status, country)
Why It Matters:
Cassandra distributes data across nodes based on partition key hash. Low cardinality = uneven distribution!
Cardinality Examples:
- ✅ High (Good): user_id, email, UUID, timestamp, product_sku
- ⚠️ Medium: country (~200), zip_code (~40K in US), date
- ❌ Low (Bad): gender (2-3), boolean (2), status (3-5)
Golden Rule: Partition key should have thousands or millions of unique values for even distribution.
Answer: Choose consistency levels based on your application's availability vs consistency requirements. Most production systems use LOCAL_QUORUM for writes and LOCAL_ONE or LOCAL_QUORUM for reads.
Common Consistency Levels:
1. LOCAL_QUORUM (Recommended for Writes)
- What: Majority of replicas in local datacenter must acknowledge
- Formula: (replication_factor / 2) + 1
- Example: RF=3 → requires 2 nodes
- Use: Best balance of consistency and availability
2. LOCAL_ONE (Fastest Reads)
- What: Only one replica in local datacenter responds
- Speed: Fastest possible reads
- Use: Non-critical data, caching, session data
- Trade-off: May read stale data (eventual consistency)
3. QUORUM (Cross-DC Consistency)
- What: Majority across ALL datacenters
- Use: Critical data requiring strong consistency
- Trade-off: Slower (cross-datacenter latency)
Production Recommendations:
| Use Case | Write CL | Read CL |
|---|---|---|
| Session data, caching | LOCAL_ONE | LOCAL_ONE |
| General application (default) | LOCAL_QUORUM | LOCAL_QUORUM |
| Financial transactions | QUORUM | QUORUM |
| Analytics (read-heavy) | LOCAL_QUORUM | LOCAL_ONE |
Golden Rule: Write at LOCAL_QUORUM, read at LOCAL_ONE for best performance with reasonable consistency. Increase for critical data.
🎓 Chapter Summary: CQL Best Practices Mastery
Congratulations! You now know how to write production-grade CQL!
The 10 Commandments of CQL:
- Query-Driven Design: Model tables around queries, not entities
- Keep Partitions Small: < 100MB, use bucketing strategies
- Always Use Partition Key: No queries without WHERE partition_key = ?
- Never ALLOW FILTERING: Create proper tables or indexes instead
- High Cardinality Keys: Millions of unique values for even distribution
- Denormalize Fearlessly: Duplicate data for query patterns
- Use LIMIT Always: Prevent accidentally reading millions of rows
- BATCH for Atomicity: Not for performance! Use async for bulk
- Prepared Statements: 10x faster + prevents SQL injection
- LOCAL_QUORUM Default: Best consistency/performance balance
Critical Anti-Patterns to Avoid:
- ❌ The "God Partition" - unbounded partitions
- ❌ Using collections like arrays (100K+ items)
- ❌ Trying to use Cassandra like SQL (JOINs, aggregations)
- ❌ Read-before-write patterns (unnecessary!)
- ❌ BATCH for bulk inserts (actually slower!)
- ❌ SELECT * without partition key
- ❌ Low cardinality partition keys
Performance Checklist:
- ✅ All queries use partition key
- ✅ Partitions < 100MB / 100K rows
- ✅ Prepared statements for all queries
- ✅ Consistency level: LOCAL_QUORUM or LOCAL_ONE
- ✅ TTL for temporary data
- ✅ Proper compaction strategy per table
- ✅ No ALLOW FILTERING in production
- ✅ LIMIT on all queries
- ✅ High cardinality partition keys
- ✅ Monitoring and slow query logging enabled
🚀 You're now ready to build production-grade Cassandra applications!
Remember Tom's story: Following these best practices = Happy users, 99.99% uptime, and low AWS bills! 🎉
Responsive Ad