Chapter 03 · Strings, Counters, Bitmaps, Bitfields, and Binary Values

INCR / DECR / INCRBYFLOAT: Counters, Quotas, Sequencing, and Overflow/Precision Awareness

Build atomic counters with exact 64-bit integer boundaries, understand floating-point normalization and precision, and reason about quotas, sequencing, retries, persistence, and failover.

Intermediate120–150 minutesCounter + precision labRedis Open Source 8.10.1Free/local-firstLast reviewed: September 6, 2026

Learning outcomes

AtlasMart needs page-view counters, inventory reservation attempts, per-customer quotas, and monotonic-looking internal sequence numbers. Teams often reach for INCR because it is atomic and fast, then accidentally assume the stored value has a numeric data type, that counters cannot overflow, that retries cannot double-count, or that INCRBYFLOAT is exact enough for money. Redis provides precise scalar operations; the application still owns the meaning.

01

Explain how INCR/DECR/INCRBY interpret String bytes as base-10 signed 64-bit integers and what happens for missing, non-numeric, wrong-type, and overflow cases.

02

Prove that in-place numeric mutation keeps an existing key TTL while replacement with SET can remove it.

03

Use INCRBYFLOAT with an explicit model of double parsing, normalized stored output, and precision limits; reject it for exact financial arithmetic.

04

Distinguish an atomic counter from a gapless sequence, an idempotent event count, a durable monotonic identifier, or a globally ordered distributed log.

05

Build bounded quota/counter fixtures and design retry, persistence, replication, Cluster, ACL, and observability requirements around them.

Exact lab baseline

All Chapter 03 labs reuse the disposable Chapter 01 environment: Redis Open Source 8.10.1 from Docker Official Image redis:8.10.1, container atlasmart-redis-ch01, standalone topology, host publication 127.0.0.1:6379, TLS disabled only because traffic stays on loopback, default user disabled, named ACL users atlasmart-app and academy-admin, logical database 0, AOF with appendfsync everysec plus RDB snapshots, persistent /data volume, and no explicit Redis maxmemory limit/eviction policy. The application ACL is restricted to ~atlasmart:* and normal read/write/connection categories. Search, JSON, vector, time-series, and probabilistic features are not required. No financial/customer production data is used. Boundary tests use disposable keys; integer overflow commands are expected to error without changing the stored value.

1. Redis has no separate integer type

INCR, DECR, INCRBY, and DECRBY are String commands. If a key is absent, Redis treats its starting numeric value as zero. If a String cannot be represented as a signed 64-bit integer, Redis returns an error. If the key contains another Redis data type, the command also errors. The successful result is stored back in the String value and returned as an integer reply.

redis-cli · integer counter state and parsing
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app UNLINK atlasmart:ch03:counter:ordersdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCR atlasmart:ch03:counter:ordersdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCRBY atlasmart:ch03:counter:orders 9docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DECR atlasmart:ch03:counter:ordersdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GET atlasmart:ch03:counter:ordersdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TYPE atlasmart:ch03:counter:orders# Expected: 1, 10, 9, "9", and type "string".

This is useful because the server performs read-modify-write atomically for one key. Two clients issuing INCR do not both read the same old value and overwrite each other. The property belongs to the Redis command; it does not make the calling business workflow atomic with an external database, payment provider, or message broker.

2. Existing TTL survives INCR; SET replacement does not

Redis expiration is attached to the key. Commands that mutate the value in place, such as INCR, leave the timeout intact. A replacement with plain SET removes it. That difference is central for fixed-window counters and temporary quotas.

redis-cli · counter mutation versus value replacement
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch03:counter:ttl 5 EX 120docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TTL atlasmart:ch03:counter:ttldocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCR atlasmart:ch03:counter:ttldocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TTL atlasmart:ch03:counter:ttl# -> still positive.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch03:counter:ttl 99docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TTL atlasmart:ch03:counter:ttl# -> -1 because SET replaced the value/lifecycle.

