Skip to content

Modeling Objects with Hashes

One hash per object — the canonical pattern

Section titled “One hash per object — the canonical pattern”

The idiomatic way to model a domain object in Redis is to store all its fields under a single hash key. A user record, for example, lives entirely under user:1:

127.0.0.1:6379> HSET user:1 name "Ada" email "[email protected]" age "28" points "0"
(integer) 4
127.0.0.1:6379> HGETALL user:1
1) "name"
2) "Ada"
3) "email"
5) "age"
6) "28"
7) "points"
8) "0"

Compare that to the many-keys alternative:

ApproachKeys required
Hashuser:1 — one key, one HSET
Separate stringsuser:1:name, user:1:email, user:1:age, user:1:points — four keys

The hash approach uses less memory and lets you fetch or update a specific field — with HGET / HSET — without loading or touching any other field in the object.

HSET user:1 name "Ada" email "[email protected]" age "28" points "0"
HGETALL user:1

When a hash field holds a number, Redis can increment it atomically — no read-modify-write race:

  • HINCRBY key field increment — increment an integer field by an integer amount
  • HINCRBYFLOAT key field increment — increment a float field by a float amount
127.0.0.1:6379> HINCRBY user:1 points 10
(integer) 10
127.0.0.1:6379> HINCRBY user:1 age 1
(integer) 29
127.0.0.1:6379> HINCRBYFLOAT user:1 score 1.5
"1.5"
127.0.0.1:6379> HINCRBYFLOAT user:1 score 0.5
"2"
127.0.0.1:6379> HGET user:1 points
"10"

If the field does not exist yet, both commands treat its starting value as 0 before applying the increment, so HINCRBY user:1 points 5 on a missing points field creates it with the value 5.

HSET user:1 name "Ada" email "[email protected]" age "28" points "0"
HINCRBY user:1 points 10
HINCRBY user:1 age 1
HINCRBYFLOAT user:1 score 1.5
HINCRBYFLOAT user:1 score 0.5
HGET user:1 points

HSETNX key field value sets a field only if it does not already exist. It returns 1 if the field was created, 0 if it was already present and left unchanged.

127.0.0.1:6379> HSETNX user:1 email "[email protected]"
(integer) 0
127.0.0.1:6379> HGET user:1 email
127.0.0.1:6379> HSETNX user:1 referrer "bob"
(integer) 1
127.0.0.1:6379> HGET user:1 referrer
"bob"

A practical use case: stamp a created_at field exactly once when the object is first written. Because HSETNX is atomic, two concurrent writers cannot both win — only the first call returns 1.

HSET user:1 name "Ada" email "[email protected]"
HSETNX user:1 email "[email protected]"
HGET user:1 email
HSETNX user:1 referrer "bob"
HGET user:1 referrer

Hash vs many string keys — when to use which

Section titled “Hash vs many string keys — when to use which”
Hash (one key)Many string keys
MemoryCompact (listpack for small hashes)Higher overhead per key
Fetch one fieldHGET user:1 nameGET user:1:name
Fetch all fieldsHGETALL user:1MGET user:1:name user:1:email ...
Atomic incrementHINCRBYINCR user:1:points
TTL per fieldNot possibleYes (each key has its own TTL)

Rule of thumb: prefer one hash per object. Use separate string keys only if you need per-field TTL.

What does `HINCRBY user:1 points 5` do if `points` field does not exist yet?
What does `HSETNX user:1 email '[email protected]'` return if the field already exists?
Which approach uses less memory for storing a user object in Redis?
Which command atomically increments a float field in a hash?