Lists, Sets, Sorted Sets, Arrays & Geospatial

This content was generated with the assistance of AI and should be verified against the official Redis documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Redis’s collection types share the same key-value model — one key names the whole structure — but differ in how their elements are ordered and addressed: a List keeps insertion order, a Set has none, a Sorted Set orders its members by an attached score, and an Array addresses elements by an integer index the way a fixed-size buffer does. Geospatial indexing reuses the Sorted Set internally rather than adding a distinct type. Examples on this page target Redis Open Source 8.10 (8.10.2), against Jedis 8 and Lettuce 7.7.

Lists

A List is a doubly-linked sequence of strings, pushed and popped from either end in O(1) time.

  • LPUSH key element [element …​] / RPUSH key element [element …​] — insert one or more elements at the head or tail. LPUSH / RPUSH.

  • LPOP key [count] — remove and return one or count elements from the head; RPOP is the tail-side mirror. LPOP.

  • BRPOP key [key …​] timeout — the blocking form of RPOP: waits up to timeout seconds for an element to appear in the first non-empty key given, instead of returning immediately when the list is empty. BRPOP.

  • LMOVE source destination LEFT|RIGHT LEFT|RIGHT — atomically pops one element from source and pushes it to destination, in a single round trip. BLMOVE is its blocking counterpart, waiting up to timeout seconds for source to produce an element. LMOVE / BLMOVE.

  • LMOVEM source destination LEFT|RIGHT LEFT|RIGHT [<COUNT count | EXACTLY exactly> <OBO | BULK>] — new in Redis 8.10, this is the multi-element version of LMOVE: without the optional block it behaves exactly like LMOVE, but with it moves several elements atomically in one call. COUNT moves up to that many elements (fewer if source is shorter); EXACTLY moves precisely that many or nothing, returning nil if source does not hold enough — useful when elements form fixed-size groups that must move together. OBO (one by one) and BULK control the resulting order on destination: OBO moves elements individually (and, when source and destination are the same key, reverses them in place), BULK moves the whole batch at once, preserving relative order. BLMOVEM is the blocking counterpart. LMOVEM.

  • LTRIM key start stop — trims a list down to the elements between start and stop (inclusive, zero-based, negative indexes count from the tail), discarding everything outside that range. Run after every push, it caps a list’s size in O(N) proportional to the number of elements removed. LTRIM.

Example: recently viewed books, capped with LTRIM

Every book view pushes an ISBN onto bookshop:recent:<userId>, then LTRIM caps the list at the 20 most recent entries so it never grows unbounded:

127.0.0.1:6379> LPUSH bookshop:recent:42 9780000000003
(integer) 1
127.0.0.1:6379> LTRIM bookshop:recent:42 0 19
OK
127.0.0.1:6379> LRANGE bookshop:recent:42 0 -1
1) "9780000000003"

See LTRIM and Redis lists for the full list reference this example derives from.

Sets

A Set is an unordered collection of unique strings, with O(1) average membership tests.

  • SADD key member [member …​] — add one or more members, ignoring ones already present. SADD.

  • SISMEMBER key member — test membership in O(1). SMISMEMBER tests several members in one call. SISMEMBER.

  • SINTER key [key …​] — returns the members present in every given set. SINTER.

  • SUNIONCARD numkeys key [key …​] [LIMIT limit] — the count-only counterpart of SUNION: returns just the number of distinct members in the union, without materializing it, optionally stopping early once limit members are counted. It also accepts APPROX, which estimates the cardinality with HyperLogLog for large unions instead of computing it exactly. SUNIONCARD.

  • SDIFFCARD numkeys key [key …​] [LIMIT limit] — new in Redis 8.10, the count-only counterpart of SDIFF: returns the number of members in the first set that are absent from every subsequent set, again with an optional early-stopping LIMIT. SDIFFCARD.

  • SRANDMEMBER key [count] — returns one or count random members without removing them; a positive count never repeats a member, a negative count may. SRANDMEMBER.

SUNIONCARD and SDIFFCARD both let an application ask "how many" without paying to transfer and re-count the member list on the client, which is what SUNION/SDIFF piped through a client-side count would otherwise cost.

Sorted sets

