In distributed projects, the primary key ID of the data table may generally exist in two forms: UUID or self-increment ID. UUID is easy to understand and is the easiest to implement. However, the disadvantage is that the primary key ID in the data table is a 32-bit string, and the performance will be relatively poor in big data queries, etc., so when the requirements allow, we usually give priority to using self-increment ID instead of UUID.
In a distributed project, if the primary key ID of your data table is self-incrementing ID, then the common ways to generate the primary key ID of the object are:
-
Snowflake Algorithm
Advantages: Simple implementation
Disadvantages: The generation ID is long and the generation ID is discontinuous, which will cause ID waste.
-
Use the framework's own ID generator, such as @AutoID from MybatisPlus
Advantages: Relying on the framework, simple implementation
Disadvantage: Cannot get the primary key ID of the object before inserting it
-
Use Redis cache to generate primary key ID, etc.
Advantages: The generated ID is continuous, and the data is readable in the table
Disadvantages: With the help of Redis cache, the implementation is more complex than the first two
In this article, we mainly introduce how to generate the self-increment ID of the object through Redis.
1. Cache implementation principle
The method of realizing the self-increasing object ID through Redis is mainly through RedisINCR
andINCRBY
Commands to increment key values to realize the function of counters. The main reason is that Redis is a single-threaded model, and all commands are atomic operations and there will be no race conditions.
Pay attention to the following features and precautions when using
Atomicity:
INCR
、INCRBY
andINCRBYFLOAT
Commands are atomic, which means that if multiple clients execute these commands at the same time, Redis will ensure that there will be no race conditions for each command to be executed.Data type requirements: The values required by these commands are integers (
INCR
andINCRBY
) or floating point number (INCRBYFLOAT
). If the value corresponding to the key is not a numeric type, Redis will return an error.performance:Redis is single-threaded, and all commands are atomic, so it performs very well when executing these commands in high concurrency environments.
The situation where the key does not exist: If executed
INCR
orINCRBY
When the key does not exist, Redis will automatically create this key and initialize its value to 0, and then increment.
2. Redis tool class realizes automatic ID generation
After understanding the principle implemented through Redis, it is how to design caches to ensure that the data in each table will not affect each other.
Taking the user table as an example, the corresponding model is UserModel, then we can use the model name as the key and the current largest ID as the value to store it. Because the corresponding model name of each table is different, there will definitely be no key duplication here.
The following are some tools and methods to generate and obtain the self-increment ID of the object in Redis, which facilitates direct call when creating a new object.
@Component("redisUtils") @RequiredArgsConstructor @Slf4j public class RedisUtils implements InitializingBean { private final StringRedisTemplate stringRedisTemplate; private static RedisUtils redisUtils; @Override public void afterPropertiesSet() { redisUtils = this; } /** * Get the self-increasing ID */ public static <T> Integer getIncr(Class<T> tClass) { try { String key = (); Long increment = ().increment(key, 1); return (increment); } catch (Exception e) { (); throw new RedisSystemException((), e); } } /** * Get the self-increment ID, specify the incremental growth rate, and only request Redis once when creating objects in batches * If you create five objects in batches, delta is equal to 5 */ public static <T> Integer getIncr(Class<T> tClass, int delta) { try { String key = (); Long increment = ().increment(key, delta); return (increment); } catch (Exception e) { (); throw new RedisSystemException((), e); } } /** * Cache warm-up, used to initialize the maximum ID of each table in Redis at project startup */ public static <T> long incrPreheat(Class<T> tClass, Long maxValue) { if (maxValue == null) { maxValue = 0L; } if (maxValue < 0) { //Exception is thrown, the initial value of preheating cannot be less than 0 } Integer incr = getIncr(tClass); if (maxValue <= incr) { return incr; } return getIncr(tClass, (int) (maxValue - incr)); } }
explain:InitializingBean is an extension interface provided by Spring. The InitializingBean interface provides bean with processing methods after property initialization. It only has one afterPropertiesSet method. Any class inheriting the interface will execute the method after the bean's property is initialized.
3. Cache preheating
Cache warm-up means that when the project starts, the current maximum primary key ID of each table is preloaded into Redis. In this way, when used later, you can directly obtain the next ID from Redis. There is no need to access the database to query the maximum ID. Cache warm-up is generally established in Redis's special initialization class so that the class can be run when starting the project. The specific details are as follows:
/** * Cache preheating * * @author hxy */ @Component @DependsOn("redisUtils") @RequiredArgsConstructor public class RedisInit { private final UserMapper userMapper; /** * Get the current maximum ID in each table and warm up to redis */ @PostConstruct public void init() { (, ()); } }
explain:The @DependsOn annotation can be defined in classes and methods, which means that my component depends on another component, which means that the dependent component will be registered in the IOC container before the component.
Explanation: @PostConstruct annotation
In Java, the @PostConstruct annotation is usually used to mark a method that means that the method is executed immediately after class instantiation (after object creation through constructor).
The method with @PostConstruct annotation will be executed after all dependencies of the object have been injected. By using the @PostConstruct annotation, we can ensure that these operations are performed only after the object is fully created and initialized. This annotation is usually used in Dependency Injection frameworks, such as Spring.
The @PostConstruct annotation can be used on any class's methods, but it is most commonly used to mark initialization methods in Bean classes in Spring Framework.
4. Model layer encapsulation call
When instantiating a model object, you need to assign the primary key ID of the model. At this time, you need to obtain the corresponding primary key ID from Redis. We can build a newInstance method or parameter construct in the model class to specifically generate an object that needs to be assigned a primary key ID. In the method, call the getIncr method of the redis tool class, obtain the latest and smallest unused primary key ID and assign it to the new model.
@Getter @Setter public class UserModel { private Integer id; private String name; private Integer age; private String tel; public UserModel() { } /** * Provide a parameterized structure that can generate self-increment IDs. It is called when generating self-increment IDs. */ public UserModel(boolean autoId) { if (autoId) { = (); } } }
If you need to create objects in batches and assign primary key IDs to the object, you can use them as follows, so that Redis can only be called once, avoiding repeated calls to Redis to affect performance.
@Service @RequiredArgsConstructor public class UserServiceImpl { private final UserMapper userMapper; public void batchAddUser(){ int addNumber = 10; //Generate the ID of the custom span in advance, for example, the maximum ID before is 6, and the incr obtained after obtaining is 16 Integer incr = (, addNumber); List<UserModel> userModels = new ArrayList<>(); for (int i = 0; i < addNumber; i++) { UserModel userModel = new UserModel(); //Each primary key ID is used in decreasing order (incr--); (userModel); } (userModels); } }
At this point, the method of obtaining the self-increase of the object through Redis has been completed. If there are other scenario requirements during use, you can expand the methods in redisUtils.
This is the article about using Redis to generate self-increase IDs for objects. For more related content on self-increase IDs for Redis, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!