RedisJSON
Standard Redis is a flat key-value store. While you have Hashes, you cannot easily nest data or query deep into a structure without retrieving the entire object. RedisJSON solves this by adding support for hierarchical documents as a first-class data type.
1. What is RedisJSON?
RedisJSON transforms Redis into a high-performance document database. It allows you to store, update, and fetch JSON values from Redis keys.
The “Tree” Architecture
Unlike a simple SET key value where the value is a blob of text, RedisJSON parses your JSON and stores it as a binary tree structure in memory.
- O(1) Access: Accessing a nested field like
$.store.book[0].titledoes not require parsing the whole JSON string. Redis traverses the tree pointers directly to the target node. - Atomic Updates: You can modify a single number deep in the tree without touching the rest of the document.
- Memory Efficient: Keys are stored once in a dictionary, reducing memory usage for documents with repetitive field names.
graph TD
subgraph Memory_Layout [Redis Memory Layout]
style Memory_Layout fill:var(--bg-main),stroke:var(--border-muted),color:var(--fg-default)
Root((Root Key
user:1001))
style Root fill:var(--accent-blue),stroke:var(--accent-blue),color:#ffffff
Obj1{Object}
style Obj1 fill:var(--accent-green),stroke:var(--accent-green),color:#ffffff
Name(name: "Alice")
style Name fill:var(--bg-subtle),stroke:var(--border-muted),color:var(--fg-muted)
Stats{stats}
style Stats fill:var(--accent-green),stroke:var(--accent-green),color:#ffffff
Login(login: 5)
style Login fill:var(--bg-subtle),stroke:var(--border-muted),color:var(--fg-muted)
Score(score: 98.5)
style Score fill:var(--bg-subtle),stroke:var(--border-muted),color:var(--fg-muted)
Root --> Obj1
Obj1 --> Name
Obj1 --> Stats
Stats --> Login
Stats --> Score
end
2. Key Operations
The primary commands revolve around JSON.SET and JSON.GET, using JSONPath syntax to target specific nodes.
Java & Go Implementation
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.json.Path2;
import org.json.JSONObject;
public class RedisJsonExample {
public static void main(String[] args) {
JedisPooled client = new JedisPooled("localhost", 6379);
// 1. Store a JSON object
JSONObject user = new JSONObject()
.put("name", "Alice")
.put("age", 30)
.put("stats", new JSONObject().put("login", 5).put("score", 100));
// JSON.SET user:1 $ '{"name":"Alice",...}'
client.jsonSet("user:1", Path2.ROOT_PATH, user);
// 2. Get a specific field
// JSON.GET user:1 $.stats.login
Object loginCount = client.jsonGet("user:1", Path2.of("$.stats.login"));
System.out.println("Login Count: " + loginCount); // 5
// 3. Atomic Increment
// JSON.NUMINCRBY user:1 $.stats.login 1
client.jsonToggle("user:1", Path2.of("$.stats.login"));
// Note: Jedis method names vary, using generic command for clarity:
// client.sendCommand(JsonCommand.NUMINCRBY, "user:1", "$.stats.login", "1");
}
}
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Stats Stats `json:"stats"`
}
type Stats struct {
Login int `json:"login"`
Score int `json:"score"`
}
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
// 1. Store a JSON object
user := User{
Name: "Alice",
Age: 30,
Stats: Stats{Login: 5, Score: 100},
}
// JSON.SET user:1 $ ...
err := rdb.JSONSet(ctx, "user:1", "$", user).Err()
if err != nil {
panic(err)
}
// 2. Get a specific field
// JSON.GET user:1 $.stats.login
val, _ := rdb.JSONGet(ctx, "user:1", "$.stats.login").Result()
fmt.Println("Login Count:", val) // "[5]" (returns array of matches)
// 3. Atomic Increment
// JSON.NUMINCRBY user:1 $.stats.login 1
rdb.JSONNumIncrBy(ctx, "user:1", "$.stats.login", 1)
}
3. Why use RedisJSON?
- Partial Updates: In standard Redis, updating a field in a large JSON string requires fetching the whole string (network cost), decoding it (CPU cost), modifying it, encoding it, and saving it back. With RedisJSON, you send
JSON.SET key $.field value, and the server updates just that pointer in the tree. - Atomicity: Updates to nested fields are atomic. Two clients can update different fields of the same JSON document simultaneously without overwriting each other’s changes.
- Type Safety: RedisJSON enforces JSON validity. You cannot insert malformed JSON.
4. Interactive: JSON Path Visualizer
Type a JSONPath query to see how Redis traverses the document to find your data.
$is the root..storeaccesses the store object.[0]accesses the first element of an array.
Document Structure
Query Result