SoFunction
Updated on 2025-04-25

Detailed explanation of 7 strategies for integrating Redis to implement serialization

In distributed systems, Redis is widely used as a high-performance cache and data storage solution. When integrating Redis using the SpringBoot framework, the choice of serialization policy directly affects the performance, compatibility and maintainability of the system.

Serialization is the process of converting Java objects into transferable on the network or stored on disk, while deserialization is the process of reconverting this data into Java objects.

A suitable serialization strategy can bring the following benefits:

  • Improve system performance and storage efficiency
  • Enhance cross-language and cross-platform interoperability
  • Reduce network transmission and storage overhead
  • Provides better security and maintainability

This article will introduce 7 serialization strategies for Redis in SpringBoot

1. JdkSerializationRedisSerializer

1.1 Introduction to the principle

JdkSerializationRedisSerializer is the default serialization strategy for Spring Data Redis. It uses Java native serialization mechanism() to serialize objects into byte arrays. This method requires that the serialized object must implement the Serializable interface.

1.2 Implementation method

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        (connectionFactory);
        
        // Serialize value using JdkSerializationRedisSerializer        (new JdkSerializationRedisSerializer());
        
        // Serialize keys using StringRedisSerializer        (new StringRedisSerializer());
        
        ();
        return template;
    }
}

Example of usage:

@Service
public class UserService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void saveUser(User user) {
        // User class must implement Serializable interface        ().set("user:" + (), user);
    }
    
    public User getUser(Long id) {
        return (User) ().get("user:" + id);
    }
}

1.3 Pros and cons analysis

advantage

  • Integrated in JDK, no need to introduce third-party dependencies
  • Easy to use, seamless integration with Spring Data Redis
  • Ability to handle complex object graphs and circular references

shortcoming

  • The serialized data is large in size and takes up storage space
  • Serialization/deserialization performance is poor, affecting system throughput
  • The serialization result is binary and unreadable
  • Strongly coupled to Java platform, does not support cross-language operation
  • Sensitive to class modifications, which may cause deserialization compatibility issues

Applicable scenarios

  • Internal system temporary cache, small data volume and low performance requirements
  • Simple and quick prototype system
  • All systems use Java technology stack without cross-language requirements
  • The object structure is complex and contains circular references

2. StringRedisSerializer

2.1 Introduction to the principle

StringRedisSerializer is the simplest serializer, which directly encodes data of String type into byte arrays according to the specified character set (default UTF-8). Due to its simple and efficient nature, it is usually used for key serialization of Redis, and is also suitable for scenarios where value is String type.

2.2 Implementation method

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, String> stringRedisTemplate(RedisConnectionFactory connectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        (connectionFactory);
        return template;
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        (connectionFactory);
        
        // Use StringRedisSerializer for key        (new StringRedisSerializer());
        (new StringRedisSerializer());
        
        // Other serializers can be used for value        // ...
        
        ();
        return template;
    }
}

Example of usage:

@Service
public class CacheService {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    public void saveString(String key, String value) {
        ().set(key, value);
    }
    
    public String getString(String key) {
        return ().get(key);
    }
}

2.3 Pros and cons analysis

advantage

  • The results are readable, easy to debug and monitor
  • Small memory usage, small size after serialization
  • Compatible with Redis command line client for easy management
  • Supports cross-language operation

shortcoming

  • Only String type is supported, complex objects cannot be stored directly
  • Storage objects need to be converted into strings (such as JSON) first, adding additional steps

Applicable scenarios

  • Redis key value serialization
  • Store simple string data
  • Stores data serialized into strings (such as JSON, XML)
  • You need to view or modify the data through the Redis command line
  • Multilingual system collaboration scenario

3. Jackson2JsonRedisSerializer

3.1 Introduction to the principle

Jackson2JsonRedisSerializer uses the Jackson library to serialize objects into strings in JSON format. It can handle most common Java objects and produce human-readable serialization results, while providing better performance and compression. This serializer needs to specify the serialized target type.

3.2 Implementation method

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, User> userRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, User> template = new RedisTemplate<>();
        (connectionFactory);
        
        // Use StringRedisSerializer for key        (new StringRedisSerializer());
        
        // Use Jackson2JsonRedisSerializer for User object        Jackson2JsonRedisSerializer<User> serializer = new Jackson2JsonRedisSerializer<>();
        
        // Configure ObjectMapper to enhance serialization        ObjectMapper mapper = new ObjectMapper();
        (, );
        (, 
                .NON_FINAL, );
        (mapper);
        
        (serializer);
        ();
        return template;
    }
}

