Strings, Lists, and Hashes
[!NOTE] This module explores the core principles of Strings, Lists, and Hashes, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
1. Strings: The Universal Building Block
A Redis String is binary-safe, meaning it can store text, serialized JSON, images, or raw bytes. It’s the most basic but versatile type.
Internals: SDS (Simple Dynamic String)
Redis does not use C-style strings (char*). It uses SDS.
- O(1) Length: Stores length in a header.
- Buffer Overflow Protection: Tracks free space.
- Binary Safe: Can store null bytes
\0.
Use Case: Distributed Rate Limiter
Prevent abuse by limiting API calls (e.g., 10 requests per minute).
Java
public boolean allowRequest(String userId) {
String key = "rate:" + userId;
long current = jedis.incr(key);
if (current == 1) {
jedis.expire(key, 60); // Reset after 1 minute
}
return current <= 10;
}
Go
func AllowRequest(ctx context.Context, userID string) bool {
key := "rate:" + userID
// Atomically increment
count, _ := rdb.Incr(ctx, key).Result()
if count == 1 {
rdb.Expire(ctx, key, 60*time.Second)
}
return count <= 10
}
2. Lists: Queues and Stacks
Redis Lists are Doubly Linked Lists.
- Push/Pop: O(1) at head or tail.
- Access: O(N) by index (slow for middle elements).
Internals: Quicklist
To save memory, Redis compresses small lists into a Ziplist (contiguous memory chunk). Large lists become a Quicklist (linked list of ziplists).
Use Case: Job Queue
Producer pushes jobs, Consumer processes them.
[!TIP] Blocking Pop (
BLPOP): Instead of polling (looping withLPOP), useBLPOP. It puts the connection to sleep until an item arrives. Zero CPU waste.
Java
// Producer
jedis.lpush("jobs", "job_123");
// Consumer (Blocks for 0 = infinite)
List<String> result = jedis.blpop(0, "jobs");
String job = result.get(1); // [key, value]
process(job);
Go
// Producer
rdb.LPush(ctx, "jobs", "job_123")
// Consumer
res, err := rdb.BLPop(ctx, 0, "jobs").Result()
if err == nil {
job := res[1] // [key, value]
Process(job)
}
3. Hashes: The Object Store
Hashes map string fields to string values. Perfect for storing objects (User, Product).
- Memory Efficient: Small hashes are encoded as Ziplists, consuming very little RAM compared to full overhead of keys.
- Atomic Field Updates:
HINCRBYcan increment just one field (e.g.,cart_items) without reading the whole object.
4. Interactive: Command Pipeline Visualizer
Visualize how commands affect data structures.