Several-fold less memory
Every top-level Redis key carries heavy per-key overhead. Bucketing a million of them into ~2,600 listpack hashes pays it 2,600 times instead of a million.
Client-agnostic Redis helper that shards key→value pairs across bucketed hashes so each one stays in the compact listpack encoding.
npm install redis-packhashimport { PackHash } from "redis-packhash";
import Redis from "ioredis";
const store = new PackHash(new Redis(), {
namespace: "users",
expectedKeys: 1_000_000,
});
// A profile cache keyed by user UUID. Pass the bare UUID, not "user:<uuid>":
// the namespace already scopes it, and the key is stored as a hash field, so
// its bytes count against Redis's 64-byte listpack value limit just as the
// value's do. Values are strings; serialize and parse on your side.
const id = "f47ac10b-58cc-4372-a567-0e02b2c3d479";
await store.set(id, JSON.stringify({ name: "John Doe", plan: "pro" }));
const raw = await store.get(id);
//=> '{"name":"John Doe","plan":"pro"}'
await store.has(id); //=> true
await store.del(id); //=> trueRedis keeps small hashes in listpack, a flat encoding with almost no per-entry overhead, renamed from ziplist in Redis 7.0. It promotes a hash to a far more expensive hash table as soon as either threshold is crossed: more than hash-max-listpack-entries fields (512 by default), or any single field name or value over hash-max-listpack-value (64 bytes). So you never use listpack; you keep each hash small enough to stay in it. That is the entire trick:
store.set("f47ac10b-58cc-4372-a567-0e02b2c3d479", val)
│
├─ bucket = fnv1a(key) % 2605 → 1342
├─ HSET users:1342 "f47ac10b-58cc-4372-a567-0e02b2c3d479" val
│ └─ 36-byte field, under the 64-byte listpack limit
└─ each "users:N" hash stays a listpack → compact memoryStoring a million values as a million top-level keys pays per-key overhead a million times. Storing them as fields inside ~2,600 listpack-encoded hashes does not.
How it works, in full → · Get started →
Node 22 or newer. Zero runtime dependencies. Both ESM and CommonJS builds ship in the package, with TypeScript types included.