Example of usage:

@Service
public class UserCacheService {
    
    @Autowired
    private RedisTemplate<String, User> userRedisTemplate;
    
    public void saveUser(User user) {
        ().set("user:" + (), user);
    }
    
    public User getUser(Long id) {
        return ().get("user:" + id);
    }
}

3.3 Pros and cons analysis

advantage

  • The serialization result is in JSON format, which is very readable
  • Good performance, moderate volume after serialization
  • JSON format supports cross-language and cross-platform operations

shortcoming

  • Need to specify the serialized target type, not flexible enough
  • Unable to deal with generic and polymorphic objects directly
  • Serializing/deserializing complex objects may require additional configuration
  • Prone to type conversion problems

Applicable scenarios

  • Known and fixed type of object cache
  • Need to read cached data across languages
  • Cache data needs to be viewed and modified manually
  • RESTful API system uses Redis as cache layer

4. GenericJackson2JsonRedisSerializer

4.1 Introduction to the principle

GenericJackson2JsonRedisSerializer is an enhanced version of Jackson2JsonRedisSerializer. It does not require specifying the target type and is able to handle any type of Java objects. It supports generics and polymorphisms by embedding type information in JSON, allowing deserialization to correctly restore object types.

4.2 Implementation method

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate&lt;String, Object&gt; redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate&lt;String, Object&gt; template = new RedisTemplate&lt;&gt;();
        (connectionFactory);
        
        // Use StringRedisSerializer for key        (new StringRedisSerializer());
        (new StringRedisSerializer());
        
        // Serialization using GenericJackson2JsonRedisSerializer        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        (jsonSerializer);
        (jsonSerializer);
        
        ();
        return template;
    }
}

Example of usage

@Service
public class CacheService {
    
    @Autowired
    private RedisTemplate&lt;String, Object&gt; redisTemplate;
    
    // Can store any type of object    public &lt;T&gt; void save(String key, T object) {
        ().set(key, object);
    }
    
    // No type conversion required    @SuppressWarnings("unchecked")
    public &lt;T&gt; T get(String key, Class&lt;T&gt; type) {
        return (T) ().get(key);
    }
    
    //Storing generic collections    public &lt;T&gt; void saveList(String key, List&lt;T&gt; list) {
        ().set(key, list);
    }
    
    // Get generic collections    @SuppressWarnings("unchecked")
    public &lt;T&gt; List&lt;T&gt; getList(String key) {
        return (List&lt;T&gt;) ().get(key);
    }
}

4.3 Pros and cons analysis

advantage

  • Supports any Java type without specifying the target class
  • Can correctly handle generic and polymorphic objects
  • The serialization result is in JSON format, which is very readable
  • Good performance, comparable to Jackson2JsonRedisSerializer

shortcoming

  • The serialization result contains type information, resulting in volume increase
  • Deserialization requires the presence of corresponding classes in the classpath
  • Deserialization may fail after class reconstruction
  • JSON containing type information is not easy to process in other languages

Applicable scenarios:

  • Store many different types of Java objects
  • Need to deal with generic collections and polymorphic objects
  • Scenarios with uncertain types or frequent changes
  • Pure Java technology stack and serialized data does not require cross-language processing

5. FastJsonRedisSerializer

5.1 Introduction to the principle

FastJsonRedisSerializer is based on Alibaba's FastJson library, which is a JSON processing library with superior performance, designed specifically for the Java platform. It provides extremely high serialization and deserialization performance, which is especially obvious when processing large amounts of data.

5.2 Implementation method

First, add FastJson dependencies:

<dependency>
    <groupId></groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

Create a custom FastJson serializer

public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

    private final Class<T> clazz;
    
    public FastJsonRedisSerializer(Class<T> clazz) {
        super();
         = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        try {
            return (t, ).getBytes(StandardCharsets.UTF_8);
        } catch (Exception ex) {
            throw new SerializationException("Could not serialize: " + (), ex);
        }
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null ||  == 0) {
            return null;
        }
        try {
            String str = new String(bytes, StandardCharsets.UTF_8);
            return (str, clazz);
        } catch (Exception ex) {
            throw new SerializationException("Could not deserialize: " + (), ex);
        }
    }
}