A Sorted Set (ZSET) pairs each unique member with a floating-point score and keeps members ordered by score, backed by a skip list plus a hash table, so both score-ordered range scans and by-member lookups are efficient.

  • ZADD key score member [score member …​] — add or update scored members; GT/LT/NX/XX flags constrain when an existing score is overwritten, and INCR turns it into an increment. This is the primary write for the best-sellers example below. ZADD.

  • ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count] [WITHSCORES] — reads a range of members. By default start/stop are rank indexes; BYSCORE interprets them as a score range instead (-inf/+inf and ( for exclusive bounds), BYLEX as a lexicographic range over equally-scored members, and REV reverses the scan from highest to lowest. This single command replaces the older ZRANGEBYSCORE/ZREVRANGE/ZRANGEBYLEX family. ZRANGE.

  • ZRANK key member [WITHSCORE] — returns a member’s 0-based rank in ascending score order (ZREVRANK for descending), optionally with its score in the same reply. ZRANK.

  • ZINCRBY key increment member — atomically adds increment to a member’s score, creating the member with that score if it is not already present. ZINCRBY.

  • ZPOPMIN key [count] — removes and returns the count members with the lowest scores; ZPOPMAX mirrors it at the high end. ZPOPMIN.

  • BZMPOP timeout numkeys key [key …​] MIN|MAX [COUNT count] — the blocking, multi-key pop: waits up to timeout seconds, then pops from the first of the given keys that is non-empty, taking the lowest (MIN) or highest (MAX) scoring members. BZMPOP.

  • ZUNION numkeys key [key …​] [WEIGHTS w [w …​]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES] and the equivalent ZINTER — combine several sorted sets, scoring a member present in more than one input by the chosen AGGREGATE function (SUM by default) over its optionally WEIGHTS-scaled input scores. ZUNION / ZINTER.

Example: best-sellers, a sorted set scored by units sold

bookshop:bestsellers scores each ISBN by units sold; ZINCRBY records a sale, and a reversed ZRANGE reads the top 10:

127.0.0.1:6379> ZINCRBY bookshop:bestsellers 1 9780000000001
"1"
127.0.0.1:6379> ZADD bookshop:bestsellers 42 9780000000002 17 9780000000003
(integer) 2
127.0.0.1:6379> ZRANGE bookshop:bestsellers 0 9 REV WITHSCORES
1) "9780000000002"
2) "42"
3) "9780000000003"
4) "17"
5) "9780000000001"
6) "1"

See ZINCRBY, ZRANGE, and Redis sorted sets for the reference this example derives from.

The Array type

Redis 8.8 added Array as a distinct data type from List: a sparse, index-addressable structure that maps integer indexes (0 to 264-1) to string values, giving O(1) access to any index instead of a List’s O(N) positional access. Because indexes can be set out of order and gaps are left unallocated, an Array suits timestamped event logs, ring buffers over streaming measurements, and sliding-window analytics — workloads that need direct addressing rather than sequential push/pop, which is exactly where a List, Hash (no position concept), or Sorted Set (scores are metadata, not addresses) falls short.

Its 18 commands split into a few groups:

  • Point access: ARSET key index value [value …​] writes one or more contiguous values starting at index; ARGET key index reads a single index, returning nil if unset. ARSET / ARGET.

  • Scattered access: ARMSET key index value [index value …​] writes arbitrary, non-contiguous indexes in one call; ARMGET key index [index …​] reads several indexes in one round trip. ARMSET / ARMGET.

  • Size and metadata: ARLEN key returns the logical length (highest set index + 1); ARCOUNT key returns the number of non-empty elements — the two diverge sharply on a sparse array. ARINFO key [FULL] returns the array’s metadata (count, length, next insert index, slice layout), and FULL adds per-slice dense/sparse statistics at O(N) cost. ARLEN / ARCOUNT / ARINFO.

  • Ranges and iteration: ARGETRANGE key start stop reads a contiguous range, including nil for unset slots; ARSCAN key start stop iterates only the elements that exist, returning index-value pairs. ARGETRANGE / ARSCAN.

  • Cursor-based append and ring buffers: ARINSERT appends at an auto-advancing cursor (ARNEXT reads it, ARSEEK repositions it); ARRING key size value [value …​] maintains a fixed-size circular buffer that overwrites the oldest entry once full, paired with ARLASTITEMS key count [REV] to read back the most recently inserted elements. ARRING / ARLASTITEMS.

  • Search, aggregate, and delete: ARGREP searches a range with textual predicates (EXACT/MATCH/GLOB/ RE, combinable with AND/OR); AROP runs a single-pass aggregate (such as SUM, MAX, or a match count) over a contiguous range; ARDEL key index [index …​] deletes specific indexes and ARDELRANGE deletes a whole index range. ARGREP / AROP.

