Databases 9 min read

Why Big Companies Avoid SET for User Data: A Redis Storage Guide

The article compares storing user objects in Redis using plain SET with JSON versus using HASH fields, providing code demos, benchmark results, memory and concurrency analysis, and practical guidelines on when to choose each approach for optimal performance and safety.

Java Companion
Java Companion
Java Companion
Why Big Companies Avoid SET for User Data: A Redis Storage Guide

Scenario Simulation

Store a user object {"id":1001,"name":"张三","age":28,"vip":true}. Two schemes are considered:

Scheme 1: Store the whole JSON string with SET user:1001 ... Scheme 2: Store each field in a Redis hash with

HSET user:1001 id 1001 name 张三 age 28 vip 1

Object Definition

public class User {
    private int id;
    private String name;
    private int age;
    private boolean vip;
    public String toJson() { return new Gson().toJson(this); }
    public static User fromJson(String json) { return new Gson().fromJson(json, User.class); }
}

String Storage Demo (JSON serialization)

public class StringStorageDemo {
    private static final Jedis jedis = new Jedis("localhost");
    public void saveUser(User user) {
        jedis.set("user:" + user.getId(), user.toJson());
    }
    public User getUser(int id) {
        String json = jedis.get("user:" + id);
        return User.fromJson(json);
    }
    public void updateAge(int id, int newAge) throws Exception {
        // non‑atomic read‑modify‑write
        String key = "user:" + id;
        User user = User.fromJson(jedis.get(key));
        user.setAge(newAge);
        jedis.set(key, user.toJson());
    }
}

Hash Storage Demo (field‑level storage)

public class HashStorageDemo {
    private static final Jedis jedis = new Jedis("localhost");
    private Map<String, String> toMap(User user) {
        Map<String, String> map = new HashMap<>();
        map.put("id", String.valueOf(user.getId()));
        map.put("name", user.getName());
        map.put("age", String.valueOf(user.getAge()));
        map.put("vip", user.isVip() ? "1" : "0");
        return map;
    }
    public void saveUser(User user) {
        jedis.hset("user:" + user.getId(), toMap(user));
    }
    public User getUser(int id) {
        Map<String, String> map = jedis.hgetAll("user:" + id);
        return new User(
            Integer.parseInt(map.get("id")),
            map.get("name"),
            Integer.parseInt(map.get("age")),
            map.get("vip").equals("1")
        );
    }
    public void updateAge(int id, int newAge) {
        jedis.hset("user:" + id, "age", String.valueOf(newAge));
    }
}

Performance Test Recommendation

Run redis-benchmark -n 100000 -t set,hset to compare write performance.

Benchmark Results (10 000 operations)

Operation   String Scheme   Hash Scheme   Improvement
Write time   4200 ms         850 ms        395%
Read time    3800 ms         650 ms        485%
Network      12 MB           2.3 MB        422%

When to Use String

Simple values that need expiration

Counters or other single‑value scenarios

Storing serialized binary data

Why Hash Is Recommended

1. Memory Optimization

Redis hash uses a ziplist when field count < 512 and value size < 64 bytes; it converts to a hashtable as it grows.

Memory measured with redis-rdb-tools:

String storage ≈ 120 bytes per object

Hash storage ≈ 65 bytes per object (≈ 45 % saving)

2. Operation Efficiency

Read a single field: GET + deserialization vs HGET Modify a single field: GET → modify → SET vs HSET Batch operations: multiple commands vs HMSET/HMGET in one round‑trip

3. Concurrency Safety

# Non‑atomic (String)
GET user:1001 → modify age → SET user:1001
# Atomic (Hash)
HSET user:1001 age 29

4. Extensibility

Adding a new field with a hash requires a single HSET; the string approach requires a full read‑modify‑write cycle.

Key Difference Illustration

Updating a field with String:

Get the whole JSON

Deserialize to object

Modify the field

Serialize back to JSON

Store the new JSON

Updating a field with Hash: direct HSET of the changed field (network traffic reduced by > 60 %).

Concurrency risk: the multi‑step string update can cause data overwrite, whereas a single HSET is atomic.

Java Development Best Practices

Scenarios for Using Hash

Frequent partial updates (e.g., user profile changes)

Objects with more than three fields (memory advantage appears)

Need for atomic field operations

Exception Cases for Using String

void saveOrderSnapshot(Order order) {
    jedis.set("order:" + order.getId(), order.toJson());
}

Performance Optimization Tip

public void batchUpdate(Map<Integer, User> users) {
    Pipeline pipeline = jedis.pipelined();
    users.forEach((id, user) -> {
        pipeline.hset("user:" + id, toMap(user));
    });
    pipeline.sync();
}

Conclusion

Hash storage provides memory efficiency, faster single‑field operations, atomic updates, and easier schema evolution compared with storing the whole object as a string.

JavaPerformanceRedisData ModelingstringHash
Java Companion
Written by

Java Companion

A highly professional Java public account

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.