Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

What is Apache Cassandra?

Apache Cassandra is a distributed, wide-column NoSQL database designed for high availability and linear scalability. It handles massive amounts of data across commodity servers with no single point of failure, making it the database of choice for applications that cannot afford downtime.

This guide goes beyond surface-level descriptions to explain why Cassandra works the way it does, when it is the right choice, and what tradeoffs are involved.

Before understanding Cassandra, understand the problem it was built to solve.

Traditional relational databases scale vertically - when more capacity is needed, a bigger server is purchased:

Year 1: 1 server, 16GB RAM, 500GB disk → Works great
Year 2: 1 server, 64GB RAM, 2TB disk → Still okay
Year 3: 1 server, 256GB RAM, 10TB disk → Expensive, but works
Year 4: ??? Maximum server size reached → NOW WHAT?

At some point, a wall is hit. The biggest server money can buy is not enough. Data must be spread across multiple machines, but traditional databases were not designed for this.

Even with an infinitely large server, it would still be a single point of failure. When that server goes down - and it will - the entire application is offline.

Traditional solutions (primary-replica replication, clustering) help but introduce complexity and failure modes:

Primary-Replica Problems:
- Primary fails → manual failover required (downtime)
- Replication lag → stale reads from replicas
- Split brain → both think they're primary (data corruption)
- Write scalability → still limited to single primary

Global applications need data close to users. A user in Tokyo should not wait 200ms for every database query to reach a server in Virginia. But traditional databases were not built for multi-region deployment.

Cassandra was designed from the ground up to address these challenges:

Need more capacity? Add more nodes. The relationship is linear:

3 nodes → handles X requests/second
6 nodes → handles 2X requests/second
12 nodes → handles 4X requests/second
No architectural limit. Companies run 1000+ node clusters.

This works because:

  • Data is automatically distributed across nodes using consistent hashing
  • Each node is responsible for a portion of the data
  • Adding nodes automatically rebalances data

Every node in a Cassandra cluster is identical. There is no primary, no leader, no special node:

Primary-replica topology compared with the Cassandra peer-to-peer ringPrimary-replica topology compared with the Cassandra peer-to-peer ringTraditionalCassandraPrimary(SPOF)ReplicaReplicaNodeNodeNodeNode

Data is replicated to multiple nodes. When a node fails:

  • Other nodes have copies of its data
  • Requests are automatically routed to surviving nodes
  • When the node recovers, it automatically catches up

Cassandra was built for geographic distribution:

Users served by a local datacenter with replication between datacentersUsers served by a local datacenter with replication between datacentersUS-EASTEU-WESTNodeNodeNodeNodeNodeNodeUser(US)User(EU)Low latencyLow latencyReplication
  • Each region has local replicas for low latency
  • Cross-datacenter consistency is tunable per query
  • Each region can operate independently during network partitions

Cassandra's architecture makes more sense when understanding why it was built.

Facebook needed to store and search billions of messages. Requirements:

  • Handle 100+ million users
  • Millisecond search latency
  • Never go offline (users expect 24/7 availability)
  • Scale without rebuilding

Existing solutions didn't work:

  • MySQL could not scale writes
  • Oracle was too expensive
  • Existing NoSQL options (memcached) could not persist reliably

Facebook engineers Avinash Lakshman and Prashant Malik combined:

Amazon Dynamo (2007):

  • Consistent hashing for data distribution
  • Gossip protocol for cluster membership
  • Tunable consistency
  • Sloppy quorum for availability

Google BigTable (2006):

  • Wide-column data model
  • Log-structured storage (LSM trees)
  • Column families

The result was Cassandra - open-sourced in 2008, became Apache top-level project in 2010.

Understanding Cassandra's heritage explains its behavior:

Inherited FromFeatureImplication
DynamoEventual consistencyMust design for it
DynamoNo single point of failureTrue peer-to-peer
DynamoTunable consistencyApplication chooses
BigTableColumn-oriented storageEfficient for wide rows
BigTableLSM treesFast writes, slower reads

Understanding Cassandra requires understanding the CAP theorem.

The CAP theorem states that a distributed system can only guarantee two of three properties:

  • Consistency: Every read receives the most recent write
  • Availability: Every request receives a response
  • Partition tolerance: System continues despite network failures
