LearnThatStack Ace your next interview

Cassandra

CQL and nodetool for Cassandra 5.0 Checked Oct 2026 66 commands

The model

  • Cassandra hashes the partition key into a token. Nodes own ranges of tokens on a ring, so the token picks a row's nodes.
  • Every node is equal, so any node can be the coordinator for a request. Nodes share cluster state through gossip.
  • The replication factor (RF) sets how many copies exist. The consistency level sets how many replicas must answer. When W + R > RF, every read shares a replica with the latest write.
  • Writes go to the commit log and an in-memory memtable. The memtable flushes to immutable SSTables, which compaction merges.
  • A delete writes a tombstone. Run repair within gc_grace_seconds, 10 days by default, or deleted data can come back.
ClientdriverCoordinatorany nodeReplicas A, BupReplica Cdown for a whileWRITEINSERT QUORUMwritehint replayed later2 acksOKREADQUORUM read latestONE read may be stale
With RF 3, QUORUM is 2. Any 2 replicas a read picks include one with the write. A ONE read can reach C before the hint does.

Ring & nodes

how equal nodes share the data

What Cassandra is

Say what Cassandra isA distributed wide-column database. Every node is equal, and none is the master.
Say how it differs from a relational databaseIt scales out by adding nodes. It has no joins or foreign keys, and no ACID across partitions.
Choose Cassandra over SQLFor huge, write-heavy data and queries you know in advance. SQL suits joins and ad hoc queries.

TrapDon't call Cassandra schemaless. Every CQL table has typed columns, but adding a column is cheap.

The token ring

Say how Cassandra picks a row's nodesIt hashes the partition key into a token. The nodes that own that token's range store the row.
Find the nodes that hold one keynodetool getendpoints app users 42
Give each node many small token rangesnum_tokens: 16Virtual nodes (vnodes). 16 is the default since 4.0, down from 256.

TipThe default partitioner is Murmur3Partitioner. Hashing keys onto a ring of nodes is called consistent hashing.

TrapMore tokens per node have a cost. More combinations of failed nodes can take part of the ring offline, and there are more repairs to run.

Coordinator and gossip

Say what the coordinator doesIt forwards a client's request to the replicas, and replies once enough of them answer.
Check which nodes are upnodetool statusUN means up and normal, and DN means down.
Say how nodes share cluster stateGossip. Every second, each node swaps state with a random peer and decides for itself who is down.

TrapSeed nodes only help new nodes join. They aren't masters, and any node can coordinate any request.

Replication & consistency

how many copies, and how many must answer

Replication strategies

Say what the replication factor setsHow many copies of each row exist. With RF 3, three nodes hold each row.
Keep 3 copies in dc1 and 2 in dc2{'class': 'NetworkTopologyStrategy', 'dc1': 3, 'dc2': 2}Set it in the keyspace's replication option.
Replicate a test cluster{'class': 'SimpleStrategy', 'replication_factor': 3}It ignores datacenters and racks.
Tell Cassandra each node's datacenter and rackChoose a snitch with endpoint_snitch. Production clusters use GossipingPropertyFileSnitch.

TipUse NetworkTopologyStrategy in production, even with one datacenter. It puts each datacenter's replicas on different racks.

Consistency levels, RF 3 in two DCs

CONSISTENCY ONEGet the fastest reads and writes
CONSISTENCY LOCAL_QUORUMRead your writes within one datacenter
CONSISTENCY QUORUMRead your writes across the cluster
CONSISTENCY ALLWait for every replica
yesno

TipLatest means a read sees the last write when reads and writes use that level. For LOCAL_QUORUM, that holds only within one DC.

CAP and eventual consistency

Place Cassandra in the CAP theoremAt ONE, it's AP. It keeps answering during a network partition, but reads may be stale.
Say what QUORUM does in a network partitionReads and writes see the latest write, but fail on the side without a majority.
Say what an eventually consistent read isIt may return an older value until every replica has the write. When replicas disagree, the newest timestamp wins.
Make every read see the latest writeWait for W write and R read replicas, where W + R > RF. With RF 3, QUORUM is 2, and 2 + 2 > 3.

Hinted handoff and read repair

