分库分表ShardingJDBC分布式主键
目前有许多第三方解决方案可以完美解决这个问题,比如UUID等依靠特定算法自生成不重复键,或者通过引入主键生成服务(Redis或者ZooKeeper)等。
Sharding-JDBC提供了注解生成接口KeyGenerator。各个实现类通过实现generateKey()方法即可对外提供生成主键的功能。下面分析Sharding-JDBC分布式主键的使用。
DefaultKeyGenerator是Sharding-JDBC默认的主键生成器。该主键生成器采用Twitter Snowflake算法实现生成64位的Long型编号。国内很多大型互联网公司发号器服务基于该算法加部分改造实现。下面分析DefaultKeyGenerator产生的编号的组成。
DefaultKeyGenerator生成的64位Long型编号的组成如图所示。
64位Long型编号中各个部分的如表所示。
1
符号位
0
41
时间戳
从2016-11-01 00:00:00 开始的毫秒数,支持约70年
10
工作进程编号
最大进程编号1024
12
序列号
每毫秒从0开始自增,每毫秒最多4096个编号,每秒最多4096000个编号
DefaultKeyGenerator部分代码如下:
package io.shardingjdbc.core.keygen;import com.google.common.base.Preconditions;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* Default distributed primary key generator.
*
* Use snowflake algorithm. Length is 64 bit.
*
* Call @{@code DefaultKeyGenerator.setWorkerId} to set.
*/
@Slf4j
public final class DefaultKeyGenerator implements KeyGenerator {
// 2016-11-01 00:00:00 000 对应的毫秒数
public static final long EPOCH;
// 12位序列号
private static final long SEQUENCE_BITS = 12L;
// 10位工作进程号
private static final long WORKER_ID_BITS = 10L;
// 12位序列号自增量掩码(最大值)
private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;
// 工作进程ID左移比特数(位数)
private static final long WORKER_ID_LEFT_SHIFT_BITS = SEQUENCE_BITS;
// 时间戳左移比特数(位数)
private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS;
// 工作进程ID最大值
private static final long WORKER_ID_MAX_VALUE = 1L << WORKER_ID_BITS;
@Setter
// 当前时间
private static TimeService timeService = new TimeService();
// 工作进程
private static long workerId;
// 初始化 EPOCH
static {
Calendar calendar = Calendar.getInstance();
calendar.set(2016, Calendar.NOVEMBER, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
EPOCH = calendar.getTimeInMillis();
}
// 自增量
private long sequence;
// 最后生成编号时间戳,单位:毫秒
private long lastTime;
/**
* 设置工作进程
*/
public static void setWorkerId(final long workerId) {
Preconditions.checkArgument(workerId >= 0L && workerId < WORKER_ID_MAX_VALUE);
DefaultKeyGenerator.workerId = workerId;
}
/**
* Generate key.
* 创建id
*
* @return key type is @{@link Long}.
*/
@Override
public synchronized Number generateKey() {
long currentMillis = timeService.getCurrentMillis();
Preconditions.checkState(lastTime <= currentMillis,
"Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds",
lastTime, currentMillis);
if (lastTime == currentMillis) {
if (0L == (sequence = ++sequence & SEQUENCE_MASK)) {
currentMillis = waitUntilNextTime(currentMillis);
}
} else {
sequence = 0;
}
lastTime = currentMillis;
if (log.isDebugEnabled()) {
log.debug("{}-{}-{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(lastTime)), workerId, sequence);
}
return ((currentMillis - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (workerId << WORKER_ID_LEFT_SHIFT_BITS) | sequence;
}
private long waitUntilNextTime(final long lastTime) {
long time = timeService.getCurrentMillis();
while (time <= lastTime) {
time = timeService.getCurrentMillis();
}
return time;
}
}
以上是 分库分表ShardingJDBC分布式主键 的全部内容, 来源链接: utcz.com/z/513624.html