A counter policy must therefore state which commands are allowed. “This counter has a TTL” is not a permanent property if arbitrary writers may replace the key.

3. Signed 64-bit boundaries are observable, and overflow is an error

Integer operations are limited to signed 64-bit values: from −9,223,372,036,854,775,808 through 9,223,372,036,854,775,807. Redis does not silently wrap INCR/DECR at these boundaries. An overflowing command errors and leaves the existing value unchanged.

redis-cli · safe boundary failure injection
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch03:counter:max 9223372036854775807docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCR atlasmart:ch03:counter:maxdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GET atlasmart:ch03:counter:max# -> error, then the maximum value is still stored.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch03:counter:min -9223372036854775808docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app DECR atlasmart:ch03:counter:mindocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GET atlasmart:ch03:counter:min# -> error, then the minimum value is still stored.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch03:counter:notnum tendocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCR atlasmart:ch03:counter:notnum# -> integer/parsing error; the String "ten" remains.

This failure injection is safe because it allocates only tiny values. In a real system, alert before a counter approaches a domain limit; do not rely on the final overflow error as a capacity plan.

4. INCRBYFLOAT is convenient arithmetic, not exact money

INCRBYFLOAT parses the existing String and increment as floating-point numbers, stores a normalized decimal String, and returns that String. Current Redis documentation states that output precision is fixed at 17 digits after the decimal point regardless of internal computation precision. This is not a decimal-currency contract.

redis-cli · floating-point behavior and normalization
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch03:metric:ratio 10.50docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCRBYFLOAT atlasmart:ch03:metric:ratio 0.1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCRBYFLOAT atlasmart:ch03:metric:ratio -5docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GET atlasmart:ch03:metric:ratio# Redis stores a normalized decimal representation with trailing zeroes removed.

For exact monetary totals, use an integer minor unit such as cents only if the currency/business domain truly fits that model, or use an authoritative decimal-capable datastore/application arithmetic with explicit scale and rounding. A counter named revenue_dollars incremented by binary floating values is vulnerable to representation and policy errors even when every Redis command succeeds.

Replication/AOF detail

Redis documents that successful INCRBYFLOAT operations are propagated to replication and the Append Only File as a SET operation, reducing cross-platform floating-point divergence in replay. That implementation detail does not make the arithmetic suitable for financial invariants.

5. Atomic counter does not mean exactly-once event count

Suppose a client sends INCR atlasmart:orders:accepted, Redis executes it, but the TCP connection breaks before the reply reaches the caller. The client cannot infer from the timeout whether the increment happened. Blindly retrying may produce a second increment. This is the classic ambiguous outcome of a non-idempotent operation.

Likewise, INCR can generate unique values on one current primary, but gaps are normal when a client reserves an ID and aborts, and rollback/failover/durability behavior depends on persistence and replication state. Do not advertise it as a gapless sequence or a globally durable order unless your architecture proves those stronger properties.

Need Can INCR help? What else is required
Approximate operational event counter Yes Retry/error accounting; reconciliation if exactness matters.
Per-key atomic quota count Yes Window/lifecycle policy and idempotency of caller actions.
Unique local sequence value Often Gap tolerance, persistence/failover analysis, overflow budget.
Exact money balance No as sole primitive Decimal/ledger invariants and transactional domain design.
Exactly-once cross-system event count No Deduplication/idempotency and authoritative event/state reconciliation.

6. Deliberately wrong quota: floating money and retry-blind increments

An AtlasMart prototype charges a customer by running INCRBYFLOAT balance 0.10 and retries on every timeout. Two distinct mistakes are mixed together: floating-point arithmetic is being used as an exact ledger, and a non-idempotent increment is retried without an operation identity.

The repair is architectural. Keep financial truth in a ledger designed for exact decimal/transactional semantics. If Redis mirrors a derived metric, label it as derived and rebuildable. For event counters that must not double-apply, pair the event identity with an idempotency mechanism rather than assuming retry means “the first command definitely failed.” Chapter 13 introduces WATCH/transactions and Chapter 23 revisits idempotency patterns.