CAP theorem categories with Cassandra as AP by defaultCAP theorem categories with Cassandra as AP by defaultCAP TheoremConsistencyAvailabilityPartition ToleranceCA(Traditional DBs)CP(ZooKeeper)AP(Cassandra default)Give up P(not viable fordistributed systems)Give up A(may refuse requestsduring partition)Give up C(may return stale dataduring partition)

In the real world, network partitions will happen. P cannot be given up. So the choice is between:

  • CP systems: When partition occurs, refuse requests (maintain consistency, sacrifice availability)
  • AP systems: When partition occurs, continue serving requests (maintain availability, sacrifice consistency)

Cassandra chose AP - it will always respond, even if the response might be slightly stale.

BUT: Cassandra allows tuning consistency per operation. Strong consistency is available when needed:

-- AP behavior: fast, available, might be stale
CONSISTENCY ONE;
SELECT * FROM users WHERE user_id = ?;
-- CP behavior: slower, might fail if nodes down, always consistent
CONSISTENCY ALL;
SELECT * FROM users WHERE user_id = ?;
-- The sweet spot for most applications
CONSISTENCY LOCAL_QUORUM;
SELECT * FROM users WHERE user_id = ?;

When data is written to Cassandra:

Cassandra write path through commit log, memtable, and replicasCoordinatorCommit LogMemtableReplicaClientCoordinatorCommit LogMemtableReplicaClientClientCoordinatorNodeCoordinatorNodeCommit LogCommit LogMemtableMemtableReplicaNodesReplicaNodesCoordinatorCommit LogMemtableReplicaINSERT INTO users...1. Write to Commit Log(sequential disk write)2. Write to Memtable(memory)3. Replicate to other nodes4. Return ACK

Why writes are fast:

  1. Commit log is append-only - Sequential disk writes are 100x faster than random
  2. Memtable is in memory - Microsecond latency
  3. No read-before-write - Unlike B-trees, no need to read existing data
  4. Async replication option - Can acknowledge before all replicas confirm

When data is read:

Cassandra read path through memtable, bloom filters, key cache, and SSTablesCoordinatorClientCoordinatorClientClientCoordinatorNodeCoordinatorNodeCoordinatorSELECT * FROM users...1. Check Memtable(in memory)alt[Not found in Memtable]2. Check Bloom Filtersalt[Might exist]3. Check Key Cachealt[Not cached]4. Read SSTablefrom disk5. Merge resultsReturn data

Why reads can be slower:

  1. Data might be spread across multiple SSTables
  2. Need to merge results from memtable + multiple SSTables
  3. Tombstones (deletes) must be processed
  4. May need to query multiple replicas

This is the fundamental tradeoff: Cassandra optimizes writes at the expense of reads.

Over time, writes create many SSTables. Compaction merges them:

Compaction merging SSTables containing multiple versions of a rowCompaction merging SSTables containing multiple versions of a rowBefore CompactionAfter CompactionSSTable 1user:1 = v1SSTable 2user:1 = v2SSTable 3user:1 = v3user:2 = v1Merged SSTableuser:1 = v3user:2 = v1Fewer files = faster readsCompaction

Compaction strategies (each optimized for different workloads):

StrategyBest ForCharacteristics
STCS (Size-Tiered)Write-heavyGroups similar-sized SSTables
LCS (Leveled)Read-heavyGuarantees bounded read amplification
TWCS (Time-Window)Time-seriesEfficient for TTL data
UCS (Unified)General (5.0+)Adaptive, combines benefits

1. High Throughput at Scale

Cassandra handles both high write AND read throughput when data is modeled correctly:

Write-heavy examples:
- IoT sensor data (millions of devices writing continuously)
- Clickstream/event tracking
- Log aggregation
- Time-series metrics
Read-heavy examples:
- User profile lookups (billions of reads/day)
- Product catalogs
- Session stores
- Recommendation serving
Mixed workloads:
- Messaging systems (write messages, read conversations)
- Social feeds (write posts, read timelines)

With proper data modeling and caching (key cache, row cache), Cassandra serves reads with single-digit millisecond latency at massive scale.

2. High Availability Requirements

If downtime costs money or reputation:

Use Case Examples:
- E-commerce (cannot miss orders)
- Financial transactions
- Healthcare systems
- Gaming (live services)
- Any 24/7 service

3. Geographic Distribution

If users are global and latency matters:

Use Case Examples:
- Social media platforms
- Content delivery
- Global SaaS applications
- Multi-region applications