Configure RedisTemplate

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate&lt;String, Object&gt; redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate&lt;String, Object&gt; template = new RedisTemplate&lt;&gt;();
        (connectionFactory);
        
        // Use StringRedisSerializer to serialize key        (new StringRedisSerializer());
        (new StringRedisSerializer());
        
        // Use FastJsonRedisSerializer to serialize value        FastJsonRedisSerializer&lt;Object&gt; fastJsonRedisSerializer = new FastJsonRedisSerializer&lt;&gt;();
        (fastJsonRedisSerializer);
        (fastJsonRedisSerializer);
        
        // Configure FastJson features        ().setAutoTypeSupport(true);
        
        ();
        return template;
    }
}

Example of usage

@Service
public class ProductService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void saveProduct(Product product) {
        ().set("product:" + (), product);
    }
    
    public Product getProduct(Long id) {
        return (Product) ().get("product:" + id);
    }
    
    public void saveProductList(List<Product> products) {
        ().set("products:list", products);
    }
    
    @SuppressWarnings("unchecked")
    public List<Product> getProductList() {
        return (List<Product>) ().get("products:list");
    }
}

5.3 Pros and cons analysis

advantage

  • Excellent serialization/deserialization performance, 1.5-2 times faster than Jackson
  • Small memory usage, smaller data volume after serialization
  • The advantages are obvious when dealing with large data volume
  • Supports automatic type recognition and generic processing

shortcoming

  • There is a risk of security vulnerabilities, so you need to pay attention to version selection. It is recommended to use fastjson2 version
  • The processing of complex objects may not be as stable as Jackson
  • Not a cross-platform standard, mainly used in the Java ecosystem

Applicable scenarios

  • Systems with extremely high performance requirements
  • Cache scenarios of large data volumes
  • Pure Java technology stack application

6. Kryo Serialization

6.1 Introduction to the principle

Kryo is a fast and efficient Java serialization framework. It produces very compact serialization results, and is extremely fast serialization and deserialization.

Kryo is not only faster than Java native serialization, but also much faster than JSON serialization. It uses binary format and supports the processing of object graphs (including circular references).

6.2 Implementation method

First, add the Kryo dependency:

<dependency>
    <groupId></groupId>
    <artifactId>kryo</artifactId>
    <version>5.3.0</version>
</dependency>

Create a Kryo serializer:

public class KryoRedisSerializer&lt;T&gt; implements RedisSerializer&lt;T&gt; {
    
    private static final byte[] EMPTY_ARRAY = new byte[0];
    
    private final Class&lt;T&gt; clazz;
    private final ThreadLocal&lt;Kryo&gt; kryoThreadLocal = (() -&gt; {
        Kryo kryo = new Kryo();
        (false); // Close the registration requirement and automatically register the class        (true); // Support circular references        return kryo;
    });
    
    public KryoRedisSerializer(Class&lt;T&gt; clazz) {
         = clazz;
    }
    
    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return EMPTY_ARRAY;
        }
        
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             Output output = new Output(baos)) {
            
            Kryo kryo = ();
            (output, t);
            ();
            
            return ();
        } catch (Exception e) {
            throw new SerializationException("Error serializing object using Kryo", e);
        }
    }
    
    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null ||  == 0) {
            return null;
        }
        
        try (Input input = new Input(bytes)) {
            Kryo kryo = ();
            return (input, clazz);
        } catch (Exception e) {
            throw new SerializationException("Error deserializing object using Kryo", e);
        }
    }
}

Configure RedisTemplate:

@Configuration
public class RedisKryoConfig {
    
    @Bean
    public RedisTemplate&lt;String, Object&gt; kryoRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate&lt;String, Object&gt; template = new RedisTemplate&lt;&gt;();
        (connectionFactory);
        
        // Use StringRedisSerializer to serialize key        (new StringRedisSerializer());
        (new StringRedisSerializer());
        
        // Use KryoRedisSerializer to serialize value        KryoRedisSerializer&lt;Object&gt; kryoSerializer = new KryoRedisSerializer&lt;&gt;();
        (kryoSerializer);
        (kryoSerializer);
        
        ();
        return template;
    }
}

Example of usage:

@Service
public class UserSessionService {
    
    @Autowired
    @Qualifier("kryoRedisTemplate")
    private RedisTemplate<String, Object> kryoRedisTemplate;
    
    public void saveUserSession(String sessionId, UserSession session) {
        ().set("session:" + sessionId, session, 30, );
    }
    
    public UserSession getUserSession(String sessionId) {
        return (UserSession) ().get("session:" + sessionId);
    }
    
