分库分表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

回到顶部