Strings

beginner redis strings data-structures

The Redis string isn’t just text. It’s a binary-safe blob that can hold anything — UTF-8, JPEGs, gzipped JSON, protobuf, you name it — up to 512 MB per value. It’s the simplest and most-used Redis type.

In simple language: think of it like the value half of any key-value store, except Redis gives us a bunch of clever operations on top.

The basics

SET user:42:name "Alice"
GET user:42:name
# "Alice"

SET page:home:title "Welcome" EX 3600   # expires in 1 hour
TTL page:home:title
# (integer) 3600

DEL user:42:name

EX sets seconds; PX sets milliseconds. NX makes SET only succeed if the key doesn’t exist (useful for locks); XX only if it does exist.

SET lock:job:99 "worker-1" NX EX 30
# (nil) if someone else already holds it

Atomic counters with INCR

This is one of the most underrated Redis tricks. INCR is atomic — Redis runs it to completion under the single-threaded loop, so no race conditions even with 1000 clients hammering it.

SET page:home:views 0
INCR page:home:views
# (integer) 1
INCRBY page:home:views 5
# (integer) 6
DECR page:home:views
# (integer) 5

Use cases: page view counters, request counters for rate limiting, generating sequential IDs, stock levels in flash sales.

# Cheap unique ID generator
INCR id:invoice
# (integer) 100451

Range operations

SET msg "Hello, Redis world!"
GETRANGE msg 0 4
# "Hello"
GETRANGE msg 7 11
# "Redis"
APPEND msg " — and beyond"
STRLEN msg
# (integer) 32

GETRANGE is O(N) over the slice length, not the full string. Useful for things like storing log buffers or pulling a chunk of a large blob.

Multi-get / multi-set

Batch reads and writes save network round trips.

MSET user:1:name "Alice" user:2:name "Bob" user:3:name "Carol"
MGET user:1:name user:2:name user:3:name
# 1) "Alice"
# 2) "Bob"
# 3) "Carol"

Visualizing it

String Layout
key
user:42:name
value (≤ 512MB, binary-safe)
"Alice"
+ optional TTL, + optional encoding (int / embstr / raw)

Encoding under the hood

Redis stores small integers as actual long values (int encoding) — super compact. Short strings under 44 bytes use embstr (single allocation with the object header). Larger strings use raw. The encoding is invisible to us but matters for memory tuning.

SET counter 42
OBJECT ENCODING counter
# "int"
SET name "Alice"
OBJECT ENCODING name
# "embstr"

Cache pattern in a real app

async function getProduct(id) {
  const cached = await redis.get(`product:${id}`);
  if (cached) return JSON.parse(cached);

  const row = await db.query("SELECT * FROM products WHERE id=$1", [id]);
  await redis.set(`product:${id}`, JSON.stringify(row), "EX", 3600);
  return row;
}

When not to use strings

If we’re storing a JSON object and we’ll only ever update one field, use a hash instead — we’d otherwise have to read the whole blob, parse, mutate, re-serialize, and write back. Strings are best when the value is opaque to Redis or atomic counters/flags.