Skip to content

Memcached - Incr/Decr

Memcached provides incr and decr commands for atomically modifying numeric values stored as strings. ‘Atomic’ means the operation is performed as a single, indivisible step, preventing race conditions without needing more complex ‘check-and-set’ (CAS) logic. This makes them ideal for implementing high-performance counters, rate limiters, or simple leaderboards.

The incr command increases the numeric value of an existing key by the specified amount. The value stored in Memcached must be a string representation of an unsigned 64-bit integer.

incr key increment_value

In this example, we first set a key visitors to a numeric value. Then we use incr to increase it.

# First, set an initial numeric value. The value must be on its own line.
set visitors 0 900 2
10
STORED
# Get the current value
get visitors
VALUE visitors 0 2
10
END
# Increment the value by 5
incr visitors 5
15
# Verify the new value
get visitors
VALUE visitors 0 2
15
END

The decr command decreases the numeric value. An important rule is that decr will never decrease a value below 0.

decr key decrement_value
# Assuming the 'visitors' key is 15 from the previous example
decr visitors 5
10
get visitors
VALUE visitors 0 2
10
END
# Attempting to decrement below zero stops at 0
decr visitors 20
0
get visitors
VALUE visitors 0 1
0
END

In application code, a common challenge is initializing a counter. If multiple clients try to set the initial value at the same time, it can create a race condition. Modern Memcached clients solve this by providing a version of incr that atomically initializes the counter if it doesn’t exist. This is the recommended best practice.

Ensure you have the spymemcached client in your pom.xml:

<dependency>
<groupId>net.spy</groupId>
<artifactId>spymemcached</artifactId>
<version>2.12.3</version>
</dependency>
import net.spy.memcached.MemcachedClient;
import java.net.InetSocketAddress;
public class IncrDecrExample {
public static void main(String[] args) {
MemcachedClient mcc = null;
try {
mcc = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
System.out.println("Connection to server successful.");
String counterKey = "page_visits";
// For a clean test, let's delete the key if it exists
mcc.delete(counterKey);
// 1. Atomically initialize and increment the counter.
// The method signature is incr(key, by, defaultValue, expiration).
// If 'page_visits' doesn't exist, it will be created with defaultValue (1) and then incremented by 'by' (1), resulting in 1.
// Note: Some clients might directly set the default value. Spymemcached sets it to defaultValue and then performs the operation.
// For spymemcached, a better initial call is incr(key, 1, 1), which sets to 1 if not exists.
long firstVisit = mcc.incr(counterKey, 1, 1, 3600); // Initialize with 1, expire in 1 hr
System.out.println("After first increment (and initialization): " + firstVisit);
// 2. Increment the existing counter
long secondVisit = mcc.incr(counterKey, 1, 1, 3600);
System.out.println("After second increment: " + secondVisit);
// 3. Increment by a larger value
long multiVisit = mcc.incr(counterKey, 5, 1, 3600);
System.out.println("After incrementing by 5: " + multiVisit);
// 4. Decrement the counter
long decrementedValue = mcc.decr(counterKey, 2, 0, 3600);
System.out.println("After decrementing by 2: " + decrementedValue);
} catch (Exception e) {
System.err.println("An error occurred: " + e.getMessage());
} finally {
if (mcc != null) {
mcc.shutdown();
}
}
}
}
Connection to server successful.
After first increment (and initialization): 1
After second increment: 2
After incrementing by 5: 7
After decrementing by 2: 5

Non-numeric values: Trying to incr or decr a key that holds a non-numeric string (e.g., “hello”) will result in an error from the server. The client library will typically throw an exception.

Non-existent keys (without a default): If you use a simple incr(key, by) on a key that doesn’t exist, the operation will fail (returning -1 in spymemcached). This is why using the version with a default value is the recommended best practice to avoid race conditions on initialization.

incr/decr are highly performant and commonly used for:

  • API Rate Limiting: Tracking the number of API calls a user has made in a specific time window.
  • View/Download Counters: Counting how many times an article has been read or a file has been downloaded.
  • Real-time Statistics: Aggregating simple, non-critical statistics without hitting a database on every event.