4. Linear Scalability Needs

If data growth is unpredictable or potentially massive:

Use Case Examples:
- Startups expecting growth
- Platforms with viral potential
- Data-intensive applications
CompanyUse CaseScale
AppleiCloud, Apple Music, Siri150,000+ nodes, 10+ PB
NetflixStreaming, recommendations500+ nodes per cluster
InstagramDirect messages, feedsMillions of writes/second
UberDriver/rider matching, mapsBillions of requests/day
DiscordMessages, presenceBillions of messages, trillions of reads
SpotifyUser data, playlistsThousands of nodes

Cassandra is not a general-purpose database. It has specific tradeoffs that make it wrong for certain use cases—though several traditional limitations are being addressed in recent releases.

1. Complex Joins and Aggregations

-- CASSANDRA CANNOT DO THIS:
SELECT u.name, COUNT(o.id), SUM(o.total)
FROM users u
JOIN orders o ON u.id = o.user_id
GROUP BY u.name
HAVING SUM(o.total) > 1000;
-- Data must be denormalized or use Spark for analytics
-- Use: PostgreSQL, ClickHouse, Snowflake for analytical queries

This is a fundamental architectural choice—Cassandra optimizes for distributed writes, not relational queries.

2. Small Datasets Without Availability Requirements

If data fits on one server AND availability is not critical, simpler options exist:

Consider simpler alternatives when:
- Downtime during maintenance is acceptable
- Single-region deployment is sufficient
- Team lacks distributed systems expertise
If availability IS critical: Cassandra is appropriate regardless of dataset size

Many organizations run Cassandra for small datasets specifically because they cannot tolerate downtime.

3. Rapidly Evolving Query Patterns

If query patterns change frequently and unpredictably:

Cassandra data modeling principle:
- Design tables around queries (query-first design)
- Adding new query patterns may require new tables
- Schema changes at scale require careful planning
For exploratory/evolving queries: PostgreSQL, Elasticsearch

The following traditional limitations are being addressed in Cassandra 5.x:

Ad-hoc Queries (Improved in 5.0)

Storage Attached Indexes (SAI) in Cassandra 5.0 significantly improve secondary index performance:

-- BEFORE 5.0: Secondary indexes were expensive and limited
-- WITH SAI (5.0+): Much more practical for non-partition-key queries
CREATE INDEX ON users (email) USING 'sai';
SELECT * FROM users WHERE email = 'user@example.com';
-- SAI provides ~40% better throughput and ~230% better latency
-- than legacy secondary indexes

SAI does not make Cassandra equivalent to a relational database for ad-hoc queries, but it substantially expands what is practical.

Multi-Partition ACID Transactions (Coming in 5.1+)

The Accord protocol (CEP-15) brings general-purpose ACID transactions to Cassandra:

-- COMING WITH ACCORD (5.1+):
BEGIN TRANSACTION
UPDATE accounts SET balance = balance - 100 WHERE id = 'sender';
UPDATE accounts SET balance = balance + 100 WHERE id = 'receiver';
COMMIT;

Accord is a leaderless consensus protocol that enables:

  • Multi-partition transactions across any set of keys
  • Strict serializable isolation
  • Single wide-area round-trip for cross-region transactions
  • No single point of failure (unlike leader-based approaches)

Current Status

Accord is under active development. For applications requiring multi-partition transactions today, consider PostgreSQL, CockroachDB, or implement application-level saga patterns.

Strong Consistency Trade-offs

Cassandra has always supported strong consistency via QUORUM and LWT, but at the cost of availability during partitions. With Accord:

  • Strict serializability without sacrificing Cassandra's distributed architecture
  • Better performance than Paxos-based LWT for complex operations
  • Maintains Cassandra's peer-to-peer, no-single-point-of-failure design
AspectCassandraMongoDB
Data ModelWide-column (table-like)Document (JSON)
SchemaSchema-per-tableSchema-per-collection (flexible)
ScalabilityPeer-to-peer, linearSharded, with config servers
ConsistencyTunable (default: eventual)Strong for single doc
JoinsNone$lookup (limited)
Secondary IndexesExpensiveNative support
Write PerformanceExcellentGood
Read PerformanceGood (with right model)Excellent
OperationsComplexEasier
Best ForScale, availability, writesFlexibility, documents

Choose Cassandra when: Write-heavy, need linear scale, multi-DC required Choose MongoDB when: Flexible schema, document-oriented, smaller scale

