网站备案管理系统登录不上去,网络服务提供者发现未成年秘密信息应采取,网站开发公司管理模式,wordpress添加用户注册登录界面hello。大家好#xff0c;我是灰小猿#xff0c;一个超会写bug的程序猿#xff01;
在分布式项目中#xff0c;数据表的主键ID一般可能存在于UUID或自增ID这两种形式#xff0c;UUID好理解而且实现起来也最容易#xff0c;但是缺点就是数据表中的主键ID是32位的字符串我是灰小猿一个超会写bug的程序猿
在分布式项目中数据表的主键ID一般可能存在于UUID或自增ID这两种形式UUID好理解而且实现起来也最容易但是缺点就是数据表中的主键ID是32位的字符串在大数据查询等情况下性能会相对比较差所以在需求允许的情况下我们通常会优先考虑使用自增ID来代替UUID使用。
在分布式项目中如果你的数据表的主键ID是自增ID那么常见的生成对象主键ID的方式有 雪花算法 优点实现简单 缺点生成ID较长、生成ID不连续会造成ID浪费 采用框架自带的ID生成器如MybatisPlus的AutoID 优点依赖框架实现简单 缺点无法在插入对象前获取到对象的主键ID 采用Redis缓存生成主键ID等 优点生成的ID连续数据在表中的可读性好 缺点借助Redis缓存实现相比前两种较复杂
通过雪花算法生成对象主键ID的方式我们在之前的文章中已经介绍过了这篇文章我们主要介绍如何通过Redis来实现生成对象自增ID的方法。
1、缓存实现原理
通过Redis实现对象自增ID的方式主要是通过Redis的INCR 和 INCRBY 命令来对键值进行递增操作从而实现计数器的功能主要原因是Redis 是单线程模型所有命令都是原子操作不会发生竞态条件
在使用时要留意以下特点与注意事项 原子性INCR、INCRBY 和 INCRBYFLOAT 命令都是原子性的这意味着如果多个客户端同时执行这些命令Redis 会保证每个命令的执行不会发生竞态条件。 数据类型要求这些命令要求操作的值是整数INCR 和 INCRBY或浮点数INCRBYFLOAT。如果键对应的值不是数值类型Redis 会返回错误。 性能Redis 是单线程的所有命令都是原子操作因此在高并发环境下执行这些命令时性能表现非常好。 键不存在的情况如果执行 INCR 或 INCRBY 时键不存在Redis 会自动创建这个键并初始化其值为 0然后进行递增。
2、Redis工具类实现ID自动生成
了解到通过Redis实现的原理之后就是如何设计缓存以保证每张表的数据之间不会相互影响
以user表为例对应的model为UserModel那么我们就可以将model名称作为key当前已有的最大的ID作为value来进行存储因为每一张表对应的model名称都是不一样的所以这里一定不会出现key重复的情况
下面是一些在Redis中生成和获取对象自增ID的工具方法方便新建对象时直接调用
Component(redisUtils)
RequiredArgsConstructor
Slf4j
public class RedisUtils implements InitializingBean {private final StringRedisTemplate stringRedisTemplate;private static RedisUtils redisUtils;Overridepublic void afterPropertiesSet() {redisUtils this;}/*** 获取自增ID*/public static T Integer getIncr(ClassT tClass) {try {String key tClass.getName();Long increment redisUtils.stringRedisTemplate.opsForValue().increment(key, 1);return Math.toIntExact(increment);} catch (Exception e) {e.printStackTrace();throw new RedisSystemException(e.getMessage(), e);}}/*** 获取自增ID指定递增长度用于批量创建对象时只请求一次Redis* 如批量创建五个对象delta等于5*/public static T Integer getIncr(ClassT tClass, int delta) {try {String key tClass.getName();Long increment redisUtils.stringRedisTemplate.opsForValue().increment(key, delta);return Math.toIntExact(increment);} catch (Exception e) {e.printStackTrace();throw new RedisSystemException(e.getMessage(), e);}}/*** 缓存预热用于项目启动时初始化Redis中各个表的最大ID*/public static T long incrPreheat(ClassT tClass, Long maxValue) {if (maxValue null) {maxValue 0L;}if (maxValue 0) {//异常抛出预热的初始值不能小于0}Integer incr getIncr(tClass);if (maxValue incr) {return incr;}return getIncr(tClass, (int) (maxValue - incr));}
} 解释InitializingBean是Spring提供的拓展性接口InitializingBean接口为bean提供了属性初始化后的处理方法它只有一个afterPropertiesSet方法凡是继承该接口的类在bean的属性初始化后都会执行该方法。 3、缓存预热
缓存预热是指在项目启动时将每一张表当前最大的主键ID预先加载到Redis中这样在后面使用的时候就可以直接从Redis中获取下一次ID即可不需要再去访问数据库查询最大ID缓存预热一般会建立在Redis专门的初始化类中以便在启动项目时可以运行该类具体如下
/*** 缓存预热** author hxy*/
Component
DependsOn(redisUtils)
RequiredArgsConstructor
public class RedisInit {private final UserMapper userMapper;/*** 获取每一张表中当前的最大ID然后预热到redis中*/PostConstructpublic void init() {RedisUtils.incrPreheat(UserModel.class, userMapper.maxId());}
} 解释DependsOn注解可以定义在类和方法上意思是我这个组件要依赖于另一个组件也就是说被依赖的组件会比该组件先注册到IOC容器中。 解释PostConstruct注解 在Java中PostConstruct注解通常用于标记一个方法它表示该方法在类实例化之后通过构造函数创建对象之后立即执行。 加上PostConstruct注解的方法会在对象的所有依赖项都已经注入完成之后执行。通过使用PostConstruct注解我们可以确保在对象完全创建和初始化之后才执行这些操作。这个注解通常用在依赖注入Dependency Injection的框架中例如Spring。 PostConstruct 注解可以用在任何类的方法上但它最常用于标记在 Spring Framework 中的 Bean 类中的初始化方法。 4、model层封装调用
在实例化一个model对象的时候要将model的主键ID进行赋值这个时候就要从Redis中获取到当前对象应该对应的主键ID我们可以在model类中构建一个newInstance方法或有参构造来专门的生成需要赋值主键ID的对象在方法中调用redis工具类的getIncr方法获取到最新最小未使用的主键ID并赋值给新建的model即可。
Getter
Setter
public class UserModel {private Integer id;private String name;private Integer age;private String tel;public UserModel() {}/*** 提供一个能够生成自增ID的有参构造需要生成自增ID时调用*/public UserModel(boolean autoId) {if (autoId) {this.id RedisUtils.getIncr(UserModel.class);}}
}
如果你是需要批量创建对象并且给对象赋值主键ID的情况可以按照如下方式使用这样可以只调用一次Redis避免重复调用Redis影响性能。
Service
RequiredArgsConstructor
public class UserServiceImpl {private final UserMapper userMapper;public void batchAddUser(){int addNumber 10;//提前生成自定跨度的ID比如之前最大ID是6获取后得到的incr是16Integer incr RedisUtils.getIncr(UserModel.class, addNumber);ListUserModel userModels new ArrayList();for (int i 0; i addNumber; i) {UserModel userModel new UserModel();//每一个主键ID依次递减使用userModel.setId(incr--);userModels.add(userModel);}userMapper.insertBatch(userModels);}
}
至此通过Redis来获取对象自增ID的方法已经完成如果在使用过程中有其他场景需求可以对redisUtils中的方法进行拓展即可。
我是灰小猿我们下期见