Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

INCR and DECR for Counters

Master Redis atomic counters with INCR and DECR commands. This lesson covers core concepts, step-by-step usage, hands-on exercises, edge cases, and next steps for sequential learning.

Focus: incr and decr for counters

Sponsored

Ever built a page view counter, a rate limiter, or a leaderboard that buckled under concurrent requests? The root cause is usually a non-atomic read-then-write operation — a classic race condition. Redis solves this with INCR and DECR, atomic commands that increment and decrement counters in a single, thread-safe step, guaranteeing correctness even under heavy load.

The problem this lesson solves

Application counters are everywhere: tracking website visits, API call quotas, like counts, or inventory levels. In a multi-threaded or distributed system, a naive counter implementation — GET then SET — can lose updates when two clients read the same value simultaneously, overwrite each other's work, and produce an incorrect total. This is the classic check-then-set race condition. Redis's INCR and DECR commands eliminate this problem by performing the read, increment, and write as one atomic operation, ensuring accuracy without locks or transactions.

Core concept / mental model

Think of a Redis counter as a shared, single-owner scoreboard. The scoreboard starts at zero. Each time you want to add a point, you don't read the current score, add one in your head, and write it back — that would be like two people updating the same whiteboard without talking. Instead, you give the scoreboard a command: "Increase by one." The scoreboard handles reading, incrementing, and writing in one go. INCR does exactly this: it takes a key, increments its value by 1, and returns the new value. DECR decrements by 1. Both commands are atomic, meaning no other client can interfere during the operation.

Atomicity defined

Atomicity in Redis means a command executes completely or not at all, and no other command can execute between its sub-steps. For counters, this guarantees that each INCR or DECR produces a correct, unique result even if thousands of clients issue them at the same instant.

How it works step by step

  1. Create or get a counter key: If the key does not exist, INCR initializes it to 0 before incrementing, so you never need to call SET key 0 first.
  2. Issue INCR or DECR: Redis receives the command, reads the current integer value, adds or subtracts 1, writes the new value to the key, and returns it — all as one atomic unit.
  3. Use the returned value: The command returns the new value, which you can display, log, or use in your application logic.
  4. Handle overflow or underflow: Redis counters are stored as 64-bit signed integers, so overflows wrap around — but for most counters, this is not a practical concern.

Hands-on walkthrough

Let's build a simple page view counter. Open redis-cli and follow along.

Example 1: Basic INCR

> INCR pageviews
(integer) 1
> INCR pageviews
(integer) 2
> INCR pageviews
(integer) 3

Output: Each call returns the incremented value. No GET required.

Example 2: DECR for decrementing

> SET stock 100
OK
> DECR stock
(integer) 99
> DECR stock
(integer) 98

Output: Decrements from 100 down to 98. Useful for inventory or rate limit tokens.

Example 3: INCRBY and DECRBY for custom steps

> INCRBY points 50
(integer) 50
> INCRBY points 30
(integer) 80
> DECRBY points 20
(integer) 60

Output: Increment or decrement by any integer, not just 1.

Example 4: INCR with expiration (rate limiter)

> INCR api:user:42
(integer) 1
> EXPIRE api:user:42 60
(integer) 1
> INCR api:user:42
(integer) 2
> TTL api:user:42
(integer) 55

Explanation: Combine INCR with EXPIRE to create a sliding window counter — useful for rate limiting. Each new key expires after 60 seconds.

Pro Tip: Always set an expiration if you're using INCR for rate limiting. Otherwise, the key persists forever and consumes memory.

Compare options / when to choose what

Command Behavior Use case
INCR Increment by 1 Simple counters (page views, likes, retweets)
DECR Decrement by 1 Countdowns, inventory, rate limit token release
INCRBY Increment by N Scores, points, bulk additions
DECRBY Decrement by N Bulk removals, refunds, rollbacks
GET + SET (non-atomic) Separate read and write Only when you need custom logic between read and write (rarely safe)

In most counter scenarios, INCR / INCRBY is the correct choice because atomicity ensures correctness. Avoid GET + SET unless you fully understand race conditions and can accept incorrect counts.

Troubleshooting & edge cases

Key doesn't exist

INCR and DECR treat a missing key as 0. No error — the command succeeds.

> EXISTS nonexistent
(integer) 0
> INCR nonexistent
(integer) 1

Key holds a non-integer value

If the key contains a string that cannot be parsed as a 64-bit integer, Redis returns an error.

> SET mystring "hello"
OK
> INCR mystring
(error) ERR value is not an integer or out of range

Fix: Always ensure the key stores an integer. Use TYPE key to check; DEL key to reset.

Overflow

INCR and DECR operate on signed 64-bit integers (max ~9.22e18). Exceeding this wraps the value, but this is extremely rare for most counters.

Race conditions with other operations

While INCR/DECR are atomic, combining them with non-atomic READ + WRITE steps (e.g., GET then INCR) can still create bugs. Always use INCR directly.

What you learned & what's next

You now understand why atomic counters matter, how INCR and DECR work under the hood, and how to apply them for real-world use cases like page views, inventory, and rate limiting. You also learned about INCRBY / DECRBY for custom increments and how to pair INCR with EXPIRE for time-bound counters.

You've mastered the core idea behind INCR and DECR for counters and completed a practical exercise. These commands are foundational to building performant, race-condition-free systems — whether you're tracking visits, quotas, or leaderboard scores.

Next, you'll explore SETNX and locking patterns, where atomic operations help implement distributed locks and prevent duplicate work.

Practice recap

Open redis-cli and build a counter that increments by 10 for each visitor, but resets every hour. Use INCRBY and EXPIRE. Then simulate concurrent requests by opening two redis-cli sessions and running INCR simultaneously — observe that values never get lost. This reinforces atomicity in action.

Common mistakes

  • Using GET then SET instead of INCR: This creates a race condition that loses increments under concurrent requests.
  • Forgetting to set TTL on rate-limit counters, causing keys to consume memory indefinitely.
  • Assuming INCR works on non-integer keys leads to an error; always verify the key stores a number.
  • Using DECR for negative counters instead of INCR with negative INCRBY — both work, but INCRBY is clearer for large step sizes.

Variations

  1. Use INCRBY with negative values for decrementing without DECR (e.g., INCRBY stock -5).
  2. Combine INCR with EXPIRE for sliding window rate limiting — a common pattern in API gateways.
  3. Use Lua scripting (EVAL) for complex atomic operations that go beyond single-key INCR, such as incrementing two counters atomically.

Real-world use cases

  • Tracking real-time page views on a high-traffic news website, where each request increments a Redis counter without database load.
  • Implementing a per-IP rate limiter that increments a key with a 60-second TTL, rejecting requests when the counter exceeds a threshold.
  • Maintaining a game leaderboard where player scores are incremented via INCRBY on each win, providing instant updates to all clients.

Key takeaways

  • INCR and DECR are atomic — they eliminate the race condition inherent in GET-then-SET patterns.
  • INCR initializes missing keys to 0 automatically, so you never need to SET a key before incrementing.
  • Use INCRBY and DECRBY for steps other than 1; they accept any signed integer.
  • Pair INCR with EXPIRE for time-based counters like rate limiters or daily usage quotas.
  • Always ensure the key stores an integer; otherwise, Redis returns an error.
  • Overflow is possible (64-bit signed integer), but rarely occurs in practical applications.

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.