雪花算法原理:一个 64 bit 的 long 型的数字作为全局唯一 id。这 64 个 bit 中,其中 1 个 bit 是不用的,然后用其中的 41 bit 作为毫秒数,用 10 bit 作为工作机器 id,12 bit 作为序列号。
(1)@TableId注解切入的字段为主键字段;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TableId {
/**
* 字段值(驼峰命名方式,该值可无)
*/
String value() default "";
/**
* 主键ID
* {@link IdType}
*/
IdType type() default IdType.NONE;
}
(2)环境配置了全局唯一ID (idWorker),type为3
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型(将跟随全局)
*/
NONE(1),
/**
* 用户输入ID
* 该类型可以通过自己注册自动填充插件进行填充
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 全局唯一ID (idWorker)
*/
ID_WORKER(3),
/**
* 全局唯一ID (UUID)
*/
UUID(4),
/**
* 字符串全局唯一ID (idWorker 的字符串表示)
*/
ID_WORKER_STR(5);
}
(3)接着分析ID_WORKER源码
自定义 ParameterHandler 重装构造函数,填充插入方法主键 ID
(4)com.baomidou.mybatisplus.core.toolkit.IdWorker
/**
* 无参数构造器:主机和进程的机器码
*/
private static Sequence WORKER = new Sequence();
/**
* 有参构造器
*
* @param workerId 工作机器 ID(雪花算法上边介绍的)
* @param datacenterId 序列号(雪花算法上边介绍的)
*/
public static void initSequence(long workerId, long datacenterId) {
WORKER = new Sequence(workerId, datacenterId);
}
/**
* 获取ID
*/
public static long getId() {
return WORKER.nextId();
}
(5)Sequence(核心)源码分析
//在没有设置机器号(也就是无参构造器)的情况下,会通过当前物理网卡地址和jvm的进程ID自动生成。在一个集群中,MAC+JVM进程PID
一样的几率非常小
/**
* 无参构造器
*
*/
public Sequence() {
// 通过当前物理网卡地址获取datacenterId序列号(雪花算法用到)
this.datacenterId = getDatacenterId(maxDatacenterId);
// 物理网卡地址+jvm进程pi获取workerId(工作机器ID,雪花算法用到)
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
// 有机器号情况下
/**
* 有参构造器
*
* @param workerId 工作机器 ID(雪花算法的)
* @param datacenterId 序列号(雪花算法的)
*/
public Sequence(long workerId, long datacenterId) {
Assert.isFalse(workerId > maxWorkerId || workerId < 0,
String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0,
String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
this.workerId = workerId;
this.datacenterId = datacenterId;
}
(6)无机器号情况下核心源码分析
/**
* 数据标识id部分,获取序列号
*/
protected static long getDatacenterId(long maxDatacenterId) {
long id = 0L;
try {
// 物理网卡地址获取
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
if (null != mac) {
id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
}
} catch (Exception e) {
logger.warn(" getDatacenterId: " + e.getMessage());
}
return id;
}
/**
* 获取 maxWorkerId工作机器ID
*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuilder mpid = new StringBuilder();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (StringUtils.isNotEmpty(name)) {
/*
* GET jvmPid
*/
mpid.append(name.split(StringPool.AT)[0]);
}
/*
* MAC + PID 的 hashcode 获取16个低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
(1)采用Type为3的方式,即ASSIGN_ID
可见原来的ID_WORKER(3)已经被标注过期了,这一点和上一版本还是有很大区别
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* 该类型可以通过自己注册自动填充插件进行填充
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
*/
ASSIGN_UUID(4),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_ID}
*/
@Deprecated
ID_WORKER_STR(3),
/**
* @deprecated 3.3.0 please use {@link #ASSIGN_UUID}
*/
@Deprecated
UUID(4);
}
(2)对ASSIGN_ID进行分析
/**
* Id生成器接口
*/
public interface IdentifierGenerator {
/**
* 生成Id(整型ID,推荐)
*
* @param entity 实体
* @return id
*/
Number nextId(Object entity);
// 底下相对于上一版本增加了个UUID,不推荐
.......
}
该源码可见依然是定义了Sequence有参构造器和无参构造器,Sequence源码(核心)经过在线代码比较器比较,和上一版本并无区别
/**
* 默认生成器
*
*/
public class DefaultIdentifierGenerator implements IdentifierGenerator {
private final Sequence sequence;
public DefaultIdentifierGenerator() {
this.sequence = new Sequence();
}
public DefaultIdentifierGenerator(long workerId, long dataCenterId) {
this.sequence = new Sequence(workerId, dataCenterId);
}
public DefaultIdentifierGenerator(Sequence sequence) {
this.sequence = sequence;
}
@Override
public Long nextId(Object entity) {
return sequence.nextId();
}
}