Sets
A set is an unordered collection of unique strings. Adding a member that is already there changes nothing, and checking whether a member is present takes constant time however large the set is. Sets are a natural fit for tags on an object, the unique visitors of a page, who follows whom, and any question of the form “which items are in both of these groups?”.
If you need the members in a particular order or with a score, use a sorted set. If you only need an approximate count of unique items and want it in a few kilobytes, use a HyperLogLog.
Create, read, update, delete
Section titled “Create, read, update, delete”Adding to a missing key creates the set, and removing its last member deletes it. SMEMBERS returns members in no particular order.
127.0.0.1:6379> SADD article:7:tags "redis" "databases" "caching"(integer) 3127.0.0.1:6379> SADD article:7:tags "caching"(integer) 0127.0.0.1:6379> SISMEMBER article:7:tags "redis"(integer) 1127.0.0.1:6379> SMISMEMBER article:7:tags "redis" "python"1) (integer) 12) (integer) 0127.0.0.1:6379> SCARD article:7:tags(integer) 3127.0.0.1:6379> SREM article:7:tags "databases"(integer) 1127.0.0.1:6379> SMEMBERS article:7:tags1) "redis"2) "caching"Commands that return members (SMEMBERS, SINTER, SUNION, …) return
them in no defined order, so yours may come back differently.
Other ways to read and change a set:
- SRANDMEMBER returns random members without removing them; SPOP removes and returns them.
- SMOVE moves a member from one set to another atomically.
- SSCAN iterates a large set in pages, so you never pull millions of members in one reply.
Set algebra
Section titled “Set algebra”Combine sets on the server instead of downloading them:
127.0.0.1:6379> SADD user:1:follows "ann" "bob" "cid"(integer) 3127.0.0.1:6379> SADD user:2:follows "bob" "cid" "dee"(integer) 3127.0.0.1:6379> SINTER user:1:follows user:2:follows1) "bob"2) "cid"127.0.0.1:6379> SDIFF user:1:follows user:2:follows1) "ann"127.0.0.1:6379> SINTERCARD 2 user:1:follows user:2:follows(integer) 2127.0.0.1:6379> SUNIONSTORE everyone user:1:follows user:2:follows(integer) 4SUNION, SINTER and
SDIFF return the result; the ...STORE forms write it
to a destination key and return its size. SINTERCARD
returns only the size of the intersection, and its LIMIT option stops
counting early when you only need to know “at least N”.
In a cluster, multi-key commands need all their keys in the
same hash slot. Give related sets a common hash tag, for example
{user:1}:follows and {user:1}:blocked.
Common patterns
Section titled “Common patterns”Unique visitors. SADD visitors:2026-10-02 <user-id> once per visit;
SCARD gives the exact count of distinct visitors that day.
Mutual friends and recommendations. SINTER of two follow sets gives
the people both follow; SDIFF gives the ones only one of them follows.
Deduplicating work. SADD seen:urls <url> replies 1 the first time
and 0 after that, so a crawler can skip URLs it has already queued.
Random picks. SRANDMEMBER with a count draws samples; SPOP draws
winners that should not be drawn again.
Expiry
Section titled “Expiry”A time to live applies to the whole set. Single members cannot expire. For
members that should age out on their own, use a sorted set scored by time
and trim it with ZREMRANGEBYSCORE, or a hash with
field TTLs.
Limits and performance
Section titled “Limits and performance”Each member is a string up to proto-max-bulk-len (512 MB by default).
SADD, SREM and SISMEMBER take constant time per member. SMEMBERS and
the set algebra commands touch every member involved, so on large sets
prefer SSCAN, SINTERCARD with a LIMIT, or the ...STORE forms that
keep the result on the server.
Commands
Section titled “Commands”| Command | What it does |
|---|---|
| SADD | Add members |
| SREM | Remove members |
| SISMEMBER / SMISMEMBER | Test membership |
| SCARD | Number of members |
| SMEMBERS | All members |
| SSCAN | Iterate members in pages |
| SRANDMEMBER | Random members |
| SPOP | Remove and return random members |
| SMOVE | Move a member to another set |
| SUNION / SUNIONSTORE | Union |
| SINTER / SINTERSTORE | Intersection |
| SINTERCARD | Size of the intersection |
| SDIFF / SDIFFSTORE | Difference |
From your application
Section titled “From your application”import hotkv
client = hotkv.HotKV(host="localhost", port=6379, decode_responses=True)
client.sadd("user:1:follows", "ann", "bob", "cid")client.sadd("user:2:follows", "bob", "cid", "dee")print(client.sismember("user:1:follows", "ann")) # Trueprint(client.sinter("user:1:follows", "user:2:follows")) # {'bob', 'cid'}print(client.scard("user:1:follows")) # 3client.close()import { HotKV } from "@hotkv/client";
const client = new HotKV({ url: "hotkv://127.0.0.1:6379" });
await client.sAdd("user:1:follows", "ann", "bob", "cid");await client.sAdd("user:2:follows", "bob", "cid", "dee");console.log(await client.sIsMember("user:1:follows", "ann"));console.log(await client.sInter("user:1:follows", "user:2:follows"));console.log(await client.sCard("user:1:follows"));await client.close();client, err := hotkv.NewClient(&hotkv.Options{Addr: "localhost:6379"})if err != nil { log.Fatal(err)}defer client.Close()ctx := context.Background()
client.SAdd(ctx, "user:1:follows", "ann", "bob", "cid")client.SAdd(ctx, "user:2:follows", "bob", "cid", "dee")ok, _ := client.SIsMember(ctx, "user:1:follows", "ann").Result()both, _ := client.SInter(ctx, "user:1:follows", "user:2:follows").Result()fmt.Println(ok, both)import com.hotkv.HotKV;
try (HotKV client = HotKV.create("hotkv://localhost:6379")) { client.sadd("user:1:follows", "ann", "bob", "cid"); client.sadd("user:2:follows", "bob", "cid", "dee"); System.out.println(client.sisMember("user:1:follows", "ann")); System.out.println(client.sinter("user:1:follows", "user:2:follows")); System.out.println(client.scard("user:1:follows"));}Was this page helpful?
Thanks for the feedback.