Redis SCAN vs KEYS in production

Use SCAN, never KEYS, on a Redis instance that serves traffic. KEYS pattern inspects every key in the database in one command, and because Redis executes commands on a single thread, every other client waits until it finishes. SCAN returns a small batch and a cursor, so the work is split into many short calls.

SCAN has semantics that surprise people the first time: empty batches, duplicates and no snapshot. Below is how to read its output correctly, and how to make KEYS unavailable to the account you use for browsing.

Step 1: iterate with a cursor

Start at cursor 0 and keep calling with the cursor Redis returns until it comes back as 0:

> SCAN 0 MATCH session:* COUNT 100
1) "1792"
2) 1) "session:9f2c"
   2) "session:41aa"

> SCAN 1792 MATCH session:* COUNT 100
1) "0"
2) 1) "session:07be"

The iteration is complete only when the returned cursor is "0". Getting an empty list does not mean you are done.

Step 2: understand COUNT and MATCH

  • COUNT is a hint for how much of the keyspace to walk per call, not how many keys to return. The default is 10.
  • MATCH is applied after Redis picks the batch. With a selective pattern, most batches come back empty or nearly empty. That is normal; keep following the cursor.
  • On Redis 6.0 and later, TYPE filters by value type in the same way, for example SCAN 0 MATCH user:* COUNT 100 TYPE hash.

Raise COUNT (say 500–1000) if you are walking a large keyspace with a selective pattern and the round trips dominate. Keep it modest on a busy primary.

Step 3: handle duplicates and changes

SCAN guarantees that a key present for the whole iteration is returned at least once. It does not promise exactly once, and keys added or removed during the iteration may or may not appear. So:

  • deduplicate in your code (a set is enough),
  • treat the result as “roughly the keyspace at about this time”, not a snapshot,
  • never use a SCAN result as the input to a destructive batch job without rechecking each key.

From the shell, redis-cli wraps the loop for you:

redis-cli -h cache.internal --scan --pattern 'session:*' | head

Step 4: inspect collections the same way

The same problem exists inside large values. HGETALL, SMEMBERS and LRANGE key 0 -1 on a key with millions of elements block just like KEYS. Use the incremental versions:

HSCAN user:42:prefs 0 COUNT 100
SSCAN online-users 0 COUNT 100
ZSCAN leaderboard 0 COUNT 100
LRANGE queue:emails 0 99

Check the size first with HLEN, SCARD, ZCARD or LLEN, then decide how much to read.

Step 5: take KEYS away from the browsing account

Rather than trusting everyone to remember, create an ACL user that cannot run KEYS or anything that writes. On Redis 6.2 and later:

ACL SETUSER inspector on >use-a-long-random-password ~* resetchannels -@all +@read -keys +scan +info +ping +dbsize
  • -@all starts from nothing.
  • +@read allows the read command category (GET, HGET, SCAN, TYPE, TTL and so on).
  • -keys removes KEYS, which is in the @read category.
  • +info +ping +dbsize add a few server-level commands that browsing tools use.

Then confirm:

ACL GETUSER inspector
AUTH inspector use-a-long-random-password
KEYS *
(error) NOPERM …

Persist the user with ACL SAVE if you use an ACL file, or add it to redis.conf with the same rule string. Managed Redis services often have their own way to define users. Check your provider’s documentation.

Doing this in DevOpsPad

DevOpsPad’s Redis browser only uses SCAN. The key tree loads in batches of SCAN … COUNT 100, with an optional MATCH pattern and a Load more keys button. It keeps the cursor between batches and removes duplicate names from the display. It also reminds you that SCAN can return an empty batch before it reaches the end, so an empty page is not mistaken for an empty database. Collection previews use HSCAN, SSCAN and ZSCAN and show up to 100 entries, and string previews are capped at 64 KiB.

The query editor accepts an allowlist of read-only commands such as GET, MGET, TYPE, TTL, SCAN, HSCAN, HGET, LLEN, DBSIZE, INFO and PING. KEYS is not on the list. Replies above 8 MiB are rejected with a hint to use bounded ranges. Connecting as a named ACL user is supported, so the inspector account above works as-is.

See browsing keys with SCAN, read-only Redis commands and Redis ACL users and TLS, or the Redis connector overview.