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.

Strings

Last updated

A string is the simplest HotKV value: one key, one value. Values are binary safe, so a string can hold text, a serialized JSON document, a protobuf message, an image thumbnail, or a number that HotKV can increment for you. Strings are the right choice for cached objects, session tokens, feature flags, counters, and simple locks.

Strings also back two other types: bitmaps and HyperLogLogs are strings that dedicated commands read and write in a special way.

SET writes a value and replaces whatever the key held before, of any type. GET reads it back, and returns nil when the key does not exist.

127.0.0.1:6379> SET user:42:name "Ada"
OK
127.0.0.1:6379> GET user:42:name
"Ada"
127.0.0.1:6379> GET user:43:name
(nil)
127.0.0.1:6379> APPEND user:42:name " Lovelace"
(integer) 12
127.0.0.1:6379> STRLEN user:42:name
(integer) 12
127.0.0.1:6379> GETRANGE user:42:name 0 2
"Ada"
127.0.0.1:6379> DEL user:42:name
(integer) 1

Read or write several keys in one round trip with MSET and MGET. MSET is atomic: other clients see all of the new values or none of them.

127.0.0.1:6379> MSET config:theme "dark" config:lang "en"
OK
127.0.0.1:6379> MGET config:theme config:lang config:missing
1) "dark"
2) "en"
3) (nil)

SET takes NX (only if the key is missing) and XX (only if it exists). With GET it returns the value it replaced.

127.0.0.1:6379> SET flag:beta "on" NX
OK
127.0.0.1:6379> SET flag:beta "off" NX
(nil)
127.0.0.1:6379> SET flag:beta "off" XX GET
"on"

MSETNX sets several keys only if none of them exist. GETDEL reads a value and deletes the key in one step, and GETSET is the older form of SET ... GET.

A string that holds a base-10 integer can be changed in place with INCR, INCRBY, DECR and DECRBY. A missing key counts as 0. Every call is atomic, so many clients can bump the same counter without losing updates.

127.0.0.1:6379> INCR page:home:views
(integer) 1
127.0.0.1:6379> INCRBY page:home:views 10
(integer) 11
127.0.0.1:6379> DECR page:home:views
(integer) 10
127.0.0.1:6379> SET price "10.5"
OK
127.0.0.1:6379> INCRBYFLOAT price 0.1
"10.6"
127.0.0.1:6379> SET name "Ada"
OK
127.0.0.1:6379> INCR name
(error) ERR value is not an integer or out of range

Integer counters are signed 64-bit. A change that would leave that range is refused with ERR increment or decrement would overflow and the value is left as it was. INCRBYFLOAT works on any number and refuses a result that would be NaN or infinite.

Any key can be given a time to live. For strings, the most convenient way is to set the value and the expiry together:

127.0.0.1:6379> SET session:9f2c "user:42" EX 1800
OK
127.0.0.1:6379> TTL session:9f2c
(integer) 1800
127.0.0.1:6379> GETEX session:9f2c EX 3600
"user:42"
127.0.0.1:6379> TTL session:9f2c
(integer) 3600
127.0.0.1:6379> SET session:9f2c "user:42"
OK
127.0.0.1:6379> TTL session:9f2c
(integer) -1

Things to know:

  • A plain SET on an existing key clears its time to live. Pass KEEPTTL to keep it.
  • SET accepts EX (seconds), PX (milliseconds), EXAT and PXAT (Unix time). SETEX and PSETEX are the older forms.
  • GETEX reads a value and sets, changes, or removes (PERSIST) its expiry in one step: a sliding session window in one command.
  • MSETEX sets several keys with one shared expiry, and replies 1, or 0 when an NX or XX condition stops it.
  • APPEND, INCR and the other in-place commands keep the key’s time to live.
  • HotKV can also expire keys with nanosecond precision: NEXPIRE, NEXPIREAT and NTTL.

The general expiry commands (EXPIRE, TTL, PERSIST and the rest) are covered in Keys and expiration.

Cache entries. Store the serialized object with SET key value EX seconds so stale entries disappear on their own, and fall back to your database on a nil GET.

A simple lock. SET lock:report <random-token> NX PX 30000 takes the lock only if nobody holds it, and the expiry releases it if the holder crashes. Release it only if the stored token is still yours, for example with a short Lua script that compares and deletes in one step.

Rate counters. INCR a key named after the current window (for example hits:user:42:202610021405) and give it an expiry on first use. For a ready-made limiter, see Rate limiting.

Partial reads and writes. GETRANGE and SETRANGE read or overwrite a byte range without moving the whole value. LCS finds the longest common subsequence of two string values.

A single string can be as large as proto-max-bulk-len, 512 MB by default. The setting accepts values from 1mb up to 4294967295 bytes. It is also the longest value APPEND and SETRANGE may produce: past it they reply ERR string exceeds maximum allowed size (proto-max-bulk-len). See Configuration.

Large values cost bandwidth and latency on every read. If you often need only part of an object, a hash lets you read and update single fields.

String commands take the same arguments and give the same replies as in Redis and Valkey. HotKV does not have the compare-and-set options of newer Redis releases (SET ... IFEQ, IFNE, IFDEQ, IFDNE) or the DELEX and DIGEST commands; use a transaction with WATCH, or a Lua script, to make a write depend on the current value.

Command What it does
SET Store a value, with optional expiry and NX/XX/GET
GET Read a value
MSET / MGET Write or read several keys at once
MSETNX Set several keys only if none exist
MSETEX Set several keys with one shared expiry
SETNX Set only if the key is missing (older form of SET NX)
SETEX / PSETEX Set with an expiry in seconds or milliseconds
GETEX Read and change the expiry
GETDEL Read and delete
GETSET Set and return the old value
APPEND Append to the value
STRLEN Length of the value in bytes
GETRANGE / SUBSTR Read a byte range
SETRANGE Overwrite a byte range
INCR / INCRBY Increment an integer
DECR / DECRBY Decrement an integer
INCRBYFLOAT Add a floating-point amount
LCS Longest common subsequence of two values
import hotkv
client = hotkv.HotKV(host="localhost", port=6379, decode_responses=True)
client.set("session:9f2c", "user:42", ex=1800)
print(client.get("session:9f2c")) # user:42
client.incr("page:home:views")
client.incrby("page:home:views", 10)
print(client.mget(["page:home:views", "missing"])) # ['11', None]
client.close()