Catch up a replica that was downmax_hint_window: 3hHinted handoff. The coordinator stores missed writes as hints, for up to 3 hours of downtime.
Accept a write while every replica is downCONSISTENCY ANYFor writes only. A stored hint counts, so the write is lost if the coordinator fails first.
Fix stale replicas during a readRead repair. The coordinator writes the newest value to stale replicas before it answers.

TipA node down longer than max_hint_window gets no more hints. It stays out of sync until you run repair.

TrapAt ONE, only one replica answers, so read repair never runs.

Keys & data modeling

design each table around one query

Partition key and clustering key

Say what the partition key doesCassandra hashes it to pick the nodes. Rows that share it form one partition.
Say what the clustering key doesIt sorts the rows inside a partition, so a query can read a range in order.
Pick a good partition keyChoose one with many distinct values, so data spreads evenly and partitions stay small.
Build a partition key from two columnsPRIMARY KEY ((sensor_id, day), ts)The inner parentheses hold the partition key.
Store the newest rows firstWITH CLUSTERING ORDER BY (ts DESC)

TrapYou set the primary key and the clustering order when you create a table. Changing either means a new table and a data copy.

Design from the queries

Start a data modelList the app's queries first. Then design one table for each.
Replace a joinDenormalize. Copy the data into every table whose query needs it.
Enforce a foreign keyYou can't. Cassandra has no referential integrity, so the app keeps tables consistent.
Name the common modeling mistakesUnbounded partitions, indexes on high-cardinality columns, and SQL-style normalization.

TrapDon't start from the entities. A table built around an entity rarely answers the queries the app runs.

One-to-many relationships

Store a user's ordersPRIMARY KEY (user_id, order_date, order_id)One partition per user, and one row per order, sorted by date.
Store the user's name once per partitionname text STATICA static column.
Keep a short list in the parent roworder_ids set<uuid>A collection. Use one only when the list stays small.

TipSeparate tables also work. The app joins them with two queries, which costs two round trips.

TrapCassandra reads a whole collection at once. A list that keeps growing belongs in its own table.

Large partitions and time series

Name the partition size limitsKeep partitions under 100MB. The hard limit is 2 billion cells.
Find a table's large partitionsnodetool tablestats app.events
Split a partition that grows foreverAdd a time bucket, such as a day, to the partition key.
Expire old readings without deletesUse a TTL with Time Window Compaction (TWCS). It drops whole expired SSTables.

TrapDon't make the date alone the partition key. All of that day's writes then land on the same few nodes.

Tables, types & TTL

schema changes, column types and expiry

Tables and schema changes

Add a column with no downtimeALTER TABLE users ADD phone text;
Drop a columnALTER TABLE users DROP phone;Reads hide it at once. Compaction removes the data later.
Delete every row in a tableTRUNCATE users;Every node must be up.
Drop a table or a keyspaceDROP TABLE users;DROP KEYSPACE app;
Check that every node has the same schemanodetool describecluster

TrapYou can't change a column's type or rename a non-key column. Add a new column instead.

Data types

Name the common column typestext, int, bigint, double, boolean, timestamp, uuid, timeuuid and blob.
Store several values in one columnset for sorted unique values, list for ordered ones, and map for key-value pairs.
Group named fields into one column typeCREATE TYPE address (street text, city text);A user-defined type (UDT).

TipInside a collection, a UDT must be frozen and written whole. A non-frozen UDT column can change one field.

Time to live (TTL)

Make a value expire in one dayUSING TTL 86400Add it to an INSERT or UPDATE. The TTL is in seconds.
Give every new row in a table a TTLWITH default_time_to_live = 86400By default, values never expire.

TipExpired values turn into tombstones. Compaction removes them after gc_grace_seconds.

TrapA TTL belongs to each value you write. Updating a column resets its TTL.

Counters

Add 1 to a page's view countSET views = views + 1views is a counter column. You can only add or subtract.
Say what else a counter table can holdOnly its primary key. A counter can't be part of the key.
Retry a counter update that timed outNot safely. Counters aren't idempotent, so a retry can count twice.

TipEach counter update reads the current value first, so it costs more than a normal write.

TrapCounters can't have a TTL. Don't update a counter again after you delete it.

