架构图
这种模式需要依赖MySQL,表字段table_name代表表名,max_id代表该表目前已分配的最大ID值,step代表每次Leaf往数据库请求时,一次性分配的ID数量。
ID申请表leaf_alloc
CREATE TABLE `leaf_alloc` (
`table_name` varchar(32) CHARACTER SET latin1 NOT NULL COMMENT '表名称',
`max_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '当前号段下的最大 id',
`step` int(11) unsigned NOT NULL DEFAULT '1' COMMENT '每次取号段的步长',
`desc` varchar(64) NOT NULL COMMENT '描述信息',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='叶子申请表';
引入依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
序列类定义
/**
* 序列类定义
*
* @author yangyanping
* @date 2024-04-01
*/
@Data
@Slf4j
public class Sequence {
/**
* 容量大小
*/
private int blockSize = 100;
/**
* 初始值大小
*/
private long startValue = 0;
/**
* 数据源
*/
private DataSource dataSource;
private Map<String, Step> stepMap = new HashMap<>();
public Sequence() {
}
public synchronized long get(String sequenceName) {
Step step = this.stepMap.get(sequenceName);
if (step == null) {
step = new Step(this.startValue, this.startValue + this.blockSize);
this.stepMap.put(sequenceName, step);
} else if (step.currentValue < step.endValue) {
return step.incrementAndGet();
}
for (int i = 0; i < this.blockSize; ++i) {
if (this.getNextBlock(sequenceName, step)) {
return step.incrementAndGet();
}
}
throw new RuntimeException("get error.");
}
private boolean getNextBlock(String sequenceName, Step step) {
Long value = this.getPersistenceValue(sequenceName);
if (value == null) {
try {
value = this.newPersistenceValue(sequenceName);
} catch (Exception var5) {
log.error("newPersistenceValue error!");
value = this.getPersistenceValue(sequenceName);
}
}
boolean b = this.saveValue(value, sequenceName) == 1;
if (b) {
step.setCurrentValue(value);
step.setEndValue(value + this.blockSize);
}
return b;
}
private int saveValue(long value, String sequenceName) {
Connection connection = null;
PreparedStatement statement = null;
int var8;
try {
connection = this.dataSource.getConnection();
statement = connection.prepareStatement("update leaf_alloc set max_id = ? where table_name = ? and max_id = ?");
statement.setLong(1, value + this.blockSize);
statement.setString(2, sequenceName);
statement.setLong(3, value);
var8 = statement.executeUpdate();
} catch (Exception var18) {
log.error("newPersistenceValue error!", var18);
throw new RuntimeException("newPersistenceValue error!", var18);
} finally {
this.close(null, statement, connection);
}
return var8;
}
private Long getPersistenceValue(String sequenceName) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = this.dataSource.getConnection();
statement = connection.prepareStatement("select max_id from leaf_alloc where table_name = ?");
statement.setString(1, sequenceName);
resultSet = statement.executeQuery();
if (resultSet.next()) {
return resultSet.getLong("max_id");
}
} catch (Exception var23) {
log.error("getPersistenceValue error!", var23);
throw new RuntimeException("getPersistenceValue error!", var23);
} finally {
this.close(resultSet, statement, connection);
}
return null;
}
private Long newPersistenceValue(String sequenceName) {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = this.dataSource.getConnection();
statement = connection.prepareStatement("insert into leaf_alloc (max_id,table_name) values (?,?)");
statement.setLong(1, this.startValue);
statement.setString(2, sequenceName);
statement.executeUpdate();
} catch (Exception var15) {
log.error("newPersistenceValue error!", var15);
throw new RuntimeException("newPersistenceValue error!", var15);
} finally {
this.close(null, statement, connection);
}
return this.startValue;
}
private void close(ResultSet resultSet, PreparedStatement statement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException var22) {
log.error("close resultset error!", var22);
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException var14) {
log.error("close statement error!", var14);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException var13) {
log.error("close connection error!", var13);
}
}
}
/**
* 步长定义
*/
@Data
static class Step {
/**
* 初始值
*/
private long currentValue;
/**
* 截止值
*/
private long endValue;
Step(long currentValue, long endValue) {
this.currentValue = currentValue;
this.endValue = endValue;
}
public long incrementAndGet() {
return ++this.currentValue;
}
}
}
ID生成类定义
/**
* ID生成类定义
*
* @author yangyanping
* @date 2024-04-01
*/
@Slf4j
public class IdGenerate {
private static Map<String, Sequence> sequenceMap;
private static Sequence defaultSequence;
private static DataSource dataSource;
public IdGenerate(DataSource ds) {
dataSource = ds;
}
public static long getNetId(String tableName) {
Sequence sequence = null;
if (sequenceMap != null) {
sequence = sequenceMap.get(tableName);
}
if (sequence == null) {
if (defaultSequence != null) {
return defaultSequence.get(tableName);
} else {
throw new RuntimeException("sequence " + tableName + " undefined!");
}
} else {
return sequence.get(tableName);
}
}
public void init() throws Exception {
sequenceMap = new HashMap<>();
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = dataSource.getConnection();
statement = connection.prepareStatement("select table_name,max_id,step from leaf_alloc");
resultSet = statement.executeQuery();
while (resultSet.next()) {
Integer step = resultSet.getInt("step");
String name = resultSet.getString("table_name");
Sequence sequence = new Sequence();
sequence.setStartValue(0);
sequence.setBlockSize(step);
sequence.setDataSource(dataSource);
if ("default".equalsIgnoreCase(name)) {
defaultSequence = sequence;
} else {
sequenceMap.put(name, sequence);
}
log.warn("leaf-Id-tableName={},step={}", name, step);
}
} catch (Exception var23) {
log.error("afterPropertiesSet error!", var23);
throw new RuntimeException("afterPropertiesSet error!", var23);
} finally {
this.close(resultSet, statement, connection);
}
}
private void close(ResultSet resultSet, PreparedStatement statement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException var22) {
log.error("close resultset error!", var22);
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException var14) {
log.error("close statement error!", var14);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException var13) {
log.error("close connection error!", var13);
}
}
}
}
配置IdGenerate Bean
@Configuration
public class BeanConfig {
@Bean(name = "idGenerate", initMethod = "init")
public IdGenerate idGenerate(@Autowired @Qualifier("baseDataSource") DataSource dataSource) {
return new IdGenerate(dataSource);
}
}
使用
@Test
public void test(){
long id = IdGenerate.getNetId("user);
}