Data Type | Criterion |
String | A single Value exceeds 10 MB. |
Hash | The number of elements exceeds 10,000, or the total size exceeds 100 MB. |
List | The number of elements exceeds 10,000, or the total size exceeds 100 MB. |
Set | The number of elements exceeds 10,000, or the total size exceeds 100 MB. |
ZSet | The number of elements exceeds 10,000, or the total size exceeds 100 MB. |
HGETALL operation requests per second.ZRANGE operation requests per second.Determination Dimension | Determination Method |
Access concentration | Obtain the access ranking through DBbrain hot key analysis, and observe whether there is an order-of-magnitude difference between the top-ranked key and the keys after it. |
Relative proportion | Calculate the ratio of requests for a single Key to the total requests of the instance and the ratio of traffic for a single Key to the instance's outbound bandwidth. A higher ratio indicates a higher risk. |
Resource headroom | Evaluate whether the CPU, bandwidth, and connections of the node where the Key resides are approaching their limits based on the instance specifications. |
Issue | Specific Symptom |
Uneven memory usage | In cluster architecture, the memory utilization of a shard is far higher than that of other shards, which may trigger the maxmemory limit and cause important keys to be evicted or even OOM. |
Request blocking and timeout | Command execution uses a single-threaded model. Operations on big keys take a long time (such as DEL and HGETALL), during which subsequent requests can only wait in queue. |
Sync interruption or master-replica switchover | Deleting big keys or performing RENAME operations may block the master node for a long time, causing master-replica synchronization interruption or failover. |
Network congestion | A 1 MB big Key accessed 1,000 times per second generates approximately 1 GB/s of traffic, which may consume the entire instance bandwidth. |
Issue | Specific Symptom |
Sustained high CPU utilization | Hotspot requests are concentrated on a single node, the CPU of this node becomes a bottleneck, and overall service performance degrades. |
Access skew | Shards with hot keys bear much higher access pressure than other shards, which may lead to connection exhaustion or even node exceptions. Since access is concentrated on a single key, horizontally scaling out shards cannot distribute the pressure of that key. |
Cache breakdown | When a hot Key expires or its node becomes abnormal, highly concentrated traffic directly penetrates to the backend database. If the database cannot handle the traffic, a cascading failure may occur. |
REDISCLI_AUTH environment variable instead of using the -a parameter:# Pass the password through an environment variable to avoid plaintext exposureexport REDISCLI_AUTH='<password>'# Inspect Big Keys: Output the Key with the most elements by data typeredis-cli -h <instance address> -p <port> --bigkeys# Inspect Hot Keys: Output high-frequency keys based on LFU access frequency statisticsredis-cli -h <instance address> -p <port> --hotkeys
SCAN cursor and do not block the main thread for a long time. However, they incur additional request overhead when the data volume is large, so it is recommended to run them during off-peak hours. In cluster architecture, you need to connect to each shard node separately, as a single connection can only collect statistics for the current node.--bigkeys counts the number of elements (byte length for strings) rather than actual memory usage, so the result only reflects the element scale. To locate keys by memory usage, use the memory analysis feature in Method 1. --hotkeys depends on the LFU eviction policy, so you must first adjust the eviction policy parameter of the instance before using it.--hotkeys, set the instance parameter maxmemory-policy to allkeys-lfu or volatile-lfu. Otherwise, the command will report an error directly. This parameter determines the Key eviction behavior of the instance. Modifying it may change the eviction order of existing data, so evaluate the impact on your business before making the adjustment.jedis.get(key) with HotKeyDetector.get(jedis, key) to complete the instrumentation. Then, use ScheduledExecutorService to call printTopN(10) every 60 seconds to output the results and execute reset() to reset. Other clients (Lettuce, Redisson) and commands (set, hget, and so on) can be extended by referring to this component.import redis.clients.jedis.Jedis;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.atomic.AtomicLong;public class HotKeyDetector {// Key → access countprivate static final ConcurrentHashMap<String, AtomicLong> KEY_COUNT = new ConcurrentHashMap<>();// Key → cumulative latency (nanoseconds)private static final ConcurrentHashMap<String, AtomicLong> KEY_TIME = new ConcurrentHashMap<>();/*** Wraps the Jedis get operation to automatically collect the access count and latency of each Key.*/public static String get(Jedis jedis, String key) {long start = System.nanoTime();try {return jedis.get(key);} finally {record(key, System.nanoTime() - start);}}private static void record(String key, long nanos) {KEY_COUNT.computeIfAbsent(key, k -> new AtomicLong()).incrementAndGet();KEY_TIME.computeIfAbsent(key, k -> new AtomicLong()).addAndGet(nanos);}/*** Prints the top N hot keys. It is recommended to call it through a scheduled task (for example, every 60 seconds).*/public static void printTopN(int n) {System.out.println("===== Top " + n + " Hot Keys =====");KEY_COUNT.entrySet().stream().sorted((a, b) -> Long.compare(b.getValue().get(), a.getValue().get())).limit(n).forEach(e -> {String key = e.getKey();long count = e.getValue().get();long totalNs = KEY_TIME.getOrDefault(key, new AtomicLong()).get();double avgMs = count > 0 ? (totalNs / 1_000_000.0) / count : 0;System.out.printf("Key: %s | Count: %d | Avg: %.2f ms%n",key, count, avgMs);});}/*** Resets the statistics. Call it after each print to avoid data accumulation.*/public static void reset() {KEY_COUNT.clear();KEY_TIME.clear();}}
Methodology | Application scenarios | Strength | Disadvantages |
DBbrain Console | Daily Ops and routine troubleshooting | Visualization, historical data analysis support, and no business code modification required. | Requires console access permission |
redis-cli --bigkeys | Quickly self-check big keys in the command-line environment | No additional deployment required, ready to use out of the box. | Counts only the number of elements and does not reflect memory usage. |
redis-cli --hotkeys | Quickly self-check hot keys in the command-line environment | No additional deployment required, ready to use out of the box. | Depends on the LFU eviction policy and requires adjustment of instance parameters. |
Business-Layer Instrumentation | Precise locating and long-term continuous monitoring | Statistical dimensions are customizable and can be associated with business contexts. | Requires code modification and introduces minor performance overhead. |
DEL command directly, which blocks the main thread. Instead, use progressive cleanup:# Hash: Use HSCAN + HDEL to progressively delete expired members, looping until cleanup is complete.HSCAN key cursor COUNT 100HDEL key field1 field2 ...# List: Use LTRIM to trim in batches, keeping only the first 1,000 entries.LTRIM key 0 999# Redis 4.0+: Use UNLINK to asynchronously delete the entire big Key to avoid blocking.UNLINK large_key
// Write: Store the data in binary format after compression.// You can implement gzipCompress / gzipDecompress based on java.util.zip.GZIPOutputStream.byte[] compressed = gzipCompress(rawJsonString);jedis.set(key.getBytes(), compressed);// Read: Retrieve the binary data and then decompress it to restore the original content.byte[] data = jedis.get(key.getBytes());String rawJson = gzipDecompress(data);
# Before Splittinguser:profile:1001 → 2MB JSON# After Splitting (Grouped by Field)user:profile:1001:basic → Basic informationuser:profile:1001:extend → Extended informationuser:profile:1001:settings → Settings information
# Hash Splitting: Apply Hash modulo to the Field and distribute it across N sub-Hashes.# Shard ID = hash(field) % N. Both writes and reads use the same rule for locating the shard.user:cart:1001:shard_0 → Stores fields whose modulo result is 0.user:cart:1001:shard_1 → Stores fields whose modulo result is 1....
MGET may not be able to execute across slots. If batch reads are required, use hash tags to pin sub-keys to the same shard, for example, {user:profile:1001}:basic and {user:profile:1001}:extend. As long as the content within the curly braces is the same, the keys are guaranteed to fall into the same slot. However, note that hash tags will concentrate this group of keys on a single shard. If the key is also a hot key, access skew will be exacerbated. Therefore, the need for batch reads and the need for shard distribution should be weighed based on the actual scenario.# Before Offloadingfile:report:2025-q4 → 15MB PDF BLOB# After Offloadingfile:report:2025-q4:url → "https://bucket.cos.region.myqcloud.com/report-2025-q4.pdf"file:report:2025-q4:meta → {"size": 15728640, "md5": "abc123..."}
// Cache hot data locally with Caffeineprivate static final LoadingCache<String, String> HOT_KEY_CACHE = Caffeine.newBuilder().maximumSize(1000) // Maximum number of cached entries.expireAfterWrite(50, TimeUnit.MILLISECONDS) // Expiration time, which controls the data inconsistency window.build(key -> {// On a cache miss, fetch from Redis and obtain a connection from the connection pool.try (Jedis jedis = JEDIS_POOL.getResource()) {return jedis.get(key);}});
# Original Hot Keyhot:product:1001# Split into Multiple Replicashot:product:1001:0 → replica 0hot:product:1001:1 → replica 1hot:product:1001:2 → replica 2
// During reads, randomly select a replica to distribute requests across different shards.int index = ThreadLocalRandom.current().nextInt(REPLICA_COUNT);String key = "hot:product:" + productId + ":" + index;try (Jedis jedis = JEDIS_POOL.getResource()) {String value = jedis.get(key);}
User request → Nginx local cache → application local cache (Caffeine) → Redis → database
ZRANGE 0 -1)KEYS *, SMEMBERS, and HGETALL. Instead, use SCAN, SSCAN, and HSCAN to obtain data in batches.Pipeline or MGET/MSET instead of looping single commands.UNLINK for asynchronous deletion (Redis 4.0+).フィードバック