Redis Strings
Strings are the most basic Redis data type. A Redis string can hold any kind of data: text, serialized JSON, binary data like images, or even integers and floats that Redis can manipulate atomically.
When Redis Strings Are the Right Choice
Choose Redis strings when each key naturally maps to one value: a cached JSON blob, a counter, a session token, a flag, or a lock. They are the fastest and simplest Redis type when you do not need sub-field updates or collection semantics.
Key Characteristics
• Maximum size: 512 MB per string • Binary-safe: can store any binary data • Supports atomic increment/decrement for numeric values • Perfect for caching, counters, rate limiting, and session storage
Basic Operations
# Store and retrieve strings
SET user:1:name "John Doe" # Store a string
GET user:1:name # Retrieve: "John Doe"
SETNX user:1:name "Jane" # Set only if key doesn't exist (returns 0)
SETEX session:abc123 3600 "user_data" # Set with 1-hour expiration
# Multiple keys at once
MSET user:1:city "NYC" user:1:age "30" # Set multiple keys atomically
MGET user:1:name user:1:city user:1:age # Get multiple valuesAtomic Counters
Redis strings can store integers and provide atomic increment/decrement operations—perfect for counters, rate limiters, and sequences.
SET page:views 0
INCR page:views # Increment by 1 → 1
INCRBY page:views 100 # Increment by 100 → 101
DECR page:views # Decrement by 1 → 100
DECRBY page:views 50 # Decrement by 50 → 50
INCRBYFLOAT price:item1 0.99 # Floating-point increment
# Get and set atomically
GETSET counter "0" # Returns old value, sets new
GETDEL temp:key # Get value and delete keyBit Operations
Treat strings as bit arrays for memory-efficient boolean flags, bloom filters, and user tracking.
SETBIT user:1:features 0 1 # Set bit 0 to 1 (feature enabled)
SETBIT user:1:features 1 1 # Set bit 1 to 1
GETBIT user:1:features 0 # Get bit 0 → 1
BITCOUNT user:1:features # Count set bits → 2
# Bit operations across keys
BITOP AND result key1 key2 # Bitwise AND
BITOP OR result key1 key2 # Bitwise OR
BITOP XOR result key1 key2 # Bitwise XOR
BITOP NOT result key1 # Bitwise NOT
BITPOS user:1:features 1 # Position of first '1' bitString Manipulation
APPEND user:1:name " Jr." # Append → "John Doe Jr."
STRLEN user:1:name # Length → 12
GETRANGE user:1:name 0 3 # Substring → "John"
SETRANGE user:1:name 5 "Smith" # Replace at offset 5
# Get/set with expiration
SET cache:key "value" EX 3600 # Expires in 1 hour
SET cache:key "value" PX 60000 # Expires in 60 seconds
SET cache:key "value" EXAT 1707000000 # Expires at Unix timestamp
TTL cache:key # Check remaining TTLUse Case: Caching
Store serialized objects, API responses, or rendered HTML with automatic expiration.
# Cache API response for 5 minutes
SET cache:api:users:page1 '{"users":[...]}' EX 300
# Check cache first, then fetch
cached = GET cache:api:users:page1
if cached:
return json.parse(cached)
else:
data = fetch_from_database()
SET cache:api:users:page1 json.stringify(data) EX 300
return dataUse Case: Rate Limiting
Implement sliding window counters with INCR and EXPIRE.
def is_rate_limited(user_id, limit=100, window=60):
key = f"rate:{user_id}"
current = INCR(key)
if current == 1:
EXPIRE(key, window) # Set window on first request
return current > limit
# Usage
if is_rate_limited("user:123"):
return "429 Too Many Requests"Use Case: Distributed Locks
Use SET NX PX for acquiring locks with automatic expiration.
def acquire_lock(resource, owner_id, ttl_ms=30000):
key = f"lock:{resource}"
# NX = only if not exists, PX = milliseconds TTL
result = SET(key, owner_id, "NX", "PX", ttl_ms)
return result == "OK"
def release_lock(resource, owner_id):
key = f"lock:{resource}"
# Only release if we own the lock (use Lua script for atomicity)
if GET(key) == owner_id:
DEL(key)
return True
return False
# Lua script for atomic check-and-delete
EVAL "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock:resource owner_idCommon Redis String Mistakes
A common mistake is storing a large serialized object in one string and then rewriting it for tiny changes. That pattern increases write cost and contention; if field-level updates matter, hashes are often a better fit.
Related
Start here if you are still deciding whether strings, hashes, lists, or sets fit your access pattern.
Switch to hashes when one key needs multiple named fields with partial updates.
Use streams instead of strings when you need durable append-only event processing.
Helpful when validating whether Redis is actually improving the hot path you care about.