    public void saveComplexObject(String key, ComplexObject object) {
        ().set(key, object);
    }
    
    public ComplexObject getComplexObject(String key) {
        return (ComplexObject) ().get(key);
    }
}

6.3 Pros and cons analysis

advantage

  • Excellent serialization/deserialization performance, 3-5 times faster than JSON
  • The serialization result is very small in size and has low memory usage
  • Ability to handle complex object graphs and circular references
  • Supports object version control, with certain forward compatibility

shortcoming

  • The serialization result is binary and not readable
  • High complexity in use, thread safety needs to be considered
  • Register for a large number of types for optimal performance
  • Limited cross-language support
  • The class model may face compatibility issues after changing

Applicable scenarios

  • Scenarios with extremely high performance and memory usage
  • Serialize large number of complex object graphs
  • Circular references need to be handled efficiently
  • Pure Java technology stack, no cross-language requirements

7. Protocol Buffers (ProtoBuf)

7.1 Introduction to the principle

Protocol Buffers (ProtoBuf) is a language-independent, platform-independent, extensible structured data serialization mechanism developed by Google. It is smaller, faster and simpler than XML, and is suitable for communication protocols, data storage and other scenarios.

ProtoBuf uses a predefined message format to generate code through a dedicated compiler to achieve efficient serialization and deserialization.

7.2 Implementation method

First, add the Protocol Buffers dependency:

<dependency>
    <groupId></groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.21.7</version>
</dependency>
<dependency>
    <groupId></groupId>
    <artifactId>protobuf-java-util</artifactId>
    <version>3.21.7</version>
</dependency>

Define .proto file (src/main/proto/):

syntax = "proto3";

package ;
option java_package = "";
option java_outer_classname = "UserProto";

message User {
  int64 id = 1;
  string username = 2;
  string email = 3;
  int32 age = 4;
  
  enum UserType {
    NORMAL = 0;
    VIP = 1;
    ADMIN = 2;
  }
  
  UserType userType = 5;
  repeated string roles = 6;
  map<string, string> attributes = 7;
}

Configure the Maven plugin to generate Java code:

<plugin>
    <groupId></groupId>
    <artifactId>protobuf-maven-plugin</artifactId>
    <version>0.6.1</version>
    <extensions>true</extensions>
    <configuration>
        <protocArtifact>:protoc:3.21.7:exe:${}</protocArtifact>
        <pluginId>grpc-java</pluginId>
        <protoSourceRoot>${}/src/main/proto</protoSourceRoot>
        <outputDirectory>${}/generated-sources/protobuf/java</outputDirectory>
        <clearOutputDirectory>false</clearOutputDirectory>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Create a ProtoBuf serializer:

public class ProtobufRedisSerializer<T extends MessageLite> implements RedisSerializer<T> {
    
    private final Class<T> clazz;
    private final Method parseFromMethod;
    
    public ProtobufRedisSerializer(Class<T> clazz) {
         = clazz;
        try {
             = ("parseFrom", byte[].class);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Could not find parseFrom method on class " + ());
        }
    }
    
    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        try {
            return ();
        } catch (Exception e) {
            throw new SerializationException("Error serializing ProtoBuf message", e);
        }
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null ||  == 0) {
            return null;
        }
        try {
            return (T) (null, bytes);
        } catch (Exception e) {
            throw new SerializationException("Error deserializing ProtoBuf message", e);
        }
    }
}

Configure RedisTemplate:

@Configuration
public class RedisProtobufConfig {
    
    @Bean
    public RedisTemplate&lt;String, &gt; protobufRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate&lt;String, &gt; template = new RedisTemplate&lt;&gt;();
        (connectionFactory);
        
        // Use StringRedisSerializer to serialize key        (new StringRedisSerializer());
        
        // Use ProtobufRedisSerializer to serialize value        ProtobufRedisSerializer&lt;&gt; protobufSerializer = new ProtobufRedisSerializer&lt;&gt;();
        (protobufSerializer);
        
        ();
        return template;
    }
}

Example of usage:

@Service
public class UserProtobufService {
    
    @Autowired
    private RedisTemplate&lt;String, &gt; protobufRedisTemplate;
    
    public void saveUser( user) {
        ().set("proto:user:" + (), user);
    }
    
    public  getUser(long id) {
        return ().get("proto:user:" + id);
    }
    
