Strings
Strings are Redis’ simplest data type — binary-safe byte sequences up to 512 MB. Commands: SET, GET, INCR, APPEND, EXPIRE. Counters, caches, JSON blobs, rate limits — all built on strings.
SET, GET, INCR, EXPIRE, real recipes
EXAMPLE
# 1) Basic SET / GET
SET name "Ada"
GET name # "Ada"
MSET k1 v1 k2 v2 k3 v3 # multi-set
MGET k1 k2 k3 # multi-get
# 2) SET with options
SET cache:user:42 'Ada' EX 60 # expires in 60 seconds
SET cache:user:42 'Ada' PX 60000 # expires in 60000 ms
SET cache:user:42 'Ada' EXAT 1717000000 # expires at Unix timestamp
SET counter 0 NX # NX = only if NOT exists
SET counter 10 XX # XX = only if EXISTS
SET key value KEEPTTL # keep existing TTL on overwrite
SET key value GET # set new, return old (atomic)
# 3) Counters (atomic)
INCR pageviews # 1
INCR pageviews # 2
INCRBY pageviews 10 # 12
DECR pageviews # 11
DECRBY pageviews 5 # 6
INCRBYFLOAT balance 49.99 # works with floats
# 4) TTL / EXPIRE
EXPIRE key 60 # 60s TTL
PEXPIRE key 60000 # in ms
EXPIREAT key 1717000000 # absolute Unix time
TTL key # remaining seconds (-2 = missing, -1 = no TTL)
PERSIST key # remove TTL
# 5) Append + length
APPEND log 'first line\n'
APPEND log 'second line\n'
STRLEN log
# 6) Range + substring
GETRANGE key 0 9 # first 10 chars
SETRANGE key 6 'World' # overwrite from index 6
# 7) Bitops (binary-safe)
SETBIT visitors:2026-06-08 42 1 # mark user 42 as visited
GETBIT visitors:2026-06-08 42 # 1
BITCOUNT visitors:2026-06-08 # count set bits = unique visitors today
BITOP AND visited:both visitors:2026-06-08 visitors:2026-06-09
# === Real recipes ===
# 8) Cache pattern — get-or-fetch
GET user:42 # try cache
# if (nil) → fetch from DB, SET with EXPIRE
SET user:42 '{"name":"Ada","email":"a@x.com"}' EX 300
# 9) Rate limiting (fixed window) — INCR + EXPIRE
# Per user, per minute
INCR rate:user:42:202606081430
EXPIRE rate:user:42:202606081430 60 # only on first hit
# Reject if INCR returns > 100
# 10) Distributed lock (simple, NOT failsafe)
SET lock:resource value NX EX 30 # acquire if not held
# work
DEL lock:resource # release
# Better: use Redlock library or SET ... NX EX + token + Lua release script
# 11) Cache stampede protection (probabilistic early expiration)
# When TTL is close to expiring, randomly refresh before all clients miss simultaneously.
# 12) Counter sharded across N keys (high write throughput)
INCR counter:1
INCR counter:2
# To read: sum the shards.
SUM = 0
for i in 1..N:
SUM += GET counter:$i
# 13) Distinct daily counts (bitmap)
SETBIT visitors:2026-06-08 42 1
SETBIT visitors:2026-06-08 99 1
BITCOUNT visitors:2026-06-08
# 14) Object cache (JSON blob)
SET user:42 '{"name":"Ada","email":"a@x.com","role":"admin"}' EX 600
GET user:42
# Use HASH instead if you frequently update individual fields
# 15) Atomic increment with cap (Lua)
EVAL "local v = tonumber(redis.call('INCR', KEYS[1]))
if v > tonumber(ARGV[1]) then
redis.call('DECR', KEYS[1])
return -1
end
return v" 1 inventory:item:A100 100
# 16) GETSET — atomic read + write
GETSET counter 0 # gets old value, resets to 0
# Use for snapshot + reset patterns
# 17) Sliding window rate limit (more accurate than fixed window)
# Use sorted sets — see redis/sorted-sets lesson
# 18) Multi-region / cluster considerations
# - Single shard: all strings on one node, easy
# - Cluster: keys distributed by hash slot; use { } in keys for grouping (e.g. {user:42}:profile, {user:42}:settings → same shard)
# 19) Node + ioredis
import Redis from 'ioredis';
const redis = new Redis();
await redis.set('user:42', 'Ada', 'EX', 60);
const v = await redis.get('user:42');
await redis.incr('pageviews');
await redis.expire('pageviews', 60);
# Pipeline (multiple commands in one round trip)
const pipeline = redis.pipeline();
pipeline.set('a', 1);
pipeline.set('b', 2);
pipeline.incr('c');
const results = await pipeline.exec();
# Transactions (MULTI / EXEC)
const multi = redis.multi();
multi.set('a', 1);
multi.incr('a');
const results = await multi.exec();
# 20) Common bugs
# • Forgetting EXPIRE — keys grow forever
# • Storing huge values (>1MB) — slow ops + memory pressure
# • Using GET + SET pattern instead of INCR for counters — race conditions
# • Caching with KEEPTTL forgotten — old TTLs override new ones
# • Mixing TTL semantics (EX vs PX vs EXAT) and getting timing wrong
# 21) Memory tips
# • Use HASH for small objects (memory-efficient)
# • Use bitmaps / hyperloglog for cardinality counting
# • Monitor with MEMORY USAGE <key>
# • Set maxmemory-policy (allkeys-lru, allkeys-lfu, volatile-ttl)
# 22) When NOT to use string commands
# • Field-level updates on objects → use HASH (HSET / HGET / HINCRBY)
# • Append-only logs / replay → use STREAM (XADD / XREAD)
# • Unique sets of IDs → use SET (SADD / SISMEMBER)
# • Ranked / scored data → use SORTED SET (ZADD / ZRANGE)
Why it matters
Strings power most Redis use cases: counters via INCR, caches via SET EX, rate limits via INCR + EXPIRE, bitmaps for unique visitors. Always set a TTL on cache keys — without one, memory grows until something breaks.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
SET hits 0 INCR hits # 1 INCRBY hits 10 # 11 APPEND name " Lovelace" GETRANGE name 0 2 # "Ada"Try it Yourself »
Exercise
Atomic increment.
hits
Four letters.
Discussion
Loading…