Finding Expensive Valkey Commands and Data Structures
Content reflects the state as of February 2026. AI tooling and model capabilities in this area change frequently.
A single $O(N)$ command in Valkey can stall thousands of concurrent client requests, yet identifying the offending key or query pattern must never rely on invasive commands like KEYS * or unmetered memory scans. Triage requires extracting differential command statistics, analyzing sampled slowlogs, and inspecting data structure encodings safely.
Technology and product capabilities in this series are evaluated as of June 30, 2026.
Situation
When an application experiences tail latency spikes on an in-memory datastore like Valkey (versions 7.2 and 8.0), host-level metrics frequently show a single CPU core pinned at 100% while overall system memory remains stable. Because Valkey executes data-manipulation commands on a single main event loop, any command that takes 5 milliseconds to execute introduces an immediate 5-millisecond queueing delay for every subsequent operation in the pipeline.
Valkey provides built-in telemetry to diagnose execution bottlenecks: INFO commandstats, SLOWLOG, LATENCY DOCTOR, LATENCY HISTORY, MEMORY USAGE, CLIENT LIST, and internal encoding statistics. However, naive diagnostic queries—such as executing KEYS *, unthrottled --bigkeys scans, or running MONITOR on high-throughput nodes—amplify the incident and can cause complete service outages.
The diagnostic objective is to collect deterministic execution metrics, calculate rate-of-change deltas across command families, and use an LLM to formulate and rank testable hypotheses regarding algorithmic complexity, hot-key contention, and collection anti-patterns without exposing sensitive payload data or running unsafe operations in production.
The Problem
Valkey’s speed relies on sub-microsecond in-memory operations. When performance degrades due to workload patterns, the root cause usually falls into one of five distinct categories:
- Algorithmic Complexity Traps: Running $O(N)$ or $O(M \log N)$ commands where $N$ is the total keyspace or collection size (such as
KEYS, unboundedHGETALL,SMEMBERS,SUNION, orZRANGEBYSCOREon large sorted sets). - Big Keys and Memory Serialization: Keys holding hundreds of megabytes of nested data structures require proportional CPU time to serialize across the network socket and allocate memory, blocking the main thread during both retrieval and deletion.
- Hot-Key Access Skew: A single popular cache key accessed by thousands of concurrent application threads saturates single-core throughput and fills network socket buffers, regardless of cluster size or shard count.
- Blocking Lua Scripts and Functions: Atomic execution of Lua scripts (
EVAL/EVALSHA) or Valkey Functions (FCALL) ensures consistency, but unoptimized loops or heavy string manipulations inside a script stall the entire server until the script completes or reacheslua-time-limit. - Pipelining and Buffer Bloat: While pipelining amortizes network round-trip times, unconstrained client pipelines (issuing tens of thousands of commands in a single batch) monopolize the event loop and inflate client output buffers, exhausting server memory.
How do we systematically isolate the exact command, key pattern, and data structure responsible for event-loop saturation while protecting production stability and data privacy?
Build a Workload-Aware Command Evidence Pack
flowchart TD
A[application p99 latency spikes and queueing alerts] --> E[time-bounded Valkey command evidence pack]
B[INFO commandstats deltas and latency percentiles] --> E
C[SLOWLOG samples and latency history events] --> E
D[sanitized MEMORY USAGE samples and client tracking metrics] --> E
E --> F[calculate command CPU share and per-call microsecond rates]
F --> G[correlate slowlog timestamps with client buffers and blocked states]
G --> H[LLM observations hypotheses contradictions and gaps]
H --> I[SRE and DBA verification]
I --> J[remediation validation and rollback]
To isolate expensive commands safely, construct an evidence pack covering baseline, onset, and degradation windows. The collection process must extract differential metrics without executing blocking keyspace scans:
- Command Statistics (
INFO commandstats): For each command family, record cumulativecalls,usec,usec_per_call,rejected_calls, andfailed_calls. Convert these cumulative totals into interval deltas ($\Delta \text{usec} / \Delta t$) to determine the exact percentage of main-thread CPU consumed by each command. - Slow Query Log (
SLOWLOG GET 128): Extract timestamp, execution duration in microseconds, command name, client IP/port, and key name (with payload arguments redacted to protect privacy). - Latency Diagnostics (
LATENCY LATEST,LATENCY HISTORY command): Track historical execution latency spikes across engine events. - Client and Buffer State (
INFO clients,CLIENT LIST): Captureconnected_clients,blocked_clients,client_recent_max_output_buffer,client_recent_max_input_buffer, and identify clients with large output buffers. Pin the collector to these field names for the Valkey versions you support; the olderclient_longest_output_listandclient_biggest_input_bufnames belong to earlier Redis-eraINFOoutput and will not be present. - Data Structure Telemetry: Sampled memory usage via
MEMORY USAGE <key> SAMPLES 5for suspect keys, andOBJECT ENCODING <key>to determine if collections have expanded beyond optimized compact encodings (e.g., listpack to hashtable/skiplist).
The LLM analyzes this structured payload to generate four distinct outputs:
- Observations: Quantitative facts tied to specific metric deltas (e.g., “
HGETALLaccounted for 74.2% of total main-thread CPU time over a 60-second window, averaging 4,200 µs per invocation across 18,000 calls”). - Hypotheses: Ranked architectural explanations with supporting and contradicting metrics (e.g., “Hypothesis 1: Large hash traversal on user session objects; Hypothesis 2: Client output buffer starvation caused by unbounded batch sizes”).
- Missing Evidence: Additional safe metrics required to confirm the hypothesis (e.g., “Need
OBJECT ENCODINGandHLENfor top slowlog hash keys to confirm if hash exceeds listpack boundaries”). - Actions: Non-blocking validation commands and application-level query refactoring steps.
Symptoms
| Symptom | Plausible failure classes | Evidence that separates them |
|---|---|---|
| Main-thread CPU pinned at 100% | $O(N)$ keyspace command, complex Lua script, large collection traversal | INFO commandstats $\Delta \text{usec}$, SLOWLOG duration, used_cpu_user_main_thread |
| Sudden p99 latency spike to >10ms | Big key serialization, blocking function, active defragmentation surge | SLOWLOG entries, LATENCY LATEST, latest_fork_usec, MEMORY USAGE |
| Client timeout during write burst | Blocking Lua script, synchronous eviction of big keys, unbounded pipeline | blocked_clients, lazyfree-lazy-eviction, client_recent_max_output_buffer |
| High network egress with low ops/sec | Unbounded read commands (HGETALL, SMEMBERS, LRANGE 0 -1) | total_net_output_bytes delta, INFO commandstats cmdstat_hgetall |
blocked_clients metric climbing | Blocking list/stream reads (BLPOP, BRPOP, XREAD BLOCK) | INFO clients blocked_clients, application consumer connection counts |
| Memory ballooning without key count increase | Nested data structures expanding from listpack to hashtable/skiplist | tracking_total_keys stable while used_memory_dataset climbs, OBJECT ENCODING |
First Five Checks
flowchart TD
A[Valkey command latency or CPU alert] --> B[1. Compute commandstats deltas and CPU time distribution]
B --> C[2. Inspect SLOWLOG for long-running command signatures]
C --> D[3. Audit big keys and structural encodings via non-blocking sampling]
D --> E[4. Identify hot keys and client access skew via LFU metrics]
E --> F[5. Evaluate Lua script execution times and client output buffers]
F --> G[Synthesize observations hypotheses and remediation steps]
1. Compute commandstats deltas and CPU time distribution
Never evaluate raw cumulative numbers from INFO commandstats. Calculate the rate of change over a discrete sample interval ($T_1$ to $T_2$):
$$\Delta \text{CPU_Share}(cmd) = \frac{\Delta \text{usec}(cmd)}{\Delta t_{\text{elapsed_microseconds}}} \times 100%$$
If cmdstat_keys, cmdstat_hgetall, cmdstat_smembers, or cmdstat_eval accounts for more than 20% of total elapsed time, the workload is suffering from algorithmic inefficiency. Compare usec_per_call: sub-microsecond commands (GET, SET) typically average 0.5–2.0 µs, whereas problematic collection queries average 500–10,000 µs per call.
2. Inspect SLOWLOG for long-running command signatures
Execute SLOWLOG GET 32 to capture the most recent commands that exceeded slowlog-log-slower-than (default 10,000 µs; recommended 1,000 µs for low-latency triage).
Analyze the slowlog payload:
- Command Name and Duration: Identify whether latency is driven by retrieval (
HGETALL), modification (SADD), or deletion (DELon large collections). - Execution Timestamps: Correlate slowlog timestamps with application latency alarms and CPU spikes.
- Client IP and Port: Determine if slow commands originate from a specific batch service, background worker, or misconfigured microservice.
3. Audit big keys and structural encodings via non-blocking sampling
Do not execute valkey-cli --bigkeys on a heavily loaded production node without rate limiting, as it runs continuous SCAN iterations that increase CPU overhead. Instead, perform targeted sampling on suspected keys identified in the slowlog:
- Memory Footprint: Execute
MEMORY USAGE <key> SAMPLES 5to estimate total memory consumption including allocator overhead. - Internal Encoding: Run
OBJECT ENCODING <key>. Valkey stores small hashes, sets, and zsets using memory-efficient, compactlistpackstructures. When element counts or value lengths exceed configured thresholds (hash-max-listpack-entries,hash-max-listpack-value), the engine converts the structure to a standardhashtableorskiplist, increasing memory overhead by 3x–5x and changing traversal CPU characteristics.
4. Identify hot keys and client access skew via LFU metrics
When a single key experiences massive concurrency, it saturates the execution thread. If Valkey is configured with an LFU eviction policy (such as allkeys-lfu or volatile-lfu), retrieve the access frequency counter using OBJECT FREQ <key>.
To scan for hot keys safely:
- Use
valkey-cli --hotkeys -i 0.1during non-peak windows with an explicit sleep interval (-i 0.1introduces a 100 ms pause every 100SCANiterations) to avoid CPU contention. - Monitor
CLIENT LISTto detect whether hundreds of connections are querying the exact same database index or executing identical query strings.
5. Evaluate Lua script execution times and client output buffers
Lua scripts and Valkey Functions execute with strict ACID atomicity, locking the main thread for their entire duration:
- Lua Execution Stalls: Check
cmdstat_evalandcmdstat_fcallinINFO commandstats. If script duration exceedslua-time-limit(default 5,000 ms), the engine logs warnings and allows read-onlySCRIPT KILLoperations. - Client Output Buffers: Inspect
INFO clientsforclient_recent_max_output_buffer. Large pipelines (e.g., 50,000GETcommands in a single pipeline) or wide queries (LRANGE 0 -1) force Valkey to buffer megabytes of reply data in memory per client. If client consumption stalls, the engine spends excessive time managing buffer memory allocations. Because this field is a recent maximum across clients rather than a per-connection gauge, follow it withCLIENT LISTand sort byomemto find the offending connection.
Decision Tree
flowchart TD
A[Valkey tail latency spike or event loop stall] --> B{Commandstats shows high usec per call}
B -->|Yes| C{Command type identified in SLOWLOG}
C -->|KEYS or Full Keyspace Scan| D[Replace with SCAN and cursor pagination]
C -->|HGETALL SMEMBERS LRANGE| E[Check collection size and switch to HSCAN or HMGET]
C -->|EVAL FCALL Lua Script| F[Decompose script and remove blocking loops]
C -->|DEL on Large Collection| G[Enable lazyfree or use UNLINK instead of DEL]
B -->|No| H{High ops per sec on single key}
H -->|Yes| I[Implement client-side caching or key sharding]
H -->|No| J{Blocked clients climbing}
J -->|Yes| K[Inspect BLPOP BRPOP XREAD queue consumer saturation]
J -->|No| L[Check client pipeline sizes and network output buffer limits]
In Practice
The official Valkey command reference documents the time complexity of every engine operation. Commands like KEYS are $O(N)$ where $N$ is the total number of keys in the database, making them prohibited in production. Collection operations such as HGETALL and SMEMBERS are $O(N)$ where $N$ is the number of elements in the specific collection; for hashes containing 100,000 fields, executing HGETALL forces the engine to serialize 100,000 key-value pairs synchronously.
The Valkey memory optimization guide specifies that hashes, lists, and sets below configured thresholds (such as hash-max-listpack-entries 512 and hash-max-listpack-value 64) are encoded as a single contiguous memory block (listpack). This representation provides extreme memory compactness and high CPU cache locality. Once a collection exceeds these limits, it converts to a hashtable or skiplist, increasing memory pointer overhead and altering allocation behavior.
According to the Valkey latency troubleshooting guide, deleting a large collection containing millions of elements using standard DEL is an $O(N)$ operation that deallocates memory synchronously on the main thread. The documented best practice is using UNLINK (non-blocking delete) or setting lazyfree-lazy-user-del yes to offload memory reclamation to background bio threads.
Documented client architecture patterns establish that while pipelining eliminates network round-trip delays, issuing unbounded pipelines (e.g., >10,000 operations per pipeline) forces the server to buffer all response objects simultaneously. This amplifies memory consumption in client-output-buffer-limit and creates head-of-line blocking for other connected clients.
Remediation Options
| Proven root cause | Candidate remediation | Validation criteria |
|---|---|---|
Unbounded hash read (HGETALL) | Refactor application to use HMGET for required fields or HSCAN for pagination | cmdstat_hgetall rate drops to 0; p99 latency normalizes |
Synchronous collection deletion (DEL) | Replace application DEL calls with UNLINK; set lazyfree-lazy-user-del yes | SLOWLOG entries for deletions disappear; latency spikes eliminate |
Keyspace scanning (KEYS *) | Replace with cursor-based SCAN with bounded COUNT parameters (e.g., COUNT 100) | cmdstat_keys drops to 0; event-loop stalls resolve |
| Hot-key access saturation | Enable Client-Side Caching (CLIENT TRACKING) or shard keys (key_{1..N}) | CPU distribution balances across cores/shards; OBJECT FREQ skews flatten |
| Long-running Lua script | Refactor complex business logic to client application or split script into smaller batches | cmdstat_eval average usec_per_call drops below 500 µs |
| Unbounded client pipelining | Enforce maximum client pipeline batch size (e.g., 500 commands per batch) | client_recent_max_output_buffer stabilizes; client buffer memory drops |
| Blocking list queue thundering herd | Stagger worker retry intervals and add exponential backoff on empty BLPOP | blocked_clients stabilizes; worker connection churn drops |
Rollback Plan
All production workload adjustments and configuration changes must maintain a clear, low-risk rollback strategy:
- Configuration Parameter Rollback: Any runtime setting modified via
CONFIG SET(such aslazyfree-lazy-user-del,hash-max-listpack-entries, orslowlog-log-slower-than) can be immediately restored to its baseline value using a reciprocalCONFIG SET. Always record initial parameters usingCONFIG GET *before modifications. - Client Application Feature Flags: When rolling out query refactorings (e.g., migrating from
HGETALLtoHMGET/HSCAN, or implementing client-side caching), gate changes behind dynamic configuration flags. If application-side batching introduces unforeseen race conditions, toggle the feature flag to revert to baseline behavior instantly. - Key Sharding / Salting Reversal: If partitioning hot keys across multiple sub-keys (
key_0,key_1), ensure the client read layer supports dual-reading from both the unpartitioned and partitioned key paths until data migration completes. - Client-Side Caching Evacuation: If enabling
CLIENT TRACKINGcauses invalidation message storming over application connections, disable tracking globally at the client library level without modifying database server configurations.
Where It Breaks
| Failure mode | Why naive reasoning fails | Better diagnostic boundary |
|---|---|---|
| ”Slow database means slow server” | A single client issuing KEYS * stalls an otherwise idle, multi-gigabyte instance | Audit INFO commandstats and SLOWLOG to isolate specific commands |
| ”Low CPU means all queries are fast” | Bursts of $O(N)$ commands lasting 5ms may barely move 1-minute CPU averages | Inspect p99/p999 latency percentiles and LATENCY LATEST history |
| ”Big keys must be deleted immediately” | Executing synchronous DEL on a 500MB key will freeze the server for seconds | Use UNLINK or asynchronous background deallocation |
| ”Pipelining is always better” | Massive pipelines create head-of-line blocking and trigger memory buffer limits | Benchmark pipeline batch sizes and monitor client output buffers |
| ”Lua scripts run faster than commands” | Lua executes synchronously; complex loops inside scripts block all clients | Profile Lua execution duration via cmdstat_eval and slowlog |
| ”Hot keys can be fixed by vertical scaling” | Valkey command execution is single-threaded per shard; more CPU cores will not help | Implement client-side caching or key partitioning |
What the LLM Cannot Do
To guarantee production security, data privacy, and operational stability, strict guardrails define LLM operations:
- No Raw Payload Inspection: The LLM must never receive unredacted key names, user IDs, authentication tokens, or payload data from
SLOWLOGorDUMPoutputs. All diagnostic evidence packs must mask payload parameters before analysis. - No Direct Query Execution: The LLM does not connect directly to the database to execute diagnostic queries, run
SCANloops, or modify configurations. - No Autonomous Key Deletion: The LLM cannot execute
DEL,UNLINK, orFLUSHDB. Remediation actions must be authorized and applied by human engineers through validated deployment pipelines. - No Guesses on Incomplete Commandstats: If
INFO commandstatslacks baseline rate-of-change deltas, the LLM must explicitly flag missing evidence rather than guessing workload distribution from cumulative lifetime counters.
What to Do Next
- Problem: Expensive commands, big collections, and hot keys stall Valkey’s single-threaded event loop, introducing severe tail latency across upstream services.
- Solution: Construct a workload-aware command evidence pack capturing differential
INFO commandstats, sampledSLOWLOGentries, and structural encoding diagnostics. - Proof: Correlate specific command families with main-thread CPU time share, verify collection encodings via
OBJECT ENCODING, and validate execution durations against slowlog timestamps. - Action: Instrument differential commandstat collection across your Valkey nodes. If host-level triage and command profiling confirm engine health, proceed to Part 3 to diagnose multi-node clustering, shard balancing, and ElastiCache managed failover behavior.
Sources
- Valkey Command Documentation and Time Complexities
- Valkey Memory Optimization and Data Structure Encodings
- Valkey Latency Troubleshooting Guide
- Valkey Programmability — Lua Scripts and Functions
- Valkey Pipelining and Network Transport Architecture
- Valkey Client-Side Caching and Key Invalidation Architecture