Queries & indexes

what CQL reads fast, and what it refuses

Query rules and ALLOW FILTERING

Read one partitionSELECT * FROM events WHERE user_id = ?;
Read a time range in that partitionWHERE user_id = ? AND event_time > ?
Filter on clustering columnsIn the order you declared them. You can't skip one to filter the next.
Filter on a column outside the keyALLOW FILTERINGCassandra may read every partition, then drop the rows that don't match.

TipUse ALLOW FILTERING only on small data, like one partition. Otherwise add a table keyed for the query, or an SAI index.

TrapAn ALLOW FILTERING query slows as the table grows, even when it returns few rows.

Indexes and views

Index a column (5.0+)CREATE INDEX ON users (country) USING 'sai';A storage-attached index (SAI). It also handles numeric ranges.
Index a column before 5.0CREATE INDEX ON users (country);A legacy secondary index. Each node indexes only its own data.

TipMaterialized views are experimental and off by default. For a busy query, keep a second table keyed for it.

TrapOn 5.0, a plain CREATE INDEX still builds the legacy index. Add USING 'sai'.

Pagination

Page through a large resultLet the driver page. Pass back its paging state to get the next page.
Jump straight to page 5You can't. CQL has no OFFSET, and a paging state only moves forward.
Page through a whole table yourselfWHERE token(user_id) > ? LIMIT 100

TrapA paging state works only with the query that returned it.

Deletes, batches & LWTs

tombstones, atomic writes, compare and set

Deletes and tombstones

Delete a rowDELETE FROM users WHERE id = ?;Cassandra writes a tombstone, a delete marker with a timestamp.
Set how long tombstones stayWITH gc_grace_seconds = 86400010 days is the default. After that, compaction can remove them.
Say when tombstones slow or break a readEvery read scans past them. A read warns at 1,000 tombstones and fails at 100,000, by default.

TrapRepair within gc_grace_seconds. Otherwise a replica that missed the delete can bring the data back.

Batches

Write to two tables so every write landsBEGIN BATCHAPPLY BATCH;A logged batch, the default. Its batch log makes every write land eventually.
Skip the batch logBEGIN UNLOGGED BATCHA failed batch may then apply only partly.
Group writes to one partitionIt's atomic and isolated, and Cassandra skips the batch log.

TrapA batch has no rollback and no isolation across partitions. Other clients can see part of it.

Lightweight transactions

Insert a row that may already existINSERT INTO users (id, email) VALUES (?, ?);It silently overwrites the old values. INSERT and UPDATE are both upserts.
Insert only if the row is newIF NOT EXISTSAn LWT. The result's [applied] column says if it ran.
Update only if a value is unchangedIF email = 'old@example.com'Compare and set. The replicas agree on it through Paxos.
Say what an LWT costs4 round trips with Paxos v1, the default. Paxos v2 (4.1+) needs 2.

TipLWTs are linearizable, so every client sees them in one order. Use them for unique usernames or stock counts.

TrapAn LWT covers one partition. Cassandra 6.0, in alpha as of October 2026, adds Accord for transactions across partitions.

Write & read path

what happens inside one node

The write path

writememtableSSTablecommit log
one replicaA write goes to the commit log and the memtable. A full memtable flushes to an SSTable.
Say what a replica does with a writeIt appends the write to the commit log and adds it to the memtable. Nothing is read first.
Say what a crashed node can loseIts last few seconds of writes, since the commit log syncs every 10 seconds. Other replicas still have them.

TipWrites are fast because no file changes in place. SSTables never change, and compaction merges them later.

The read path

Say how a replica reads a partitionIt merges the memtable with each SSTable that may hold it. The newest timestamp wins.
Skip SSTables that can't hold the partitionUse each SSTable's bloom filter. Its no is always right, and its yes means maybe.
Use more memory for fewer false positivesWITH bloom_filter_fp_chance = 0.001The default is 0.01, or 0.1 with LCS.
Count the SSTables each read touchesnodetool tablehistograms app users

TipCassandra 5.0 adds the BTI SSTable format. Its trie index replaces the key cache and index lookups.

Compaction