AspectCassandraPostgreSQL
TypeDistributed NoSQLRelational
ScalingHorizontal (add nodes)Vertical (bigger server)
ConsistencyTunableStrong ACID
TransactionsSingle-partition LWTFull ACID
SchemaMust design for queriesNormalized design
JoinsNot supportedFull support
IndexesLimitedRich (B-tree, GIN, GiST)
Query FlexibilityLimitedFull SQL
AvailabilityNative HARequires setup
Best ForScale, availabilityComplex queries, transactions

Choose Cassandra when: Scaling beyond single server, HA is critical Choose PostgreSQL when: Complex queries, transactions, < 100GB data

AspectCassandraDynamoDB
DeploymentSelf-managed or DBaaSAWS-managed
VendorOpen sourceAWS only
PricingInfrastructurePer-request or capacity
Multi-RegionBuilt-inGlobal Tables ($$$)
Query LanguageCQLPartiQL / API
FlexibilityFull controlLimited configuration
Expertise RequiredHighLower
Cost at ScaleLowerHigher

Choose Cassandra when: Vendor independence, multi-cloud, cost control at scale Choose DynamoDB when: AWS-only, minimal ops, getting started quickly

AspectCassandraScyllaDB
LanguageJavaC++
PerformanceGood (gap narrowing with 5.0+)Faster (historically 3-10x, now closer)
Resource UsageImproved with modern GCsMore efficient
CompatibilityOriginalCQL compatible
CommunityLarge, matureGrowing
FeaturesAll features (Accord, SAI, Vector)Most features
SupportApache, vendorsScyllaDB Inc
JDK OptionsJDK 17+ with Shenandoah/ZGCN/A

The performance gap has narrowed significantly:

  • Cassandra 5.0 includes substantial performance improvements
  • JDK 17+ with Shenandoah or ZGC dramatically reduces GC pause times
  • Modern Cassandra deployments see much smaller performance differences than historical benchmarks suggest

Choose Cassandra when: Need latest features (Accord, SAI), larger ecosystem, JVM expertise Choose ScyllaDB when: Maximum single-node throughput, want to avoid JVM tuning

TermDefinitionAnalogy
ClusterAll nodes working togetherThe entire database
NodeSingle Cassandra instanceOne server
Datacenter (DC)Logical grouping of nodesUsually maps to physical DC or region
RackSubdivision of DCFor failure isolation
TokenHash value identifying data ownershipNode's "address" on the ring
Token RangeRange of tokens a node ownsNode's "slice" of data
Seed NodeBootstrap node for gossipHow new nodes find the cluster
TermDefinitionSQL Equivalent
KeyspaceContainer for tablesDatabase
TableCollection of partitionsTable
PartitionUnit of distributionNo equivalent
Partition KeyDetermines data locationPart of PRIMARY KEY
Clustering ColumnSorts within partitionORDER BY column
RowSingle recordRow
CellSingle column valueCell
TTLAuto-expire dataNo equivalent
TombstoneMarker for deleted dataNo equivalent
TermDefinition
Replication Factor (RF)Copies of data stored
Consistency Level (CL)Replicas required to respond
QuorumMajority of replicas ((RF/2) + 1)
CompactionMerging SSTables
RepairSynchronizing replicas
GossipNode-to-node state sharing
Hinted HandoffStore-and-forward for down nodes
Read RepairFix inconsistencies on read
Anti-entropy RepairFull replica synchronization

After understanding what Cassandra is and when to use it:

1. Install Cassandra → Get it running
└── installation/index.md
2. First Cluster → Multi-node setup
└── first-cluster.md
3. CQL Basics → Write queries
└── quickstart-cql.md
4. Data Modeling → Design schemas
└── ../data-modeling/index.md
5. Architecture Deep Dive → Understand internals
└── ../architecture/index.md
Terminal window
# Start Cassandra with Docker
docker run --name cassandra -d -p 9042:9042 cassandra:5.0
# Wait for startup
sleep 60
# Connect
docker exec -it cassandra cqlsh
# Create and query data
CREATE KEYSPACE test WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};
USE test;
CREATE TABLE users (id uuid PRIMARY KEY, name text);
INSERT INTO users (id, name) VALUES (uuid(), 'Test User');
SELECT * FROM users;

Next: Install Cassandra - Get Cassandra running