Keys and expiration
Every value in HotKV lives under a key. This page covers the commands that work on keys whatever type of value they hold: finding them, renaming and deleting them, choosing a database, and making them expire.
Naming keys
Section titled “Naming keys”A key is a binary-safe string: any sequence of bytes, from an empty string up
to proto-max-bulk-len (512 MB by default). In practice, short readable keys
with a consistent structure are easiest to work with. A common convention is
to join the parts with colons, from the most general to the most specific:
user:42user:42:sessionsorder:2026-10:1187Keep keys short when you have many millions of them, since every key takes
memory, but prefer clarity over saving a few bytes. In a cluster, the part of
a key between the first { and the following } decides its hash slot, so
user:{42}:profile and user:{42}:cart always land on the same node. See
Clustering.
Finding keys
Section titled “Finding keys”SCAN walks the keyspace a page at a time with a cursor.
Start with cursor 0 and call it again with the cursor each reply gives you
until it returns 0. MATCH filters by a glob pattern, COUNT hints how
much work to do per call, and TYPE keeps only keys of one type.
127.0.0.1:6379> SCAN 0 MATCH user:* COUNT 1001) "0"2) 1) "user:42" 2) "user:7"127.0.0.1:6379> SCAN 0 TYPE hash1) "0"2) 1) "profile:42"A full iteration returns every key that existed for the whole scan exactly once. Keys added or removed during the scan may or may not appear. Code written for Redis, where a key can come back more than once, works the same.
KEYS returns every matching key in one reply. It is fine on
a small development database, but on a large one it builds a very large reply
and holds up other work while it runs, so use SCAN in production code.
RANDOMKEY returns one key at random and
DBSIZE counts the keys in the current database.
Inspecting and changing keys
Section titled “Inspecting and changing keys”| Command | What it does |
|---|---|
| EXISTS | How many of the given keys exist |
| TYPE | The value’s type: string, list, set, zset, hash, stream, or none |
| RENAME | Give a key a new name, replacing any key already there |
| RENAMENX | Rename only if the new name is free |
| COPY | Copy a value to another key, optionally in another database |
| TOUCH | Count a key as accessed (for idle time and LRU/LFU eviction) |
| DEL | Delete keys and return how many existed |
| UNLINK | Same reply as DEL |
TYPE reports Bloom filters, Count-Min sketches and Top-K structures with the
same names Redis Stack uses (MBbloom--, CMSk-TYPE, TopK-TYPE), so tools
that check those names keep working.
127.0.0.1:6379> SET greeting "hello"OK127.0.0.1:6379> TYPE greetingstring127.0.0.1:6379> RENAME greeting welcomeOK127.0.0.1:6379> RENAME missing other(error) ERR no such key127.0.0.1:6379> EXISTS welcome greeting(integer) 1127.0.0.1:6379> DEL welcome(integer) 1OBJECT
Section titled “OBJECT”OBJECT reports how a value is stored and how it has been used:
- OBJECT ENCODING returns the encoding name Redis
would report (
int,embstr,raw,listpack,quicklist,intset,hashtable,skiplist,stream, …), so tools that look at encodings keep working. - OBJECT IDLETIME is the number of seconds since
the key was last accessed. It is available unless
maxmemory-policyis an LFU policy. - OBJECT FREQ is the key’s access frequency counter.
It is available only with an LFU
maxmemory-policy. - OBJECT REFCOUNT always returns
1.
Each subcommand replies nil for a key that does not exist.
127.0.0.1:6379> SET counter 42OK127.0.0.1:6379> OBJECT ENCODING counter"int"127.0.0.1:6379> OBJECT FREQ counter(error) ERR An LFU maxmemory policy is not selected, access frequency not tracked. Please note that when switching between policies at runtime LRU and LFU data will take some time to adjust.Databases
Section titled “Databases”A standalone HotKV server has 16 logical databases by default, numbered from
0 (set the count with databases). Each connection starts in database 0;
SELECT switches it. Keys in different databases are
independent, so user:42 in database 0 and user:42 in database 1 are two
different keys.
127.0.0.1:6379> SELECT 1OK127.0.0.1:6379[1]> SET user:42 "in db 1"OK127.0.0.1:6379[1]> SELECT 0OK127.0.0.1:6379> GET user:42(nil)MOVE moves a key to another database, SWAPDB swaps two databases at once, and FLUSHDB empties the current one.
Expiration
Section titled “Expiration”Any key can have a time to live (TTL). When it runs out, the key is deleted
as if you had called DEL. Expiry is set on the key as a whole; for hashes,
you can also expire individual fields (see the HEXPIRE family on the
Hashes page).
Setting a TTL
Section titled “Setting a TTL”| Command | Expire after or at |
|---|---|
| EXPIRE | A number of seconds from now |
| PEXPIRE | A number of milliseconds from now |
| EXPIREAT | A Unix time in seconds |
| PEXPIREAT | A Unix time in milliseconds |
| NEXPIRE | A number of nanoseconds from now (HotKV only) |
| NEXPIREAT | A Unix time in nanoseconds (HotKV only) |
You can also set a TTL while writing: SET takes EX, PX,
EXAT and PXAT, and GETEX changes the TTL of a key as
it reads it.
EXPIRE, PEXPIRE, EXPIREAT and PEXPIREAT accept one condition:
| Option | Set the TTL only if |
|---|---|
NX |
The key has no TTL yet |
XX |
The key already has a TTL |
GT |
The new expiry is later than the current one (a key without a TTL counts as never expiring) |
LT |
The new expiry is earlier than the current one |
They reply 1 when the TTL was set and 0 when the key does not exist or the
condition was not met. A time that has already passed deletes the key at once.
127.0.0.1:6379> SET session:42 "token"OK127.0.0.1:6379> EXPIRE session:42 3600(integer) 1127.0.0.1:6379> EXPIRE session:42 60 GT(integer) 0127.0.0.1:6379> TTL session:42(integer) 3600Reading and removing a TTL
Section titled “Reading and removing a TTL”| Command | Returns |
|---|---|
| TTL | Seconds left |
| PTTL | Milliseconds left |
| NTTL | Nanoseconds left (HotKV only) |
| EXPIRETIME | The expiry as a Unix time in seconds |
| PEXPIRETIME | The expiry as a Unix time in milliseconds |
| PERSIST | Removes the TTL; 1 if there was one |
All of the reading commands return -2 when the key does not exist and -1
when it exists without a TTL. Commands that replace a value, such as SET
without KEEPTTL, clear its TTL. Commands that change a value in place, such
as INCR, LPUSH or HSET, keep it. RENAME carries the TTL over to the
new name.
Nanosecond precision
Section titled “Nanosecond precision”NEXPIRE, NEXPIREAT and NTTL are HotKV commands that work in nanoseconds,
for short-lived keys where milliseconds are too coarse, such as
sub-millisecond leases or test fixtures. NEXPIRE takes no NX, XX, GT
or LT option.
127.0.0.1:6379> SET lease:job:9 "worker-3"OK127.0.0.1:6379> NEXPIRE lease:job:9 1500000000(integer) 1127.0.0.1:6379> NTTL lease:job:9(integer) 1499986211127.0.0.1:6379> PTTL lease:job:9(integer) 1499The exact NTTL value depends on how much time passed between the two
commands. Other clients and the regular TTL/PTTL commands see the same
expiry, rounded to their unit.
When expired keys disappear
Section titled “When expired keys disappear”From a client’s point of view a key is gone as soon as its TTL runs out: every
command treats it as missing, and TTL returns -2. Behind the scenes the
key is removed either when a command next touches it or by a background
sweep that runs a number of times per second (hz, 10 by default;
active-expire-effort from 1 to 10 makes the sweep work harder). Memory used
by keys that expire but are never read again is reclaimed by that sweep, so
it may lag the TTL slightly. If you listen for expired
keyspace notifications, they are sent
when the key is actually removed, which can be a little after the TTL ran out.
Compatibility
Section titled “Compatibility”Key commands, their options, replies and errors follow Redis 7. HotKV adds
NEXPIRE, NEXPIREAT and NTTL. OBJECT REFCOUNT always returns 1, and
UNLINK behaves like DEL.
Was this page helpful?
Thanks for the feedback.