Time-series and feed queries are fast only when the table's on-disk order matches what the query asks for.
Rows inside a partition are stored sorted by the clustering columns, so a query that reads a contiguous slice in that order is a sequential read. Design the clustering order around your most common access:
- "latest 20 messages" wants
CLUSTERING ORDER BY (sent_at DESC), so the newest rows come first andLIMIT 20stops early, - "readings between 08:00 and 10:00" is a range on the clustering column,
- with several clustering columns,
(year, month, day), you can restrict a prefix with equality and range on the next.
Reading in the reverse of the declared order works (ORDER BY sent_at ASC on a DESC table), but forward-order reads are more efficient, so declare the order your hot query uses.
Paging through a partition is natural: drivers page automatically using a paging state, and you can also page manually with a range on the clustering column: WHERE channel_id = ? AND sent_at < :last_seen LIMIT 20.
Multi-column restrictions such as (sent_at, msg_id) < ('2026-10-17 09:00', ...) compare tuples in clustering order, which is how you continue exactly after the last row when several rows share a timestamp.
Clustering order is fixed when the table is created; changing it means a new table and copying data.
Example
-- table messages: PRIMARY KEY ((channel_id), sent_at, msg_id)
-- WITH CLUSTERING ORDER BY (sent_at DESC, msg_id DESC)
-- newest 20 messages: one sequential read, stops after 20 rows
SELECT sent_at, author, body FROM shop.messages
WHERE channel_id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47
LIMIT 20;
-- next page: continue strictly after the last (sent_at, msg_id) seen
SELECT sent_at, msg_id, body FROM shop.messages
WHERE channel_id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47
AND (sent_at, msg_id) < ('2026-10-17 09:00:00+0000', 50554d6e-29bb-11ef-9f3c-0242ac120002)
LIMIT 20;
-- reverse order is allowed, but less efficient
SELECT body FROM shop.messages
WHERE channel_id = 6ab09bec-e68e-48d9-a5f8-97e6fb4c9b47
ORDER BY sent_at ASC, msg_id ASC LIMIT 5;Remember
- Rows are stored sorted by clustering columns within a partition
- Declare CLUSTERING ORDER BY to match the hot query (often DESC)
- Ranges apply to the next clustering column after equality prefixes
- Tuple comparisons continue paging exactly after the last row
Watch out for
- Declaring ascending order for a latest-first feed and paying for reverse reads on every request.