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<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); (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<String, Object> redisTemplate; // Can store any type of object public <T> void save(String key, T object) { ().set(key, object); } // No type conversion required @SuppressWarnings("unchecked") public <T> T get(String key, Class<T> type) { return (T) ().get(key); } //Storing generic collections public <T> void saveList(String key, List<T> list) { ().set(key, list); } // Get generic collections @SuppressWarnings("unchecked") public <T> List<T> getList(String key) { return (List<T>) ().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<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); (connectionFactory); // Use StringRedisSerializer to serialize key (new StringRedisSerializer()); (new StringRedisSerializer()); // Use FastJsonRedisSerializer to serialize value FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(); (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<T> implements RedisSerializer<T> { private static final byte[] EMPTY_ARRAY = new byte[0]; private final Class<T> clazz; private final ThreadLocal<Kryo> kryoThreadLocal = (() -> { Kryo kryo = new Kryo(); (false); // Close the registration requirement and automatically register the class (true); // Support circular references return kryo; }); public KryoRedisSerializer(Class<T> 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<String, Object> kryoRedisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); (connectionFactory); // Use StringRedisSerializer to serialize key (new StringRedisSerializer()); (new StringRedisSerializer()); // Use KryoRedisSerializer to serialize value KryoRedisSerializer<Object> kryoSerializer = new KryoRedisSerializer<>(); (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<String, > protobufRedisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, > template = new RedisTemplate<>(); (connectionFactory); // Use StringRedisSerializer to serialize key (new StringRedisSerializer()); // Use ProtobufRedisSerializer to serialize value ProtobufRedisSerializer<> protobufSerializer = new ProtobufRedisSerializer<>(); (protobufSerializer); (); return template; } }
Example of usage:
@Service public class UserProtobufService { @Autowired private RedisTemplate<String, > 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<>(())); // Convert Map (new HashMap<>(())); 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!