Increment Hash Fields
Master incrementing hash fields in Redis with a hands-on lesson. Learn atomic updates, increment commands, and best practices for real-world data structures.
Focus: increment hash fields
You've mastered storing and retrieving hash fields with HSET and HGET, but what happens when you need to update a numeric counter inside a hash — like tracking page views per user or inventory stock? Doing a read-update-write cycle manually is both slow and unsafe under concurrent access. This lesson introduces the atomic HINCRBY and HINCRBYFLOAT commands, letting you increment hash fields safely and efficiently in a single operation.
The problem this lesson solves
Imagine you're building a leaderboard for an online game where each player's score is stored in a Redis hash. Every time a player wins, you need to increase their score by a certain amount. Without an atomic increment, you'd have to:
HGET player:123 scoreto read the current value- Parse it in your application
- Add the new points
HSET player:123 score <new_value>to write it back
This approach is not thread-safe. If two requests increment the score at the exact same time, one update can overwrite the other — a classic race condition. Worse, the read-and-write are separate operations, so you lose consistency under load.
Core concept / mental model
Think of Redis hashes as JSON-like objects where each field holds a value. HINCRBY is like an atomic counter built into the data structure. Instead of reading, modifying, and writing back, you tell Redis: "Take the current value of this field and add X to it in one safe operation."
Blockquote: Pro tip — All increment commands in Redis are atomic. No matter how many clients call
HINCRBYsimultaneously, each increment is applied sequentially with no lost updates.
The mental model is simple: Redis locks the field for the microsecond it takes to increment, then releases it. This guarantees correctness without explicit locks in your application code.
How it works step by step
The HINCRBY command
HINCRBY key field increment — It increases the integer value stored at field inside key by the increment amount.
- If the field does not exist, it is created with a value of
0before the increment is applied. - The field must hold a string that represents an integer (e.g.,
"42"). Strings like"hello"or floating-point strings will raise an error. - The result is returned as an integer.
The HINCRBYFLOAT command
HINCRBYFLOAT key field increment — Same concept but for floating-point numbers. It handles decimal precision natively.
- Accepts both integer and float values as increment.
- Stores the result as a string representation of the float (Redis internally uses double precision).
- Returns the final value as a string.
Atomicity guarantee
When you send HINCRBY, Redis processes it in a single event-loop tick. No other command (even from another client) can interleave between reading and writing that field. This makes it ideal for counters, scores, and tracking metrics.
Hands-on walkthrough
Let's start with a real example — tracking page views per article in a Redis hash.
Example 1: Basic integer increment
# Start fresh
DEL article:42
# Set initial views to 0 (implicitly via HSET or just HINCRBY)
HSET article:42 views 0
# Increment by 1
HINCRBY article:42 views 1
# Returns: (integer) 1
# Increment by 5
HINCRBY article:42 views 5
# Returns: (integer) 6
# Check the value
HGET article:42 views
# Returns: "6"
Example 2: Multiple fields in one hash — player stats
import redis
r = redis.Redis(decode_responses=True)
# Player stats
r.hset("player:1001", mapping={
"score": 100,
"games_played": 50,
"lives": 3
})
# Player scores a bonus of 25 points
new_score = r.hincrby("player:1001", "score", 25)
print(f"New score: {new_score}") # Output: New score: 125
# Increase games played by 1
games = r.hincrby("player:1001", "games_played", 1)
print(f"Games played: {games}") # Output: Games played: 51
# Verify atomicity — check no loss
all_stats = r.hgetall("player:1001")
print(all_stats)
# Output: {'score': '125', 'games_played': '51', 'lives': '3'}
Example 3: Floating-point increments with HINCRBYFLOAT
import redis
r = redis.Redis(decode_responses=True)
# Track a user's balance
r.hset("user:42", "balance", 100.50)
# Add interest of 2.75
new_balance = r.hincrbyfloat("user:42", "balance", 2.75)
print(f"New balance: {new_balance}") # Output: New balance: 103.25
# Subtract a fee (negative increment)
new_balance = r.hincrbyfloat("user:42", "balance", -0.50)
print(f"After fee: {new_balance}") # Output: After fee: 102.75
# You can also use integer increments with float fields
new_balance = r.hincrbyfloat("user:42", "balance", 10)
print(f"After deposit: {new_balance}") # Output: After deposit: 112.75
Example 4: Handling non-existent fields
import redis
r = redis.Redis(decode_responses=True)
# Increment a field that doesn't exist yet
result = r.hincrby("stats:homepage", "visitors", 1)
print(result) # Output: 1 (field created as 0 then incremented)
# The hash now has that field
print(r.hgetall("stats:homepage"))
# Output: {'visitors': '1'}
Compare options / when to choose what
| Command / Alternative | Use case | Atomic? | Type | Notes |
|---|---|---|---|---|
HINCRBY |
Integer counters (e.g., page views, scores) | Yes | Integer | Fast, native, no client-side race |
HINCRBYFLOAT |
Float counters (e.g., balances, ratings) | Yes | Float | Handles precision; returns string |
Manual HGET + HSET |
Non-numeric updates | No | Any | Risk of race conditions; avoid for counters |
INCR / INCRBY |
Simple string key counters | Yes | Integer | Use for root-level keys, not fields |
| Lua scripting | Complex multi-field logic | Yes | Mixed | Overkill for simple increments |
When to use what:
- Use HINCRBY for integer counters inside hashes — that's your primary use case.
- Use HINCRBYFLOAT when you need decimal precision like currency or ratings.
- Avoid manual read-write for numeric updates — always prefer atomic increments.
Troubleshooting & edge cases
Error: WRONGTYPE Operation against a key holding the wrong kind of value
This happens if you try to use HINCRBY on a key that's not a hash (e.g., a string or list).
# Set a string
r.set("mykey", "hello")
# This will raise:
# redis.exceptions.ResponseError: WRONGTYPE Operation against a key holding the wrong kind of value
r.hincrby("mykey", "field", 1)
Fix: Ensure you're using HINCRBY only on hash keys.
Error: hash value is not an integer
If the field value is a string that can't be parsed as an integer (e.g., "abc"), HINCRBY fails.
r.hset("myhash", "field", "abc")
# This will raise:
# redis.exceptions.ResponseError: hash value is not an integer
r.hincrby("myhash", "field", 1)
Fix: Only use HINCRBY on fields initialized with numeric strings (or created via HINCRBY itself).
Edge case: Overflow behavior
In Redis 7+, integer values are stored as 64-bit signed integers. If you exceed 2^63 - 1 or go below -2^63, HINCRBY will raise an overflow error. For most use cases this is fine, but be aware for very high-volume counters.
# Attempt overflow (9,223,372,036,854,775,807 max)
r.hset("test", "big", 9223372036854775807)
# This will raise:
# redis.exceptions.ResponseError: increment or decrement would overflow
r.hincrby("test", "big", 1)
Edge case: Negative increments for decrementing
You can pass a negative number to HINCRBY to decrement. There is no separate HDECRBY command — just use a negative increment.
r.hincrby("stats", "errors", -1) # Same as decrement by 1
What you learned & what's next
You now know how to increment hash fields atomically using HINCRBY for integers and HINCRBYFLOAT for floats. You've seen the problems with manual read-update-write cycles, understood the atomicity guarantee through Redis's event-loop model, and practiced with concrete Python examples. You also learned to avoid common pitfalls like type mismatches and overflow.
Key points to remember:
HINCRBYis atomic — safe for concurrent updates- Non-existent fields are created with value 0 before increment
- Use
HINCRBYFLOATfor decimal values; it returns a string - Never mix numeric and non-numeric values in a field you increment
What's next
With hash field increments mastered, you're ready to delete hash fields using HDEL. The next lesson covers removing individual fields and handling cleanup in your data structures. You'll see how deletion complements increment operations to manage the lifecycle of your hash data.
Practice recap
Open your Redis CLI and create a hash called practice:cart with a quantity field. Run HINCRBY practice:cart quantity 3 to add 3 items, then HINCRBY practice:cart quantity -1 to remove one. Finally, use HINCRBYFLOAT to update a subtotal field by 19.99. Verify all values with HGETALL practice:cart.
Common mistakes
- Using HINCRBY on a field that contains a non-numeric string like 'abc' — results in an error: 'hash value is not an integer'. Always initialize numeric fields with
HINCRBYorHSETwith an integer string. - Forgetting that
HINCRBYFLOATreturns a string, not a float. In Python, you must explicitly convert the result withfloat()if you need to use it in arithmetic. - Attempting to increment a key that's not a hash (e.g., a string or list) — raises WRONGTYPE error. Verify the key type with
TYPEbefore incrementing if unsure. - Assuming
HINCRBYworks with negative increments for safe decrement — it does, but be careful: you can accidentally decrement below zero if you don't check the result. UseHINCRBYwith a negative value, but consider checking after if a floor is needed.
Variations
- Use
HINCRBYFLOATinstead ofHINCRBYwhen dealing with decimal numbers like currency or ratings — it handles precision natively but returns a string. - For complex multi-field increments (e.g., increment score and check if player levels up), use a Lua script to perform multiple operations atomically.
- If you need to increment a root-level string key (not a hash field), use
INCRorINCRBYinstead ofHINCRBY— they work the same way but on simple keys.
Real-world use cases
- Tracking page views per article in a blogging platform:
HINCRBY article:123 views 1for each visit, storing all articles in a single hash or one hash per article. - Managing game leaderboards:
HINCRBY player:user1 score 10after each win, then sorting by score usingHGETALLcombined withSORTorHSCAN. - Maintaining user session counters (e.g., login attempts):
HINCRBY user:email failed_logins 1on each failed attempt, then checking against a threshold to lock the account.
Key takeaways
HINCRBYatomically increments an integer hash field, eliminating race conditions from manual read-update-write.- Non-existent fields are auto-created with value 0 before applying the increment.
- Use
HINCRBYFLOATfor floating-point values; it stores the result as a string with full double precision. HINCRBYcan decrement by passing a negative increment — no separateHDECRBYcommand exists.- Always ensure the target field holds a numeric string; non-numeric values cause errors.
- Redis 64-bit integer range applies — exceeding ~9.2 quintillion causes an overflow error.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.