Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Use Redis Hashes for Objects

Learn to store and retrieve objects efficiently using Redis hashes: a hands-on guide to mapping real-world data structures to hash fields.

Focus: use redis hashes for objects

Sponsored

Storing flat key-value pairs works well for simple data, but real-world applications deal with objects: user profiles, product details, session data. Storing each field as a separate key clutters your namespace and hurts performance. Redis Hashes give you a compact, mutable way to represent objects as a single key with multiple fields — the map data structure you've been missing.

The problem this lesson solves

Imagine building a user profile service. Each user has a name, email, age, and preferences. With plain string keys, you'd end up with keys like user:1001:name, user:1001:email, and so on. This approach is wasteful: Redis stores each key with its own metadata overhead, and fetching or updating a full profile requires multiple round trips. Worse still, atomic updates to multiple fields are impossible without transactions or Lua scripts.

When you need to represent a mutable object with many fields, using a Redis Hash is the idiomatic solution. Hashes store field-value pairs under one key, provide O(1) operations for field-level access, and support partial updates and deletions — perfect for mapping programming objects to Redis.

Core concept / mental model

Think of a Redis Hash as a mini dictionary nested inside a Redis key. The outer key acts like a class name or an object ID, and the fields are its attributes. Just like a Python dict or a JavaScript object, you can set, get, increment, or delete individual fields without touching the rest.

Key properties of hashes

  • Flat structure: fields are strings, values are strings (binary safe); no nesting
  • Field-level operations: HSET, HGET, HDEL, HINCRBY — you control each attribute independently
  • Bulk read/write: HGETALL, HMGET, HMSET let you fetch or set multiple fields in one command
  • Memory efficient: hashes are optimized when the number of fields is small (< 512 by default via hash-max-ziplist-entries)

Pro tip: Use hashes when you need to update a subset of an object's properties atomically without a transaction. For example, incrementing a user's login count (HINCRBY) while leaving other fields untouched.

How it works step by step

