Skip to content

Ask the HotKV docs

Answers come only from these docs and can be wrong; check the linked pages before you rely on them.

Keys and expiration

Last updated

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.

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:42
user:42:sessions
order:2026-10:1187

Keep 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.

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 100
1) "0"
2) 1) "user:42"
2) "user:7"
127.0.0.1:6379> SCAN 0 TYPE hash
1) "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.

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"
OK
127.0.0.1:6379> TYPE greeting
string
127.0.0.1:6379> RENAME greeting welcome
OK
127.0.0.1:6379> RENAME missing other
(error) ERR no such key
127.0.0.1:6379> EXISTS welcome greeting
(integer) 1
127.0.0.1:6379> DEL welcome
(integer) 1

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-policy is 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 42
OK
127.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.

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 1
OK
127.0.0.1:6379[1]> SET user:42 "in db 1"
OK
127.0.0.1:6379[1]> SELECT 0
OK
127.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.

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).

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"
OK
127.0.0.1:6379> EXPIRE session:42 3600
(integer) 1
127.0.0.1:6379> EXPIRE session:42 60 GT
(integer) 0
127.0.0.1:6379> TTL session:42
(integer) 3600
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.

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"
OK
127.0.0.1:6379> NEXPIRE lease:job:9 1500000000
(integer) 1
127.0.0.1:6379> NTTL lease:job:9
(integer) 1499986211
127.0.0.1:6379> PTTL lease:job:9
(integer) 1499

The 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.

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.

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.