Merge SSTables of similar size{'class': 'SizeTieredCompactionStrategy'}STCS, still the default. It suits heavy writes.
Keep each read to few SSTables{'class': 'LeveledCompactionStrategy'}LCS. It suits heavy reads.
Group SSTables by time window{'class': 'TimeWindowCompactionStrategy'}TWCS. It suits time series that expire with a TTL.
Pick a strategy for a new table (5.0+){'class': 'UnifiedCompactionStrategy'}UCS. It can act like any of the others.

TipCompaction keeps the newest version of each row and drops tombstones past their grace period.

TrapA growing backlog of pending compactions means each read touches more SSTables.

Repair, nodes & backups

keep replicas in sync and change the cluster

Repair

Repair each token range oncenodetool repair -prRun it on every node.
Say what plain nodetool repair runsAn incremental repair. It checks only data written since the last one.
Check all the data, repaired or notnodetool repair --full
Say how often to repairEvery node, at least every 7 days. Automatic repair comes in 6.0, in alpha as of October 2026.

TipEach replica builds a Merkle tree, a tree of hashes. Replicas stream only the ranges whose hashes differ.

TrapDon't rely on hints and read repair. They're best effort, and only nodetool repair compares all the data.

Add, remove or replace a node

Add a nodeIt bootstraps, streaming its token ranges before it serves requests.
Drop data a node no longer ownsnodetool cleanupRun it on the old nodes after a node joins.
Remove a live nodenodetool decommissionIt streams its data to the new owners first.
Remove a dead nodenodetool removenode <host-id>The other replicas stream its data instead.
Replace a dead node with a new machine-Dcassandra.replace_address_first_boot=<ip>It takes over the same token ranges.

TipIf the dead node was down longer than max_hint_window, run repair after you replace it.

Back up and restore

Snapshot a keyspacenodetool snapshot -t nightly appIt hard-links the SSTables, so it's fast. Run it on every node.
Also keep each newly flushed SSTablenodetool enablebackupIncremental backups. They're off by default.
Restore on the same nodenodetool import app users /backup/usersIt loads SSTables from any folder. It replaced nodetool refresh, deprecated in 4.0.
Restore into a cluster with another layoutsstableloader -d 10.0.0.1 app/users
Delete a snapshotnodetool clearsnapshot -t nightly

TipTRUNCATE and DROP TABLE take a snapshot first by default, so you can still restore the data.

TrapA snapshot sits on the same disk as the data. Copy it off the node, or one failed disk loses both.

Monitor & tune

spot trouble early and fix slow nodes

Monitor cluster health

Find thread pools that are backed upnodetool tpstatsPending or blocked tasks show the overloaded stage.
Measure read and write latencynodetool proxyhistogramsIt shows latency at the coordinator.
Read metrics with CQL (4.0+)SELECT * FROM system_views.thread_pools;

TipCassandra exposes metrics on each node through JMX and virtual tables. Collect them with a tool like Prometheus and Grafana.

TrapDataStax OpsCenter works only with DataStax Enterprise. It can't monitor open-source Cassandra.

Troubleshoot slow requests

See where one query spends its timeTRACING ONRun it in cqlsh, then run the query.
Check for long garbage collection pausesCassandra logs them. Long pauses slow every request on that node.
Record live queries to replay later (4.0+)nodetool enablefullquerylog --path /var/lib/fql

TipThe fix is often in the data model. Split large partitions, drop ALLOW FILTERING, and use no higher consistency than you need.

Tune for heavy writes

Pick compaction for heavy writesOn 5.0, UCS with a tiered setting. Otherwise STCS, the default.
Flush memtables less oftenmemtable_heap_space: 2048MiBBigger memtables use more heap.
Set how many writes run at onceconcurrent_writes: 128About 8 per CPU core.
Speed up the commit log on spinning disksGive it its own disk, apart from the data.

TipWrite at the lowest consistency level the app accepts. ONE writes fastest.

TrapEach speedup has a cost. Tiered compaction slows reads, and a lower consistency level weakens guarantees.

Found this useful? Pass it on.
Pro · $10/mo

This sheet stays free. With Pro, you can:

  • Every answer on this sheet. Read the full answer to all 41 questions it links, not just the 5 free ones.
  • My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
  • PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
  • The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.