Redis CLI: Every Flag That Matters, and What Your Java Client Can't Do
redis-cli is the client that ships with the server, and most documentation for it is organised by flag. That is fine when you already know the flag. It is useless at 2am when memory is climbing, latency has doubled, and you do not yet know which question to ask.
This article is organised by problem instead. It also covers the half of the job the CLI manual does not — SLOWLOG, the LATENCY command family, MEMORY USAGE, OBJECT ENCODING, CLIENT LIST. Those are server commands, so they live on their own reference pages and almost never get read alongside the flags, which is how most people end up knowing half of each.
Everything here applies to Valkey and valkey-cli too, with the divergences called out in their own section. At the end there is a table mapping every operation onto its Java equivalent in Redisson — including the ones that have no equivalent, which turns out to be the more interesting half of the list.
How to Get a redis-cli Prompt
You rarely need to install a Redis server to get a client. If Redis is already running somewhere you can reach, three routes are faster than a package install.
# A container you already have
docker exec -it my-redis redis-cli
# A pod in Kubernetes
kubectl exec -it redis-0 -- redis-cli
# A throwaway client container pointed at a remote host
docker run --rm -it redis:8 redis-cli -h cache.internal -p 6379
If you want the binary on your machine and nothing else, Redis now publishes a standalone installer that drops a single statically linked redis-cli into /usr/local/bin:
curl -fsSL https://packages.redis.io/redis-cli/install.sh | sh
Linux and macOS, x86_64 and arm64. Failing that, Debian and Ubuntu package the client separately as redis-tools; on macOS brew install redis gets you redis-cli, but the server binaries come with it. For a full server install, see how to install Redis.
One thing to check before you plan a debugging session: on a managed endpoint, a large part of this article may be unavailable to you. Providers routinely block CONFIG, DEBUG, MONITOR and the cluster write subcommands. DEBUG in particular is documented as an internal command, carries the @admin, @slow and @dangerous ACL categories, and is listed as unsupported on Redis Software and Redis Cloud. Managed Redis covers what each provider takes away.
Connecting: Host, Port, Auth, TLS and Cluster
The connection flags are the ones everybody half-knows. These are the parts that bite.
| Flag | What it does |
|---|---|
-h / -p | Host and port. Defaults 127.0.0.1:6379 |
-s <socket> | Connect over a Unix domain socket instead of TCP |
-u <uri> | redis://user:password@host:port/dbnum. Use rediss:// for TLS |
-n <db> | Select a database number on connect |
-c | Cluster mode — follow -MOVED and -ASK redirects |
--user / --pass | ACL username and password |
--askpass | Prompt for the password with a masked input |
--tls | TLS, with --cacert, --cacertdir, --cert, --key, --insecure, --sni |
-t <seconds> | Connection timeout. Defaults to 0 — no limit, which is why redis-cli aimed at a filtered port appears to hang |
--name <name> | Set the connection name, so you can find yourself in CLIENT LIST |
-3 / -2 | RESP3 or RESP2. The default is RESP2 — redis-cli only sends HELLO 3 if you ask |
Do not use -a. It puts the password into your shell history and into ps output for every user on the box. redis-cli says so itself:
$ redis-cli -a hunter2 PING
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
PONG
Note the wording — the warning covers -u with an embedded password too, which most write-ups miss. The right answers are the REDISCLI_AUTH environment variable, or --askpass for an interactive session. If both -a and the environment variable are set, -a wins; --askpass ignores both. --no-auth-warning silences the message without fixing the problem, which is worth knowing mainly so you recognise it in someone else's script.
Two more things worth having in your head. For a server with ACLs where you only have a password, authenticate as the username default rather than leaving the username empty — the abbreviated redis://:pass@host form is parsed inconsistently across clients and versions, a trap covered in more detail in NOAUTH Authentication required. And --sni, which sets the server name for TLS and is exactly what you need behind a terminating proxy or a managed endpoint, is present in redis-cli but absent from the official manual's option list. It exists; it is simply undocumented there.
In cluster mode, -c does something you can see. Without it, a key that hashes elsewhere returns a MOVED error. With it, redis-cli follows the redirect and tells you:
$ redis-cli -c -p 7000
127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
127.0.0.1:7002>
Watch the prompt on the last line. redis-cli does not hop back — it stays on 7002 for your next command. That is usually what you want and occasionally very much not, if you were trying to inspect a specific node. For the Java side of all of this, see connecting to Redis in Java, TLS/SSL connections and Unix domain sockets.
Finding Keys Without Taking Down Production
One rule: KEYS blocks the server for the length of the scan, SCAN does not. On a keyspace of any size, KEYS * is an outage waiting for a trigger.
redis-cli wraps the whole cursor loop for you:
redis-cli --scan # every key
redis-cli --scan --pattern 'user:*' # glob-filtered
redis-cli --scan --pattern 'user:*' --count 500 # larger COUNT hint
redis-cli --scan -i 0.01 # 10ms pause between cycles
Note that -i means three different things depending on mode: with -r it waits between commands, with --scan and --stat it waits per cycle, and with the sampling modes below it waits per hundred cycles. And remember the guarantee SCAN actually gives — every key present for the whole iteration is returned at least once, so duplicates are normal and your consumer must be idempotent.
That is the short version. Listing all keys with KEYS and SCAN covers cursor behaviour across a resize, cluster-wide scanning, HSCAN/SSCAN/ZSCAN, and deleting by pattern safely. For clearing a database rather than reading it, see FLUSHALL and FLUSHDB.
Inspecting a Single Key: Type, Encoding and Memory
This is where the official manual stops and the useful part begins. Four commands tell you almost everything about one key.
> TYPE user:1001
hash
> OBJECT ENCODING user:1001
listpack
> MEMORY USAGE user:1001
264
> OBJECT IDLETIME user:1001
93
OBJECT ENCODING is the one worth understanding, because it explains memory behaviour that otherwise looks arbitrary. Redis stores small collections in a compact, cache-friendly representation and switches to a general-purpose one when they outgrow it. On Redis 8 the complete set of encodings is raw, int, embstr, listpack, listpackex, quicklist, hashtable, intset, skiplist and stream.
If you were expecting ziplist, linkedlist or zipmap: those are gone. They are still listed on the official OBJECT ENCODING reference page — ziplist version-qualified to Redis 6.2 and earlier, linkedlist and zipmap simply marked "no longer used" — but no code path in an 8.x server can return any of them. listpackex is the one people have not heard of — it is what a hash becomes once you set a per-field TTL with HEXPIRE.
The thresholds that trigger the switch, as shipped in redis.conf:
| Type | Compact form | Directive | Shipped value |
|---|---|---|---|
| Hash | listpack → hashtable | hash-max-listpack-entrieshash-max-listpack-value | 512 64 |
| List | listpack → quicklist | list-max-listpack-size | -2 (8 KB per node, not a count) |
| Set (integers) | intset → hashtable | set-max-intset-entries | 512 |
| Set (other) | listpack → hashtable | set-max-listpack-entriesset-max-listpack-value | 128 64 |
| Sorted set | listpack → skiplist | zset-max-listpack-entrieszset-max-listpack-value | 128 64 |
Three traps in that table. list-max-listpack-size is the odd one out: a negative value is a size, not an entry count, where -1 through -5 mean 4 KB, 8 KB, 16 KB, 32 KB and 64 KB per node. An all-integer set only reaches listpack if you lower set-max-intset-entries below set-max-listpack-entries; at the shipped values it goes straight to hashtable, because anything over 512 is already over 128. And a server started with no configuration file — the official container image, for instance — uses compiled-in defaults rather than the file's values, so confirm the pair you actually care about with CONFIG GET rather than assuming.
The behaviour that surprises people: for hashes, sets and sorted sets the conversion is one-way while the object lives, but a reload undoes it. A hash that grows past its configured entry threshold becomes a hashtable and stays one even if you delete fields back down to ten. Encodings are re-decided when an object is loaded from disk, so a restart or a replica load will put it back to listpack. If you are chasing memory that will not come down after a cleanup, that is very often the answer. (DEBUG RELOAD forces the same re-encode on demand, but it serialises and reloads the whole dataset synchronously — a staging-box tool, not something to fire at a production primary out of curiosity.)
Lists are the exception. Since Redis 7.2 a quicklist converts back to listpack on its own once the list shrinks, using a deliberately stricter threshold than the one that triggered the promotion so it does not flap on every push and pop. LPOP, RPOP, LSET, LTRIM, LREM and the LMOVE family can all trigger it. So a list is the one type where deleting elements does reclaim the compact encoding without a reload.
Two smaller ones. OBJECT REFCOUNT returns 2147483647 for shared integers rather than a real count — Redis keeps a shared pool of the integers 0 through 9999, so this is expected, not a bug. And MEMORY USAGE estimates nested values from a sample of 5 by default; MEMORY USAGE key SAMPLES 0 walks everything for an exact figure at the cost of doing the work. For what each type is and when to use it, see Redis data types and Redis hashes.
Finding What's Eating Your Memory
Per-key inspection assumes you know which key. When you do not, redis-cli has four sampling modes that walk the whole keyspace.
redis-cli --bigkeys # largest key of each type, by element count
redis-cli --memkeys # largest keys by actual memory, via MEMORY USAGE
redis-cli --keystats # both, plus a size distribution (Redis 7.4+)
redis-cli --hotkeys # most frequently accessed keys (requires LFU)
All four walk the keyspace with SCAN rather than KEYS, and the documentation says of --bigkeys that it "can be executed against a busy server without impacting the operations". Take that as specific to --bigkeys, which asks only O(1) size questions per key: --memkeys and --keystats issue MEMORY USAGE per key, and --hotkeys issues OBJECT FREQ per key, so on a large keyspace all three are considerably chattier. -i throttles all four, pausing every hundred cycles. Two extra flags belong to --keystats alone: --top <n> sets how many key sizes it lists (default 10), and --cursor <n> resumes a scan you interrupted with Ctrl-C rather than starting over. They are ignored by the other three, and --bigkeys in particular has no top-N list to resize — it reports one biggest key per type.
--hotkeys is the one that fails mysteriously. It depends on OBJECT FREQ, which only works when maxmemory-policy is set to allkeys-lfu or volatile-lfu. redis-cli does not check the policy itself — it just relays the server's rejection and exits — so the failure looks like an unexplained error rather than a configuration message. Check the policy first:
redis-cli CONFIG GET maxmemory-policy
One caveat before you trust any of their output: all four scan only the node you are connected to, and -c does not fan them out. On a six-shard cluster --bigkeys reports the biggest key in one sixth of your data and says nothing about that. Run them once per primary. --cluster call will not help either, because these are client-side modes rather than commands the server can run.
For the whole-instance picture, MEMORY DOCTOR gives a human-readable report. Its thresholds are not documented anywhere, which makes its silence hard to interpret, so they are worth stating: below 5 MB allocated it declines to analyse at all and tells you so, which is the one case where its silence is legible; it flags fragmentation only when the ratio exceeds 1.4 and the wasted bytes exceed 10 MB — a conjunction, so a ratio of 2.0 on an 8 MB instance reports nothing, because 8 MB of waste is under the byte floor; allocator fragmentation uses 1.1 rather than 1.4; it flags peak memory above 1.5× current, average client buffers above 200 KB, replica buffers above 10 MB in total, and a script cache above 1000 entries.
Once you know which keys are large, HGETALL vs HSCAN covers reading them without blocking, and Redis eviction policy covers what happens when you hit maxmemory.
Diagnosing Slowness: SLOWLOG, LATENCY and --latency
Three tools with confusingly similar names, measuring three different things. Only --latency is a redis-cli flag, so only it appears in the CLI manual; SLOWLOG and LATENCY are server commands on their own pages and rarely get read alongside it, which is how most people end up with a third of the picture.
SLOWLOG is the first place to look, and the cheapest. It records commands whose execution exceeded slowlog-log-slower-than, which ships at 10000 microseconds — 10 milliseconds — keeping the last slowlog-max-len entries, which ships at 128.
redis-cli SLOWLOG GET 10 # the ten most recent slow commands
redis-cli SLOWLOG LEN # how many entries are held
redis-cli SLOWLOG RESET # clear, so you can measure a window
The critical caveat is in the documentation and almost never repeated: the recorded time "does not include I/O operations like talking with the client, sending the reply and so forth, but just the time needed to actually execute the command." So a command that returns a 100 MB reply can take a second of wall-clock time and never appear in the slow log. If your application reports slowness and SLOWLOG is empty, that gap is usually why — and it is exactly the case HGETALL vs HSCAN is about.
The LATENCY family is a different subsystem, tracking spikes in server-internal events rather than commands. It is off by default — latency-monitor-threshold ships at 0, meaning disabled — which is why LATENCY LATEST so often returns nothing on a server nobody has configured. Turn it on first:
redis-cli CONFIG SET latency-monitor-threshold 100 # milliseconds
redis-cli LATENCY LATEST # most recent spike per event
redis-cli LATENCY HISTORY fork
redis-cli LATENCY GRAPH fork # ASCII-art time series
redis-cli LATENCY DOCTOR # human-readable analysis with advice
redis-cli LATENCY RESET # clear the series and the max register
Tracked events include command, fast-command, fork, aof-write, aof-fsync-always, expire-cycle and eviction-cycle. Note that LATENCY HISTOGRAM, despite the name, belongs to a third subsystem again — per-command latency distributions, controlled by latency-tracking, which is on by default. The percentiles 50 99 99.9 you may have seen quoted belong to latency-tracking-info-percentiles and govern INFO latencystats, not LATENCY HISTOGRAM, which returns raw buckets.
The --latency flags measure from where you are standing. They send PING in a loop and report the round trip, which includes your network:
$ redis-cli --latency
min: 0, max: 1, avg: 0.19 (427 samples)
--latency-history restarts the sampling window every 15 seconds by default, so you can see drift; --latency-dist draws a colour spectrum on a one-second interval, and needs a 256-colour terminal or the output is unreadable rather than absent. --latency-percentiles 50,99,99.9 (Redis 8.10+) adds percentiles to --latency and --latency-history, though not to --latency-dist — with a real caveat, that a percentile finer than 100 divided by your sample count just resolves to the maximum, so short runs give you a meaningless p99.9.
And --intrinsic-latency is the odd one out, worth its own sentence because it is so easily misread. It takes a duration in seconds — redis-cli --intrinsic-latency 100, run on the server host rather than your laptop — and it "does not connect to a Redis instance and performs the test locally." Run it on the server host, and it measures the largest interval the kernel denied CPU to a single process. On a noisy virtual machine that number can be several milliseconds — and it is the floor below which tuning Redis is pointless, because no amount of configuration will make the server respond faster than its host will schedule it. For the percentile vocabulary underneath all of this, see latency vs throughput.
Watching a Live Server: MONITOR, INFO, --stat and CLIENT LIST
MONITOR streams every command the server processes. It is the fastest way to answer "what is my application actually sending", and it is expensive: the documentation puts the cost at "more than 50%" of throughput, and is refreshingly honest that its benchmark is "totally unscientific". Always bound it:
timeout 5 redis-cli MONITOR > /tmp/mon.log # gtimeout on macOS
Two things it will not show you. Administrative commands and QUIT are never logged. And the handling of AUTH has changed twice: it was excluded outright from 6.0, then re-included from 6.2.4 alongside HELLO and the EVAL family, with sensitive arguments redacted. "AUTH has been hidden since 6.0" is a claim you will see often and it is wrong for every version you would actually be running.
--stat gives a rolling one-line-per-second summary, which is the cheapest way to watch a server through an incident:
$ redis-cli --stat
------- data ------ --------------------- load -------------------- - child -
keys mem clients blocked requests connections
506 1015.00K 1 0 24 (+0) 7
506 1015.00K 1 0 25 (+1) 7
506 3.40M 51 0 60461 (+60436) 57
INFO is the standing snapshot. Request a section by name — INFO memory, INFO stats, INFO replication, INFO keyspace — because the bare command returns only the default set. Two sections you will want are not in it and must be asked for explicitly: INFO commandstats, which breaks down calls and time per command, and INFO latencystats, which gives per-command percentiles. Confirm on your own server with redis-cli INFO | grep -c cmdstat_; a zero there is the fastest way to remember. Redis monitoring covers which fields in each section actually matter.
For connection problems, the CLIENT family is what you want, and it is the least-covered corner of redis-cli:
redis-cli CLIENT LIST # every connection: age, idle, cmd, addr
redis-cli CLIENT KILL ID 42 # or by ADDR, LADDR, TYPE, USER, MAXAGE
redis-cli CLIENT NO-EVICT on # exempt this connection from eviction
redis-cli CLIENT NO-TOUCH on # inspect keys without changing LRU/LFU stats
CLIENT NO-TOUCH, added in 7.2, deserves attention if you have been reading the previous two sections: it stops your own inspection from perturbing exactly the access statistics that OBJECT FREQ and --hotkeys report. Without it, you change what you are measuring.
Finally, keyspace notifications turn the server into an event stream. They are off by default — notify-keyspace-events ships empty — and the flag string must contain K or E or nothing is delivered at all, whatever else you set:
redis-cli CONFIG SET notify-keyspace-events KEA
redis-cli PSUBSCRIBE '__keyevent@0__:expired'
Be aware that A is an alias for g$lshztdxea and does not include the key-miss, new-key, overwritten or type-changed events; those need m, n, o and c added by hand. Redis notifications in Java covers consuming these from an application.
Cluster Operations From the Command Line
The official manual's entire treatment of cluster management is a pointer to --cluster help. There are twelve subcommands behind it, plus help itself. These are the ones you will use:
| Subcommand | What it does |
|---|---|
--cluster check <host:port> | Verify slot coverage and agreement between nodes. Start here |
--cluster info <host:port> | Key count, slot count and node summary |
--cluster fix <host:port> | Repair open slots and coverage gaps |
--cluster reshard <host:port> | Move a chosen number of slots between nodes |
--cluster rebalance <host:port> | Even out slots automatically; --cluster-simulate shows the plan first |
--cluster call <host:port> <cmd> | Run one command on every node |
--cluster create | Build a cluster. Three primaries is the documented minimum |
--cluster add-node / del-node | Grow or shrink the cluster |
redis-cli --cluster check 10.0.0.1:6379
redis-cli --cluster rebalance 10.0.0.1:6379 --cluster-simulate
redis-cli --cluster-only-masters --cluster call 10.0.0.1:6379 DBSIZE
Two things about those option flags. The prefix is --cluster-: it is --cluster-only-masters, not --only-masters, and there is a matching --cluster-only-replicas. And position matters — redis-cli collects a subcommand's arguments as an unbroken run of non-dash tokens, so an option placed in the middle truncates them. Put cluster options before --cluster <subcommand>, or after all of its arguments; never between.
One thing to check before you reshard, because the answer changed recently and most write-ups still have the old one. Redis 8.4 added atomic slot migration via CLUSTER MIGRATION IMPORT, and Valkey 9.0 added its own via CLUSTER MIGRATESLOTS — but on both, the new commands were initially server-side only, and redis-cli --cluster reshard kept driving the legacy sequence: CLUSTER SETSLOT IMPORTING, CLUSTER SETSLOT MIGRATING, then CLUSTER GETKEYSINSLOT plus MIGRATE in batches, then CLUSTER SETSLOT NODE.
Redis 8.10 changed that. Its release notes state that --cluster reshard and --cluster rebalance now move slots with server-side atomic slot migration. So the same command does something materially different depending on your server version: on Redis 8.10 and later it is atomic, and on Redis 8.8 or earlier — and on Valkey, whose CLI has no wrapper for CLUSTER MIGRATESLOTS — it is still the legacy batch loop, which is interruptible and leaves slots in an open state if it dies halfway. Check with redis-cli INFO server | grep redis_version before you assume which one you are about to run.
The two implementations also went in opposite directions, which matters if you run both. Redis is destination-driven: you send CLUSTER MIGRATION IMPORT to the node receiving the slots. Valkey is source-driven: you send CLUSTER MIGRATESLOTS ... NODE <target> to the node giving them up. Same feature, incompatible command surfaces. Redis Cluster covers slot routing in depth, and upgrading a cluster with zero downtime covers the operational sequence.
Beyond Interactive Use: --pipe, --eval and Output Formats
--pipe is the fastest way to load bulk data, and it is fussy about input: the documented format is raw RESP protocol rather than plain commands. Generate it, then pipe it in.
$ redis-cli --pipe < data.txt
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1000000
Check errors: before you celebrate — a non-zero count there means part of your load silently failed. --pipe-timeout defaults to 30 seconds; 0 waits forever. In Java, the equivalent is batching rather than piping; see Redis pipelining.
--eval runs a Lua script from a file, using a comma to separate keys from arguments so you never have to count them:
redis-cli --eval /tmp/script.lua location:hastings:temp , 23
The spaces around the comma are required. --ldb attaches the Lua debugger to a script run. See Redis Lua scripting for what belongs in a script in the first place.
For scripting redis-cli itself, the output flags matter more than they look. --json gives parseable output — and quietly switches the connection to RESP3, which is worth knowing if you are comparing output between runs. --csv gives comma-separated, --no-raw forces quoted output when piping, -x reads the last argument from stdin, -r <n> repeats a command n times, and -e makes redis-cli exit non-zero on an error so a shell script can branch on it. --rdb <file> pulls a full dump from a remote server. It is the simplest ad-hoc backup there is, with three conditions: it works by initiating a replication sync, so the server forks and streams the entire dataset; it is among the first things managed providers disable; and on a cluster it captures one node, so a full backup is one invocation per primary.
valkey-cli: What Changed After the Fork
Almost nothing you have read so far changes. The binary is valkey-cli, and a source install creates redis-cli symlinks alongside it for compatibility — though packaged builds may not, so do not rely on it. Four differences are worth knowing:
| Area | Redis | Valkey |
|---|---|---|
| Password env var | REDISCLI_AUTH | VALKEYCLI_AUTH |
| TLS URI scheme | rediss:// | valkeys:// |
| Key statistics | --keystats (7.4+) | Not available |
| Slow log | SLOWLOG | SLOWLOG, plus COMMANDLOG (8.1+) |
COMMANDLOG is the interesting one, because it fixes the gap described earlier. Valkey 8.1 generalised the slow log into three types: SLOW, which behaves as before and still excludes I/O; LARGE-REQUEST, for commands whose request exceeded a size threshold; and LARGE-REPLY, which catches exactly the fast-command-huge-payload case the execution-time slow log structurally cannot see. SLOWLOG still works — Valkey documents COMMANDLOG GET <count> SLOW as an alternative rather than a replacement.
Valkey 9.0 also added cluster-databases, defaulting to 1, allowing multiple databases in cluster mode where Redis still permits only database 0. Valkey vs Redis covers the broader divergence, and migrating from Redis to Valkey in Java covers the client side.
The Same Operations in Java: A Redisson Equivalence Table
The node-management API lives in org.redisson.api.redisnode and is reached through getRedisNodes():
RedisCluster cluster = redisson.getRedisNodes(RedisNodes.CLUSTER);
for (RedisClusterMaster master : cluster.getMasters()) {
Map<String, String> memory = master.info(RedisNode.InfoSection.MEMORY);
System.out.println(master.getAddr() + " " + memory.get("used_memory_human"));
}
| redis-cli | Redisson |
|---|---|
-h host -p port | Config.useSingleServer().setAddress("redis://host:port") |
--user / --pass | setUsername() / setPassword(), or setCredentialsResolver() for rotating credentials |
--tls | No flag — the rediss:// or valkeys:// scheme enables it. Tune with setSslProvider(), setSslVerificationMode() |
-n <db> | SingleServerConfig.setDatabase(int) — on each servers-config, not on Config |
-c | Nothing to set. MOVED and ASK redirects are followed automatically |
SCAN MATCH COUNT | RKeys.getKeys(KeysScanOptions.defaults().pattern("user:*").chunkSize(500)) |
DBSIZE | RKeys.count() |
TYPE key | RKeys.getType() → RType. Note RType.OBJECT means a string |
DEL by pattern | RKeys.deleteByPattern() / unlinkByPattern() — non-atomic in cluster mode |
OBJECT ENCODING | RObject.getInternalEncoding() → ObjectEncoding |
OBJECT IDLETIME / REFCOUNT / FREQ | getIdleTime() / getReferenceCount() / getAccessFrequency() |
MEMORY USAGE key | RObject.sizeInMemory() |
INFO <section> | RedisNode.info(RedisNode.InfoSection.MEMORY) |
CONFIG GET / SET | RedisNode.getConfig() / setConfig() |
PING | RedisNode.ping(), or pingAll() across the group |
CLUSTER INFO | RedisClusterNode.clusterInfo() |
--cluster call | RScript.eval(...) with a result mapper runs a Lua script on every node and reduces the results — good for DBSIZE-shaped questions, but a script cannot stand in for the administrative commands --cluster call usually carries |
--pipe | redisson.createBatch() → RBatch.execute(); RBuckets.set(Map) for bulk strings |
--eval | RScript.eval(RScript.Mode.READ_WRITE, script, RScript.ReturnType.VALUE, keys) |
FLUSHALL / FLUSHDB | RKeys.flushall() / flushdb(), or the flushallParallel() variants |
PSUBSCRIBE __keyevent@ | RPatternTopic, or better, RObject.addListener(new ExpiredObjectListener(){...}) |
That last row is worth dwelling on. Rather than subscribing to __keyevent@0__:expired and parsing channel names by hand, Redisson exposes typed listeners — ExpiredObjectListener, DeletedObjectListener, SetObjectListener, MapPutListener, FlushListener and others — attached directly to the object you care about. They do not remove the server-side prerequisite, though: notify-keyspace-events still has to be enabled, at minimum Ex for expiry, and on a managed endpoint you may not be able to set it. The Redis data structures in Java post covers the collection APIs these hang off.
What redis-cli Can Do That No Client Library Can — and Vice Versa
The honest version of the comparison, because the table above is only half the story.
Seven things in this article have no first-class Redisson API — nothing in org.redisson.api, and nothing that PRO adds either. Some have a workaround; none has a supported method you would want to build on:
| Not available from Java | What to do instead |
|---|---|
SLOWLOG | Use the CLI, or client-side latency metrics |
LATENCY DOCTOR / LATEST / HISTORY | Use the CLI |
MONITOR | Use the CLI, bounded. (Redisson's SENTINEL MONITOR is an unrelated command) |
CLIENT LIST / CLIENT KILL | Use the CLI. CLIENT SETNAME is available via setClientName() |
--bigkeys / --memkeys / --hotkeys | Use the CLI, or hand-roll a scan with sizeInMemory() per key |
--stat | Poll RedisNode.info(InfoSection.STATS) on a timer |
--cluster check / reshard / rebalance | Use the CLI. Redisson exposes slot primitives, not the orchestration |
The pattern is consistent: Redisson gives you the data plane and leaves the control plane to the CLI. That is a deliberate line rather than an oversight — an application connection pool that can issue MONITOR or CLUSTER SETSLOT is more often a liability than a feature. But it is Redisson's line, not an industry one: Lettuce exposes slowlogGet() and clientList(), and Jedis ships a JedisMonitor class. If you specifically need to read the slow log from application code, that is a real reason to reach for a lower-level client alongside Redisson.
The reverse list is shorter but more consequential, because it describes the things you cannot script your way to from a shell. A client library holds a live view of cluster topology and re-routes on failover without you noticing. It pools and multiplexes connections instead of opening one per invocation. It gives you a near cache, so the hottest reads never reach the server at all. It gives you distributed locks, queues and collections built from Lua scripts you did not have to write and get right. And it gives you the client-side half of the latency picture — connection-pool exhaustion, retry storms, per-command timing as your application experiences it — none of which appears in SLOWLOG, by definition, because the server never sees it.
Use both. The CLI is how you find out what is wrong with the server; the client is how the application stops being wrong in the first place.
Frequently Asked Questions
What Is redis-cli Used For?
redis-cli is the command-line client shipped with Redis. It runs commands interactively or from a shell script, and it also carries a set of operational modes that are not commands at all — key and memory scanning (--bigkeys, --memkeys), latency measurement (--latency), bulk loading (--pipe), and cluster management (--cluster).
How Do I Use a redis-cli Command?
Run redis-cli with no arguments for an interactive prompt, or pass the command directly for a single shot: redis-cli GET user:1001. Add -h and -p for a remote server, and -n to select a database. Use -e if a shell script needs a non-zero exit code on error.
How Can I Install redis-cli?
Redis publishes a standalone client installer for Linux and macOS: curl -fsSL https://packages.redis.io/redis-cli/install.sh | sh. Debian and Ubuntu also package it as redis-tools. And if Redis already runs in a container or in Kubernetes you do not need to install anything — docker exec -it my-redis redis-cli or kubectl exec -it redis-0 -- redis-cli gives you a prompt immediately.
Is There a redis-cli for Windows?
There is no official Windows build. The practical routes are WSL, which runs the Linux binary directly, or a container with docker run --rm -it redis:8 redis-cli -h your-host. Both give you the genuine client rather than a reimplementation.
How Do I Connect redis-cli to a Remote Redis Server?
Use redis-cli -h host -p port, adding --tls for an encrypted endpoint and -c if it is a cluster. For authentication, set the REDISCLI_AUTH environment variable or use --askpass rather than -a, which exposes the password in shell history and in ps output.
Why Does --hotkeys Return Nothing?
Strictly it does not return nothing — it errors out and exits, which is easy to misread as an empty result. The cause is almost always that maxmemory-policy is not an LFU policy. --hotkeys relies on OBJECT FREQ, which only works under allkeys-lfu or volatile-lfu, and redis-cli does not check the policy itself — it just relays the server's rejection. Confirm with redis-cli CONFIG GET maxmemory-policy.
What Is the Difference Between --latency and LATENCY DOCTOR?
They measure different things. redis-cli --latency is a client-side flag that pings in a loop and reports the round trip, including your network. LATENCY DOCTOR is a server command that analyses spikes in internal events such as fork and AOF writes — and it is off by default, because latency-monitor-threshold ships at 0. A third tool, SLOWLOG, measures command execution only and explicitly excludes I/O.
Can I Run redis-cli Commands From Java?
Most of them. Redisson maps the data commands onto typed Java APIs, exposes node operations such as INFO, CONFIG GET/SET and PING through getRedisNodes(), and per-key inspection such as MEMORY USAGE and OBJECT ENCODING through RObject. Seven have no first-class Redisson API: SLOWLOG, the LATENCY family, MONITOR, CLIENT LIST/KILL, --bigkeys, --stat, and the --cluster orchestration subcommands. Other Java clients differ — Lettuce exposes slowlogGet() and clientList(), and Jedis has a JedisMonitor class.
Next Steps
For the commands behind the flags, see Redis data types and Redis Cluster. For reading the numbers INFO gives you, Redis monitoring covers which fields matter and why. For the Java equivalents in depth, the node operations documentation covers getRedisNodes() and the commands mapping lists the data commands.
If you found this article because something was slow, the two most common causes are an unbounded read on a large collection — covered in HGETALL vs HSCAN — and a KEYS call that should have been a SCAN, covered in listing all keys safely. Which is the division of labour this whole article rests on: the CLI is how you find out what is wrong with the server, and the client is how the application stops being wrong in the first place.