Reach for an Array instead of a List when the access pattern is "read or write position N directly" — a sliding window, a ring buffer of the last N measurements, or a sparse timestamp-indexed log — and reach for a List when the access pattern is genuinely sequential push/pop from either end. Full command list and walkthrough: Redis arrays.

Geospatial

Geospatial indexing is not a separate storage engine: GEOADD stores each member in an ordinary Sorted Set, scored by a 52-bit interleaved geohash of its longitude/latitude, so every Sorted Set command (ZRANGE, ZSCORE, ZREM, and so on) already works against a geo key. The geo commands are a convenience layer that encodes/decodes that geohash and runs the radius/box math on top.

  • GEOADD key longitude latitude member [longitude latitude member …​] — adds or updates members' coordinates. GEOADD.

  • GEOPOS key member [member …​] — returns each member’s stored longitude/latitude, decoded back from its geohash score. GEOPOS.

  • GEODIST key member1 member2 [M | KM | FT | MI] — the distance between two members, in the given unit (metres by default). GEODIST.

  • GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude> <BYRADIUS radius unit | BYBOX width height unit> [ASC | DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH] — searches for members inside a circle (BYRADIUS) or an axis-aligned box (BYBOX) around a member or an explicit point. GEOSEARCH is the modern replacement for the deprecated GEORADIUS/GEORADIUSBYMEMBER commands and should be used in their place. GEOSEARCH.

  • GEOSEARCHSTORE destination source <FROMMEMBER member | FROMLONLAT longitude latitude> <BYRADIUS radius unit | BYBOX width height unit> […​] [STOREDIST] — runs the same search as GEOSEARCH but writes the matches into destination as a new sorted set instead of returning them, optionally storing distance rather than the geohash as the score (STOREDIST). GEOSEARCHSTORE.

The Query Engine indexes geospatial data differently again, through GEO and GEOSHAPE field types on a JSON or hash document rather than a member of a sorted set; see Query Engine: Indexing & Data Modeling for that indexing path.

Example: nearest store lookup

bookshop:stores is a geo key mapping store IDs to coordinates; GEOSEARCH finds every store within 10 km of a customer’s location, nearest first:

127.0.0.1:6379> GEOADD bookshop:stores 2.3522 48.8566 "store:paris-1"
(integer) 1
127.0.0.1:6379> GEOADD bookshop:stores 2.2945 48.8584 "store:paris-2"
(integer) 1
127.0.0.1:6379> GEOSEARCH bookshop:stores FROMLONLAT 2.3200 48.8600 BYRADIUS 10 km ASC WITHDIST
1) 1) "store:paris-2"
   2) "1.8745"
2) 1) "store:paris-1"
   2) "2.3865"

See GEOSEARCH and Redis geospatial for the reference this example derives from.

Structures compared

flowchart LR L["List\nO(1) push/pop at ends\nO(N) index access"] -->|order matters, ends only| U1["recently viewed"] S["Set\nO(1) add/test\nno order"] -->|uniqueness, membership| U2["already-seen ISBNs"] Z["Sorted Set\nO(log N) add/rank\nO(log N + M) range"] -->|ranking, leaderboards| U3["best-sellers"] A["Array\nO(1) index access\nsparse, index-addressed"] -->|direct position access| U4["ring buffers, sliding windows"] G["Geospatial\n(Sorted Set + geohash)\nO(N + log M) search"] -->|proximity search| U5["nearest store"]
Type Ordering Key operations Typical complexity

List

Insertion order

LPUSH/RPUSH, LPOP/RPOP, LMOVE, LTRIM

O(1) at either end, O(N) for an index or a trim

Set

None

SADD, SISMEMBER, SINTER/SUNIONCARD/SDIFFCARD

O(1) add/test, O(N) for a multi-set combination

Sorted Set

By score

ZADD, ZRANGE, ZRANK, ZINCRBY, ZPOPMIN/BZMPOP

O(log N) add/rank, O(log N + M) for a range of M members

Array

By integer index

ARSET/ARGET, ARMSET/ARMGET, ARRING

O(1) per index, O(N) for a contiguous range

Geospatial

By geohash score (a Sorted Set)

GEOADD, GEOSEARCH, GEOSEARCHSTORE

O(N + log M) for a search over N candidates and M matches

  • Use Cases & Patterns — caching, session storage, and rate-limiting patterns that build on these structures.

  • Query Engine: Indexing & Data Modeling — indexing JSON/hash documents, including the GEO/GEOSHAPE field types that complement the geohash-on-a-sorted-set approach described here.