Cassandra Cheat Sheet
CQL syntax, data modeling concepts, consistency levels, and TTL usage for designing wide-column, horizontally scalable Cassandra tables.
cqlsh Basics
Connecting and exploring keyspaces.
cqlsh localhost 9042DESCRIBE KEYSPACES;USE mykeyspace;DESCRIBE TABLE users;CREATE KEYSPACE mykeyspaceWITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};
CQL DDL & DML
Defining tables and reading/writing rows.
CREATE TABLE users ( id UUID PRIMARY KEY, email TEXT, created_at TIMESTAMP);INSERT INTO users (id, email, created_at)VALUES (uuid(), '[email protected]', toTimestamp(now()));SELECT * FROM usersWHERE id = 123e4567-e89b-12d3-a456-426614174000;UPDATE users SET email = '[email protected]'WHERE id = 123e4567-e89b-12d3-a456-426614174000;
Data Modeling Concepts
Key ideas behind Cassandra's distributed model.
- Partition key- determines which node(s) store the row; drives data distribution
- Clustering key- sorts rows within a partition on disk
- Wide row- a table with many clustering columns, common for time-series data
- Denormalization- duplicating data per query pattern since Cassandra has no JOINs
- Consistency level- ONE, QUORUM, ALL — trades off latency against read/write consistency
- Compaction strategy- SizeTieredCompactionStrategy vs LeveledCompactionStrategy for SSTable merging
Consistency & TTL
Session consistency and automatic row expiry.
-- Set consistency level for the cqlsh sessionCONSISTENCY QUORUM;-- Row automatically expires after 3600 secondsINSERT INTO users (id, email) VALUES (uuid(), '[email protected]') USING TTL 3600;-- Check remaining TTL on a columnSELECT TTL(email) FROM usersWHERE id = 123e4567-e89b-12d3-a456-426614174000;
Lightweight Transactions (CAS)
Compare-and-set operations using Paxos for linearizable consistency on a single partition.
-- INSERT only if the row doesn't already existINSERT INTO users (id, email, created_at)VALUES (123e4567-e89b-12d3-a456-426614174000, '[email protected]', toTimestamp(now()))IF NOT EXISTS;-- Conditional UPDATE (optimistic locking on a version column)UPDATE users SET email = '[email protected]', version = 2WHERE id = 123e4567-e89b-12d3-a456-426614174000IF version = 1;-- Conditional DELETEDELETE FROM usersWHERE id = 123e4567-e89b-12d3-a456-426614174000IF email = '[email protected]';-- [applied] column in the result tells you whether the CAS succeeded
Collections & User-Defined Types
Modeling nested and repeated data without a JOIN, using maps, sets, lists, and UDTs.
CREATE TYPE address ( street TEXT, city TEXT, zip TEXT);CREATE TABLE users ( id UUID PRIMARY KEY, emails SET<TEXT>, logins LIST<TIMESTAMP>, attributes MAP<TEXT, TEXT>, home_address FROZEN<address>);-- Append to a set/list without a readUPDATE users SET emails = emails + {'[email protected]'} WHERE id = 123e4567-e89b-12d3-a456-426614174000;UPDATE users SET logins = logins + [toTimestamp(now())] WHERE id = 123e4567-e89b-12d3-a456-426614174000;UPDATE users SET attributes['plan'] = 'pro' WHERE id = 123e4567-e89b-12d3-a456-426614174000;
Batches & Counter Tables
Grouping same-partition writes atomically and maintaining distributed counters.
-- BATCH only guarantees atomicity within a single partition; avoid multi-partition batches for perfBEGIN BATCH INSERT INTO users_by_email (email, id) VALUES ('[email protected]', 123e4567-e89b-12d3-a456-426614174000); UPDATE users SET email = '[email protected]' WHERE id = 123e4567-e89b-12d3-a456-426614174000;APPLY BATCH;-- Counter columns require a dedicated table typeCREATE TABLE page_views ( page_id TEXT PRIMARY KEY, views COUNTER);UPDATE page_views SET views = views + 1 WHERE page_id = 'home';
Python Driver: Prepared Statements & Paging
Using the official cassandra-driver for efficient, reusable queries with automatic result paging.
from cassandra.cluster import Clusterfrom cassandra.query import SimpleStatementcluster = Cluster(['10.0.0.1', '10.0.0.2'])session = cluster.connect('mykeyspace')# Prepared statements are parsed once, reused many times — always prefer over string interpolationinsert_stmt = session.prepare("INSERT INTO users (id, email) VALUES (?, ?)")session.execute(insert_stmt, [uuid.uuid4(), '[email protected]'])# Automatic paging keeps memory bounded on large partitionsstatement = SimpleStatement("SELECT * FROM users", fetch_size=100)for row in session.execute(statement): process(row)
Operational Internals
Concepts that matter once a cluster is running in production.
- Tombstone- a marker left by a DELETE; read too many across a partition and queries time out (tombstone_warn_threshold)
- gc_grace_seconds- how long tombstones are retained before compaction can purge them; must exceed repair interval
- vnodes- virtual nodes that split each physical node's token range for finer-grained rebalancing
- Gossip protocol- peer-to-peer state exchange nodes use to discover cluster topology and failures
- Hinted handoff- a coordinator stores writes for a down replica and replays them once it rejoins
- Anti-entropy repair- nodetool repair reconciles replica divergence using Merkle trees; must run before gc_grace_seconds expires
- Snitch- determines network topology so the coordinator routes requests to the nearest replicas
- SASI / SAI index- secondary index types for non-primary-key predicates; still far more limited than a relational index
Design tables around your queries, not your entities — Cassandra has no JOINs, so the standard rule of thumb is one denormalized table per query pattern.