Memcached - Incr/Decr
Memcached - Atomic Increment & Decrement
Section titled “Memcached - Atomic Increment & Decrement”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.
Syntax - incr
Section titled “Syntax - incr”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_valueCommand Line Example
Section titled “Command Line Example”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 210STORED
# Get the current valueget visitorsVALUE visitors 0 210END
# Increment the value by 5incr visitors 515
# Verify the new valueget visitorsVALUE visitors 0 215ENDSyntax - decr
Section titled “Syntax - decr”The decr command decreases the numeric value. An important rule is that decr will never decrease a value below 0.
decr key decrement_valueCommand Line Example
Section titled “Command Line Example”# Assuming the 'visitors' key is 15 from the previous exampledecr visitors 510
get visitorsVALUE visitors 0 210END
# Attempting to decrement below zero stops at 0decr visitors 200
get visitorsVALUE visitors 0 10ENDAtomic Counters with a Modern Java Client
Section titled “Atomic Counters with a Modern Java Client”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.
Prerequisites: Maven Dependency
Section titled “Prerequisites: Maven Dependency”Ensure you have the spymemcached client in your pom.xml:
<dependency> <groupId>net.spy</groupId> <artifactId>spymemcached</artifactId> <version>2.12.3</version></dependency>Java Example
Section titled “Java Example”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(); } } }}Output
Section titled “Output”Connection to server successful.After first increment (and initialization): 1After second increment: 2After incrementing by 5: 7After decrementing by 2: 5Common Errors and Best Practices
Section titled “Common Errors and Best Practices”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.
Practical Use Cases
Section titled “Practical Use Cases”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.