Here's the logical flow when working with Redis hashes for objects:

  1. Define the object's key pattern: choose a consistent naming convention like object_type:id (e.g., user:42, product:shirt-123).
  2. Store the object: use HSET with the key, followed by alternating field-value pairs. If the key doesn't exist, Redis creates it automatically.
  3. Read individual fields: use HGET with the key and field name.
  4. Read the full object: use HGETALL — returns all field-value pairs as a flat array (Redis calls it a "list of tuples" on the wire).
  5. Update selective fields: use HSET again (it upserts — no error if the field doesn't exist).
  6. Remove fields: use HDEL with one or more field names.
  7. Delete the hash entirely: use DEL on the key.

Key pattern best practices

  • Always namespace with a colon: object:type:id
  • Avoid spaces or special characters in field names
  • Keep field names short to save memory

Hands-on walkthrough

Let's model a user object in Redis using the redis Python client. Make sure you have the package installed: pip install redis.

Example 1: Creating and reading a user profile

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# Store a user object as a hash
user_key = "user:42"
r.hset(user_key, mapping={
    "name": "Alice",
    "email": "alice@example.com",
    "age": 30,
    "city": "New York"
})

# Read single field
print(r.hget(user_key, "name"))   # Output: Alice

# Read all fields
profile = r.hgetall(user_key)
print(profile)
# Output: {'name': 'Alice', 'email': 'alice@example.com', 'age': '30', 'city': 'New York'}

Example 2: Updating selective fields and increments

# Update email and increment age in one call (two separate commands)
r.hset(user_key, "email", "alice@newdomain.com")
r.hincrby(user_key, "age", 1)

# Check the result
updated = r.hgetall(user_key)
print(updated)
# Output: {'name': 'Alice', 'email': 'alice@newdomain.com', 'age': '31', 'city': 'New York'}

Example 3: Bulk reading with HMGET

# Get only name and email
fields = r.hmget(user_key, "name", "email")
print(fields)   # Output: ['Alice', 'alice@newdomain.com']

Example 4: Removing a field and deleting the hash

# Remove city field
r.hdel(user_key, "city")
print(r.hgetall(user_key))
# Output: {'name': 'Alice', 'email': 'alice@newdomain.com', 'age': '31'}

# Delete the entire hash
r.delete(user_key)
print(r.exists(user_key))   # Output: 0

Pro tip: Use hscan_iter() when dealing with hashes that have many fields — it avoids blocking the server by returning results in batches (cursor-based).

Compare options / when to choose what

Data Type Best for Drawbacks for objects
String (JSON blob) Simple, nested objects Cannot update individual fields without re-serializing entire blob; not atomic for partial updates
Hash (this lesson) Flat objects with known fields, frequent partial updates Cannot represent nested structures (no sub-objects)
JSON module (RedisJSON) Deeply nested objects, querying by fields Requires RedisJSON module; heavier operations
Set / Sorted Set Collections, relationships Not suitable for key-value attributes

For most CRUD-oriented objects (user profiles, product details, configuration), hashes strike the perfect balance of simplicity, performance, and atomicity.

Troubleshooting & edge cases

1. HGETALL returns an empty dictionary unexpectedly

  • Cause: The hash key exists but has no fields. Check with HLEN.
  • Fix: Re-initialize the fields with HSET.

2. Fields stored as bytes in Python

  • Solution: When not using decode_responses=True, fields are bytes. Decode manually: value = r.hget(key, field).decode('utf-8').

3. Hash key conflicts with another type

  • Symptom: WRONGTYPE Operation against a key holding the wrong kind of value
  • Fix: Use TYPE key to inspect, then DEL if safe, or rename keys to follow a consistent namespace.

4. Memory warnings due to huge hashes

  • Scenario: Hash with >10,000 fields causes slow HGETALL.
  • Mitigation: Use HSCAN or HGETALL only when necessary. Consider splitting into multiple hashes (e.g., user:42:meta, user:42:preferences).

5. Integer overflow with HINCRBY

  • Note: HINCRBY works on 64-bit signed integers. Exceeding ±9 quintillion raises an error. Use HINCRBYFLOAT for floating-point counters.

What you learned & what's next

You now know how to use Redis hashes for objects: create them with HSET, read with HGET/HGETALL, update partially with HSET/HINCRBY, and delete fields with HDEL. You understand the mental model of hashes as flat dictionaries and can choose hashes over strings or JSON modules based on your data shape.

What's next? The next lesson explores expiring hashes with TTL — controlling object lifetime for sessions and caches. You'll combine hashes with expiration policies to build self-cleaning data stores.

Practice recap

Try it yourself: Write a Python script that stores a book object with fields title, author, year, and isbn. Then update the year field, read the full object, and finally delete the isbn field. Print the hash after each step to verify your commands.

Common mistakes

  • Storing each object field as a separate Redis string key (e.g., user:42:name, user:42:email) — this wastes memory and makes atomic updates impossible.
  • Using HSET without mapping in older Redis clients, leading to extra round trips when setting multiple fields.
  • Forgetting that HGETALL returns a dictionary with all fields — avoid it for very large hashes; use HSCAN instead.
  • Assuming hashes support nested objects — they don't. For nested data, serialize to JSON or use the RedisJSON module.

Variations

  1. Use HSETNX to set a field only if it doesn't already exist (atomic set-if-not-exists per field).
  2. For large hashes, use HSCAN with a cursor to iterate fields in batches instead of HGETALL.
  3. Combine hashes with EXPIRE for time-limited objects (e.g., user sessions).

Real-world use cases

  • Store user session data (last login, preferences) – fast field updates without fetching entire session.
  • Cache product catalog items – each product is a hash with fields like price, stock, description.
  • Manage configuration objects (e.g., feature flags) – update a single flag atomically across all instances.

Key takeaways

  • Redis hashes store field-value pairs under one key — perfect for mapping programming objects to Redis.
  • Use HSET to create/update, HGET to read one field, HGETALL to read all fields of a hash.
  • Hashes support partial updates (HSET, HDEL, HINCRBY) without touching other fields.
  • Choose hashes over plain strings when you need atomic updates or partial modifications of an object.
  • Avoid huge hashes with thousands of fields — use HSCAN for iteration and consider sharding.
  • Always namespace hash keys consistently (e.g., user:42) to avoid collisions.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.