HyperLogLog
A HyperLogLog (HLL) estimates how many distinct items you have added to it, without storing the items themselves. Each counter takes at most 12 KB however many items it has seen, and its estimate is typically within about 0.81% of the true count. Use it for unique visitors, distinct search terms, unique IPs per hour, and any cardinality question where an exact answer would mean keeping every item.
You cannot list or remove the items in a HyperLogLog, or ask whether one specific item was added. For exact counts and membership tests use a set; for approximate membership tests use a Bloom filter.
Create, read, update, delete
Section titled “Create, read, update, delete”PFADD adds items and creates the key if needed. It
replies 1 when the estimate changed and 0 when it did not (for example
when every item was already counted).
PFCOUNT returns the estimate.
127.0.0.1:6379> PFADD visitors:2026-10-02 "u:17" "u:23" "u:42" "u:88"(integer) 1127.0.0.1:6379> PFADD visitors:2026-10-02 "u:23"(integer) 0127.0.0.1:6379> PFCOUNT visitors:2026-10-02(integer) 4127.0.0.1:6379> PFADD visitors:2026-10-01 "u:42" "u:99"(integer) 1127.0.0.1:6379> PFCOUNT visitors:2026-10-01 visitors:2026-10-02(integer) 5127.0.0.1:6379> PFMERGE visitors:week visitors:2026-10-01 visitors:2026-10-02OK127.0.0.1:6379> PFCOUNT visitors:week(integer) 5127.0.0.1:6379> DEL visitors:week(integer) 1Counts this small are usually exact; as the number of distinct items grows, the answer becomes an estimate.
PFCOUNTwith several keys returns the estimate for their union, without changing any of them.- PFMERGE stores the union in a destination key, so you can roll daily counters up into weekly or monthly ones.
- A HyperLogLog is a string, so
TYPEreportsstringand DEL, EXPIRE and RENAME work as usual. Writing to it with string commands such asSETorAPPENDreplaces or corrupts it.
Memory
Section titled “Memory”A HyperLogLog starts out small and stays compact (sparse) until it would
take more than hll-sparse-max-bytes (3000 bytes by default); it then
switches to its full dense size of 12 KB, as in Redis. See
Configuration.
Compatibility
Section titled “Compatibility”HotKV stores HyperLogLogs in the same format as Redis, so their values can
move between Redis and HotKV (with DUMP/RESTORE, MIGRATE, or a plain
GET and SET), and both count the same items the same way.
PFDEBUG and PFSELFTEST are
there for diagnostics.
Common patterns
Section titled “Common patterns”Unique visitors per day. PFADD uv:<date> <visitor-id> on each page
view; PFCOUNT uv:<date> for the day, PFCOUNT over seven keys for the
week. Give each daily key an expiry so old days clean themselves up.
Unique counts per dimension. One counter per page, country, or
campaign (uv:page:/pricing:<date>): each costs at most 12 KB, so you can
afford many.
Rollups. Merge daily keys into a monthly key with PFMERGE at the end
of each day, then expire the daily keys.
Expiry
Section titled “Expiry”A HyperLogLog expires as a whole, like any string.
Commands
Section titled “Commands”| Command | What it does |
|---|---|
| PFADD | Add items |
| PFCOUNT | Estimated number of distinct items (of one key, or the union of several) |
| PFMERGE | Store the union of several HyperLogLogs |
| PFDEBUG | Inspect a HyperLogLog’s internal state |
| PFSELFTEST | Run the built-in self test |
From your application
Section titled “From your application”import hotkv
client = hotkv.HotKV(host="localhost", port=6379, decode_responses=True)
client.pfadd("visitors:2026-10-02", "u:17", "u:23", "u:42")client.pfadd("visitors:2026-10-01", "u:42", "u:99")print(client.pfcount("visitors:2026-10-01", "visitors:2026-10-02")) # 4client.pfmerge("visitors:week", "visitors:2026-10-01", "visitors:2026-10-02")client.close()import { HotKV } from "@hotkv/client";
const client = new HotKV({ url: "hotkv://127.0.0.1:6379" });
await client.pfAdd("visitors:2026-10-02", "u:17", "u:23", "u:42");await client.pfAdd("visitors:2026-10-01", "u:42", "u:99");console.log(await client.pfCount("visitors:2026-10-01", "visitors:2026-10-02"));await client.pfMerge("visitors:week", "visitors:2026-10-01", "visitors:2026-10-02");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.PFAdd(ctx, "visitors:2026-10-02", "u:17", "u:23", "u:42")client.PFAdd(ctx, "visitors:2026-10-01", "u:42", "u:99")n, _ := client.PFCount(ctx, "visitors:2026-10-01", "visitors:2026-10-02").Result()fmt.Println(n)client.PFMerge(ctx, "visitors:week", "visitors:2026-10-01", "visitors:2026-10-02")import com.hotkv.HotKV;
try (HotKV client = HotKV.create("hotkv://localhost:6379")) { client.pfAdd("visitors:2026-10-02", "u:17", "u:23", "u:42"); client.pfAdd("visitors:2026-10-01", "u:42", "u:99"); System.out.println(client.pfCount("visitors:2026-10-01", "visitors:2026-10-02")); client.pfMerge("visitors:week", "visitors:2026-10-01", "visitors:2026-10-02");}Was this page helpful?
Thanks for the feedback.