Data types, collections and UDTs

Apache Cassandra · Essentials · 10 min

Choosing the right types, especially ids, times and collections, avoids subtle bugs and performance problems.

CQL has the usual scalars: text, int, bigint, smallint, double, decimal (exact, for money), boolean, blob, inet, date, time, timestamp (millisecond precision) and duration.

Identifiers deserve special attention:

  • uuid for random ids (uuid() generates a version 4 UUID),
  • timeuuid for time-ordered unique ids (now() generates one; toTimestamp(id) extracts the time). Ideal as a clustering column for events, because two events in the same millisecond still get different ids.

Collections store small multi-valued data in one column: list<text>, set<text>, map<text, int>. You can update parts in place (tags = tags + {'sale'}). They're meant for small, bounded data, because a collection is read in full. Appending to lists is not idempotent and some list operations require a read before write internally; sets and maps are usually better.

User-defined types group fields: CREATE TYPE address (street text, city text). Mark collections of UDTs, or nested collections, as frozen<...>, which stores the value as one blob that can only be replaced as a whole.

counter columns support only increment and decrement and must live in tables containing only counters (plus the key). Cassandra 5.0 adds vector<float, n> for embeddings, searchable with SAI.

Example

CREATE TYPE IF NOT EXISTS shop.address (street text, city text, zip text);

CREATE TABLE shop.users (
  user_id    uuid PRIMARY KEY,
  email      text,
  tags       set<text>,
  prefs      map<text, text>,
  home       frozen<address>,
  balance    decimal,
  created_at timestamp
);

INSERT INTO shop.users (user_id, email, tags, prefs, home, balance, created_at)
VALUES (uuid(), 'ana@example.com', {'beta'}, {'lang': 'hy'},
        {street: 'Abovyan 1', city: 'Yerevan', zip: '0001'}, 12.50, toTimestamp(now()));

UPDATE shop.users SET tags = tags + {'vip'}, prefs['theme'] = 'dark'
WHERE user_id = 5b6962dd-3f90-4c93-8f61-eabfa4a803e2;

Remember

  • timeuuid gives unique, time-ordered ids; uuid gives random ones
  • Collections are for small bounded data and are read whole
  • frozen stores a UDT or collection as one replaceable value
  • counter tables hold only counters; vector<float, n> is new in 5.0

Watch out for

  • Using timestamp alone as a clustering column for events: two events in the same millisecond overwrite each other.

Check yourself

  1. Two chat messages arrive in the same millisecond in one channel. Which clustering column keeps both?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: cassandra.apache.org/doc/latest/cassandra/developing/cql/types.html