    // Create sample user object    public  createSampleUser(long id, String username) {
        return ()
                .setId(id)
                .setUsername(username)
                .setEmail(username + "@")
                .setAge(30)
                .setUserType()
                .addRoles("USER")
                .addRoles("EDITOR")
                .putAttributes("department", "Engineering")
                .putAttributes("location", "Beijing")
                .build();
    }
}

Convert ProtoBuf object to business object:

@Component
public class UserMapper {
    
    // Convert ProtoBuf object to business object    public User protoToUser( protoUser) {
        User user = new User();
        (());
        (());
        (());
        (());
        
        // Convert enum type        switch (()) {
            case VIP:
                ();
                break;
            case ADMIN:
                ();
                break;
            default:
                ();
        }
        
        // Conversion list        (new ArrayList&lt;&gt;(()));
        
        // Convert Map        (new HashMap&lt;&gt;(()));
        
        return user;
    }
    
    // Convert business object to ProtoBuf object    public  userToProto(User user) {
         builder = ()
                .setId(())
                .setUsername(())
                .setEmail(())
                .setAge(());
        
        // Convert enum type        switch (()) {
            case VIP:
                ();
                break;
            case ADMIN:
                ();
                break;
            default:
                ();
        }
        
        // Conversion list        if (() != null) {
            (());
        }
        
        // Convert Map        if (() != null) {
            (());
        }
        
        return ();
    }
}

7.3 Pros and cons analysis

advantage

  • Extremely high serialization/deserialization performance
  • The serialization result is small in size, 10 times the compression ratio of XML
  • Cross-language, support multiple programming languages
  • Good forward compatibility and backward compatibility
  • In-house field type verification
  • Code generation to reduce encoding errors

shortcoming

  • Predefined message structure (Proto file) is required
  • High complexity and additional construction steps are required
  • Object model changes require regenerating code
  • Dynamic Type Support Limited
  • Difficult to view or debug cached content directly

Applicable scenarios:

  • Multilingual system collaboration scenario
  • Real-time system with extremely high performance requirements
  • Systems with strict bandwidth requirements
  • Need strict field verification
  • Scenarios with relatively stable message format and predefined
  • Large-scale distributed system

Summary and comparison

Serialization method Serialization performance Volume size readability Cross-language Complexity Type information Forward compatibility
JDK Low big Difference no Low whole Difference
String Extremely high Small excellent yes Extremely low none excellent
Jackson2Json middle middle excellent yes middle none middle
GenericJackson2Json middle Medium-to-large middle no middle whole middle
FastJson high Medium small excellent limited middle Optional middle
Kryo Extremely high Extremely small Difference no high limited middle
ProtoBuf Extremely high Extremely small Difference yes Extremely high whole excellent

Selection suggestions

Depending on different application scenarios, the following are the recommendations for selecting serialization strategies:

  • General Web Applications

    • Recommended: GenericJackson2JsonRedisSerializer
    • Reason: Good versatility, high readability, supports complex objects, moderate performance
  • High-performance Java applications

    • Recommended: Kryo or FastJsonRedisSerializer
    • Reason: Extremely high performance and low memory footprint, suitable for isomorphic Java systems
  • Cross-language system

    • Recommended: ProtoBuf or StringRedisSerializer + JSON
    • Reason: Good cross-language support, strict type control
  • Storing only simple data

    • Recommended: StringRedisSerializer
    • Reason: Very simple implementation, extremely high performance, no need to pay attention to serialization issues
  • Mixed data types

    • Recommended: Use multiple RedisTemplates, and use different serialization for different data types.
    • Reason: Optimize performance and compatibility for specific data types
  • Temporary solution

    • Recommended: JdkSerializationRedisSerializer
    • Reason: Quick implementation, no additional configuration is required, but it is not recommended to use it in production environment for a long time
  • Large-scale distributed cache

    • Recommended: ProtoBuf or Kryo
    • Reason: Very small serialization volume and high performance, reducing network and storage overhead

Summarize

Choosing the right Redis serialization strategy is critical to system performance and compatibility.

Best practices are targeting different types of data and different usage scenarios for the best performance and development experience. At the same time, advanced features such as version control and data compression should be considered to prepare for the long-term evolution of the system.

By rationally selecting and configuring Redis serialization strategies, the system performance can be significantly improved, resource consumption can be reduced, development efficiency can be improved, and the system can be more robust and scalable.

The above is a detailed explanation of the 7 strategies for integrating Redis to implement serialization. For more information about SpringBoot Redis serialization, please pay attention to my other related articles!