cmd_get, cmd_set, and cmd_touch are cumulative counters in memcached. They increase monotonically from process start and are meaningless as raw numbers. The signal is the derived rate: the delta between two samples divided by the interval. A sudden change in that rate is almost never the root cause. It is a proxy for something that changed upstream (clients stopped or started sending traffic), inside the cache (a hot key expired, the cache was flushed), or in the application logic (a write loop, a deploy with new key patterns).
Command rate anomalies are detection signals, not diagnoses. A spike above 2x baseline or a drop below 10-50% tells you something shifted. The investigation correlates the rate change with hit ratio, eviction rate, connection state, and backend load to find the cause.
One subtlety that catches teams off guard: cmd_get counts a multiget as one command, not one per key. A single multiget for 50 keys increments cmd_get by 1, but get_hits and get_misses reflect the actual key-level lookups. A flat cmd_get rate can hide a massive change in key-level traffic if the multiget batch size changed. The real lookup volume is always get_hits + get_misses.
Reading command rates
These counters come from the stats command. There is no native rate metric from the server; sample twice with a known interval and compute the delta.
# Compute cmd_get rate over a 5-second window
A=$(echo "stats" | nc -w 2 localhost 11211 | awk '/STAT cmd_get/ {print $3}')
sleep 5
B=$(echo "stats" | nc -w 2 localhost 11211 | awk '/STAT cmd_get/ {print $3}')
echo "cmd_get rate: $(( (B - A) / 5 )) ops/sec"
Anomaly thresholds, drawn from operational consensus rather than a hard server limit:
| Direction | Threshold | What it usually means |
|---|---|---|
| Spike | Greater than 2x baseline sustained over 10+ minutes | Retry storm, cache stampede, traffic surge, or application write loop |
| Drop | Below 10% of baseline | Cache is unreachable or clients stopped sending traffic |
| Drop | Below 50% of baseline, sustained | Upstream failure, DNS change, or client misconfiguration worth investigating |
These are heuristics. The real value is anomaly detection against a time-of-day and day-of-week baseline. A cache handling 500K gets/sec with 100 evictions/sec may be perfectly healthy. A cache at 1K gets/sec with the same eviction rate is in crisis. Absolute thresholds without baselines generate false positives and miss real problems.
flowchart TD
A[Command rate anomaly] --> B{Spike or drop?}
B -->|Spike| C{cmd_get or cmd_set?}
B -->|Drop| D{Connections at max?}
C -->|cmd_get up, misses up| E[Cache stampede]
C -->|cmd_get up, hit ratio stable| F[Traffic surge or retry storm]
C -->|cmd_set up, cmd_get flat| G[Write loop or bulk load]
D -->|accepting_conns=0| H[Connection exhaustion]
D -->|uptime low or cmd_flush up| I[Restart or flush_all]
D -->|Stable connections and uptime| J[Upstream failure or DNS change]Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Cache stampede | cmd_get spikes, get_misses spikes, evictions low or zero, backend load spikes | Hit ratio direction and backend load |
| Client retry storm | cmd_get spikes, conn_yields or rejected_conns may climb, hit ratio stable | Client-side error rates and connection state |
| Application write loop | cmd_set spikes without proportional cmd_get spike, evicted_unfetched climbing | Recent deploys and set/get ratio |
| Connection exhaustion | cmd_get and cmd_set drop, curr_connections at max, accepting_conns = 0 | accepting_conns and curr_connections / max_connections |
| Upstream failure or DNS change | cmd_get drops to near zero, connections and uptime stable, process responsive | Client server lists and DNS resolution |
| Restart (uptime reset) | cmd_get and cmd_set drop, uptime near zero, curr_items at zero | uptime counter |
Accidental flush_all | cmd_flush incremented, hit ratio plummets, get_flushed spikes | cmd_flush counter |
Quick checks
Run these read-only probes to classify the anomaly. All are safe for production. Avoid polling stats more frequently than every 10 seconds under extreme throughput (above 500K ops/sec); the stats command itself adds load.
# Command counters
echo "stats" | nc -w 2 localhost 11211 | grep -E "STAT cmd_(get|set|touch|flush)"
# Hit and miss breakdown (key-level lookup volume)
echo "stats" | nc -w 2 localhost 11211 | grep -E "STAT get_(hits|misses|flushed)"
# Uptime for restart detection
echo "stats" | nc -w 2 localhost 11211 | grep "STAT uptime"
# Connection state and rejection counters
echo "stats" | nc -w 2 localhost 11211 | grep -E "STAT (curr_connections|max_connections|rejected_conns|accepting_conns|listen_disabled_num)"
# Eviction rate and memory pressure
echo "stats" | nc -w 2 localhost 11211 | grep -E "STAT (evictions|bytes |limit_maxbytes)"
# Network throughput (NIC saturation from large-value reads)
echo "stats" | nc -w 2 localhost 11211 | grep -E "STAT bytes_(read|written)"
# CPU usage
echo "stats" | nc -w 2 localhost 11211 | grep "STAT rusage"
# Connection yields (one client dominating the server)
echo "stats" | nc -w 2 localhost 11211 | grep "STAT conn_yields"
# Item count (mass expiration or flush detection)
echo "stats" | nc -w 2 localhost 11211 | grep "STAT curr_items"
How to diagnose it
Confirm the direction and magnitude. Sample
cmd_getandcmd_settwice with a known interval. Determine whether the anomaly is a spike (above 2x baseline) or a drop (below 10-50% of baseline). Compare against your time-of-day baseline, not an absolute number.If it is a spike in
cmd_get, check hit ratio direction. Pullget_hitsandget_missesand compute the ratio from deltas. Ifget_missesis climbing whileget_hitsstays flat, you have a cache stampede: a hot key expired or was evicted and every client is missing simultaneously. Ifget_hitsis climbing proportionally withget_misses, traffic genuinely increased. See Memcached cache stampede: a hot key expires and the backend takes the hit.If it is a spike in
cmd_setwithout a proportionalcmd_getspike, look for a write loop. Checkevicted_unfetchedandexpired_unfetched. If these are climbing alongside thecmd_setspike, the application is caching data nobody reads. Correlate with recent deploys.If it is a drop, check connections first. Look at
accepting_conns,curr_connections,max_connections, andrejected_conns. Ifaccepting_conns = 0orrejected_connsis incrementing, the cache hit its connection limit and clients are being turned away. See Memcached connection limit reached: accepting_conns=0 and clients being refused.Check
uptimeandcmd_flush. Ifuptimereset, the process restarted and the cache is cold. Ifcmd_flushincremented, someone or something issuedflush_all. Both produce a temporarycmd_getandcmd_setdrop followed by a cold-cache pattern: highget_misses, climbingcurr_itemsas the cache warms, and backend load spike. See Memcached flush_all: the accidental cache wipe and its cold-start blast radius.If connections are stable and the process is responsive but traffic stopped, investigate upstream. A
cmd_getdrop to near zero with stablecurr_connections, stableuptime, and a responsiveversionprobe means the server is fine but clients stopped sending. Check client-side server lists, DNS resolution, and load balancer configuration. A DNS change or a stray space character in a server name in client config can silently redirect traffic.Correlate with backend load. A
cmd_getspike with rising backend load and risingget_missesconfirms a stampede. Acmd_getdrop with rising backend load suggests clients are bypassing the cache and hitting the database directly. Acmd_getspike with stable backend load means the cache is absorbing the increase.Check
conn_yieldsif one client seems to dominate. Highconn_yieldsduring acmd_getspike means a single connection is pipelining requests faster than the server’s-Rlimit (default 20 requests per event) allows. The server forces it to yield so other connections get served. See Memcached conn_yields rising: one client’s pipeline starving the others.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
cmd_get rate | Read throughput baseline | Spike greater than 2x or drop below 10% of baseline sustained |
cmd_set rate | Write throughput; detects write loops and bulk loading | Spike without proportional cmd_get spike |
get_hits + get_misses | Actual key-level lookups (multiget-aware) | Diverges from cmd_get rate trend |
Hit ratio (get_hits / (get_hits + get_misses)) | Cache effectiveness | Sudden drop with cmd_get spike indicates stampede |
evictions | Memory pressure | Low with cmd_get spike confirms stampede, not memory exhaustion |
curr_connections, accepting_conns | Connection health | accepting_conns = 0 with rate drop indicates connection exhaustion |
cmd_flush | Cache wipe detection | Any increment in production requires investigation |
uptime | Restart detection | Reset to near zero means cold cache |
conn_yields | Client fairness | High sustained rate indicates one aggressive client |
bytes_written | Network throughput | Disproportionate to cmd_get rate means large-value reads |
rusage_user, rusage_system | CPU pressure | Spike with cmd_get spike confirms processing load |
Fixes
Cache stampede (cmd_get spike with rising misses)
Identify the hot key through application telemetry if possible. Manually warm the key to reduce the miss storm. For long-term prevention, implement stampede protection in the application layer: distributed locks around cache regeneration, probabilistic early expiration, or stale-while-revalidate patterns. The tradeoff is added application complexity and a small latency cost on the first miss after expiration. See Memcached cache stampede: a hot key expires and the backend takes the hit.
Client retry storm (cmd_get spike with stable hit ratio)
Identify which client is generating the traffic. Check client-side error rates, retry counts, and backoff configuration. A common cause is a client library retrying failed operations with no backoff, amplifying a transient blip into a sustained spike. Reduce retry aggressiveness or add exponential backoff. The tradeoff: reducing retries may increase latency for genuine transient failures.
If conn_yields is high alongside the spike, one client is pipelining aggressively. Check whether the -R limit (default 20) is appropriate for your workload, or throttle the client-side pipeline depth.
Application write loop (cmd_set spike without cmd_get spike)
High evicted_unfetched and expired_unfetched confirm the application is writing data nobody reads. Correlate with recent deploys to identify the code path. Common causes: cache-aside logic that writes to cache after every database write regardless of read demand, background jobs populating keys that are never requested, or a serialization change producing oversized items. Fix at the application level: only cache data that is actually read, and review TTLs for write-heavy patterns. See Memcached evicted_unfetched and expired_unfetched: caching data nobody ever reads.
Connection exhaustion (cmd_get and cmd_set drop, accepting_conns=0)
Verify max_connections and the OS ulimit -n for the memcached process. If curr_connections is stable at max but total_connections rate is very high, the problem is connection churn (missing pooling), not a leak. If curr_connections is monotonically increasing, it is a leak. Increasing -c requires a restart. Fix the root cause first: implement or fix client-side connection pooling. See Memcached connection limit reached: accepting_conns=0 and clients being refused and Memcached curr_connections climbing: connection leaks and missing pooling.
Upstream failure or DNS change (cmd_get drop, connections stable)
The cache is healthy but clients stopped sending traffic. Check client-side server lists, DNS resolution, and load balancer health. A DNS change pointing clients to a different IP, a misconfigured server name (including whitespace), or a load balancer that removed the memcached node from its pool all produce this pattern. Verify with ss -tn | grep :11211 to see which clients are actually connected. The fix is client-side configuration. Note that client-side DNS caching can delay detection of legitimate changes.
Restart or flush_all (uptime reset or cmd_flush increment)
After a restart or flush_all, the cache is cold. Focus on the backend: it is now receiving the full production request load the cache was absorbing. Monitor hit ratio recovery and backend load. Run cache-warming scripts if available. Implement circuit breakers in the application layer if the backend cannot absorb the cold-cache load. Do not restart memcached as a fix unless there is a confirmed process-level issue. See Memcached flush_all: the accidental cache wipe and its cold-start blast radius.
Prevention
- Baseline by time-of-day and day-of-week. Command rates are workload-driven and follow predictable patterns. Alert on deviation from baseline, not absolute thresholds.
- Alert on rate-of-change, not raw counters. The counters are cumulative. The useful signal is the derivative.
- Track
get_hits + get_missesalongsidecmd_get. Multiget batching hides key-level traffic changes. - Implement stampede prevention. Distributed locks, probabilistic early expiration, or stale-while-revalidate at the application layer prevent the most common
cmd_getspike pattern. - Alert on any
cmd_flushincrement in production. A single accidentalflush_allcauses a cold-cache thundering herd. - Monitor connection state alongside command rates. A command rate drop caused by connection exhaustion looks identical to a client-side failure until you check
accepting_connsandrejected_conns. - Track
evicted_unfetchedandexpired_unfetched. These reveal write-only caching patterns that waste memory and can mask as acmd_setanomaly.
How Netdata helps
- Per-second rate derivation. Netdata computes
cmd_get,cmd_set, andcmd_touchrates from counter deltas automatically, exposing sub-minute anomalies that 10-15 second polling intervals miss. - ML anomaly detection on command rates. Netdata’s anomaly advisor learns the time-of-day baseline for each command type and flags deviations without manual threshold tuning.
- Correlated hit ratio and miss breakdown. When
cmd_getspikes,get_hits,get_misses, and hit ratio appear in the same view, making stampede detection immediate. - Connection state alongside throughput.
accepting_conns,curr_connections,rejected_conns, andconn_yieldsappear alongside command rates, so a drop caused by connection exhaustion is distinguishable from a client-side failure in one view. - Flush and restart detection.
cmd_flushincrements anduptimeresets surface as discrete events, making cold-cache incidents immediately classifiable.
Related guides
- Memcached cache stampede: a hot key expires and the backend takes the hit
- Memcached evicted_unfetched and expired_unfetched: caching data nobody ever reads
- Memcached cas_badval climbing: check-and-set contention and lost updates
- Memcached conn_yields rising: one client’s pipeline starving the others
- Memcached connection churn: total_connections racing and TIME_WAIT buildup
- Memcached curr_connections climbing: connection leaks and missing pooling
- Memcached connection limit reached: accepting_conns=0 and clients being refused
- Memcached connection refused: telling a dead process from a hung or full one
- Memcached evicted_time low: distinguishing healthy turnover from cache thrash
- Memcached eviction cascade: when a full cache overloads the backend
- Memcached evictions climbing: the cache is full and discarding live data
- Memcached flush_all: the accidental cache wipe and its cold-start blast radius