7. Hands-on lab: counters, quota state, and boundary evidence

This lab creates a fixed-lifetime synthetic API counter and separate integer/floating boundary fixtures. It does not simulate payment state. Because an exact “increment and create TTL only on first increment” primitive varies with newer Redis features and scripting choices, this lesson uses a pre-created counter with an explicit TTL so the core semantics remain transparent.

shell · bounded counter fixture
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app UNLINK atlasmart:ch03:l2:quota atlasmart:ch03:l2:seq atlasmart:ch03:l2:ratiodocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch03:l2:quota 0 EX 300 NXdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCR atlasmart:ch03:l2:quotadocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCRBY atlasmart:ch03:l2:quota 4docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TTL atlasmart:ch03:l2:quotadocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCR atlasmart:ch03:l2:seqdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCR atlasmart:ch03:l2:seqdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app INCRBYFLOAT atlasmart:ch03:l2:ratio 0.125docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MGET atlasmart:ch03:l2:quota atlasmart:ch03:l2:seq atlasmart:ch03:l2:ratiodocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin MEMORY USAGE atlasmart:ch03:l2:quota# Cleanup only this lesson.docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app UNLINK atlasmart:ch03:l2:quota atlasmart:ch03:l2:seq atlasmart:ch03:l2:ratio atlasmart:ch03:counter:max atlasmart:ch03:counter:min atlasmart:ch03:counter:notnum atlasmart:ch03:counter:ttl atlasmart:ch03:metric:ratio atlasmart:ch03:counter:orders

Verification checklist:

  • The integer counter is a Redis String and the quota TTL remains positive after INCR/INCRBY.
  • Overflow and non-numeric tests produce errors without silently wrapping or replacing the stored value.
  • You can state the signed 64-bit integer range and explain why capacity planning must stay far inside it.
  • You can explain why INCRBYFLOAT is not an exact currency primitive and why retries make non-idempotent increments ambiguous.
  • You recorded persistence mode and standalone topology before making any durability/ordering claim.

8. Production judgment

Use Redis counters when atomic scalar mutation is a good fit and when the counter's business semantics tolerate Redis's persistence, replication, retry, and failover model. Track counter cardinality, update rate, hot-key concentration, value range, TTL distribution, command error rates, client timeouts/retries, AOF/replication bandwidth, and p50/p95/p99 latency under realistic concurrency. A single hot global counter may serialize a large fraction of workload even though each INCR is O(1).

In Cluster, a single counter is one key in one hash slot, so all traffic for that counter reaches its owning shard. Sharding the metric can reduce hot-key pressure but changes read/aggregation semantics. ACLs should grant only the commands/key patterns the application needs. Rollback/migration plans must preserve the numeric encoding and bounds expected by old and new clients.

9. Summary and next step

Redis integer counters are String values interpreted as signed 64-bit base-10 numbers and changed atomically per command. Overflow and parse errors are explicit. In-place increments preserve TTL. INCRBYFLOAT stores normalized floating-point results but is not exact decimal money. Atomic increments do not make retries exactly once or sequences gapless/durable by themselves. Next, you will treat Strings as larger byte arrays: fetching many keys, slicing ranges, appending data, and storing arbitrary binary payloads while measuring memory and network consequences.

Check your understanding

  1. What type does TYPE report for a key modified with INCR?
  2. Does INCR remove an existing TTL?
  3. What happens when INCR would exceed 9,223,372,036,854,775,807?
  4. Why is INCRBYFLOAT unsuitable as an exact money ledger?
  5. Why can retrying INCR after a timeout double-count?
Review the answers

1. string; Redis has no separate integer data type.

2. No. It mutates the value in place and leaves the existing timeout intact.

3. Redis returns an overflow error and does not silently wrap the integer counter.

4. It uses floating-point parsing/computation/output semantics rather than an explicit decimal scale/rounding ledger contract.

5. The server may have executed the first increment even if the reply was lost, so the retry is a second non-idempotent operation.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.