Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class IdGeneratorOptions {
/**
* 基础时间(ms单位)
* 不能超过当前系统时间
* 默认值 1582136402000L,表示2020-02-20 00:00:02
* 新项目建议改成 1735689600000L,表示2025-01-01 00:00:00
*/
public long BaseTime = 1582136402000L;

Expand Down Expand Up @@ -58,6 +60,12 @@ public class IdGeneratorOptions {
*/
public short TopOverCostCount = 2000;

/**
* 时间精度: 0 毫秒 1 10毫秒(实际上是8毫秒) 2 100毫秒(实际上是128毫秒) 3 1秒(实际上是1024毫秒) 4 10秒(实际上是8192毫秒)
* 默认毫秒,与原版兼容
*/
public short Precision = 0;

public IdGeneratorOptions() {

}
Expand Down
59 changes: 47 additions & 12 deletions Java/source/src/main/java/com/github/yitter/core/SnowWorkerM1.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,47 @@ public class SnowWorkerM1 implements ISnowWorker {
/**
* 基础时间
*/
protected final long BaseTime;
protected long BaseTime;

/**
* 机器码
*/
protected final short WorkerId;
protected short WorkerId;

/**
* 机器码位长
*/
protected final byte WorkerIdBitLength;
protected byte WorkerIdBitLength;

/**
* 自增序列数位长
*/
protected final byte SeqBitLength;
protected byte SeqBitLength;

/**
* 最大序列数(含)
*/
protected final int MaxSeqNumber;
protected int MaxSeqNumber;

/**
* 最小序列数(含)
*/
protected final short MinSeqNumber;
protected short MinSeqNumber;

/**
* 最大漂移次数(含)
*/
protected final int TopOverCostCount;
protected int TopOverCostCount;

protected final byte _TimestampShift;
protected final static byte[] _SyncLock = new byte[0];
/**
* 移位位数
*/
protected int ShiftBits;

protected int _sleepTime;

protected byte _TimestampShift;
protected final byte[] _SyncLock = new byte[0];

protected short _CurrentSeqNumber;
protected long _LastTimeTick = 0;
Expand All @@ -61,15 +68,43 @@ public class SnowWorkerM1 implements ISnowWorker {

public SnowWorkerM1(IdGeneratorOptions options) {
BaseTime = options.BaseTime != 0 ? options.BaseTime : 1582136402000L;
WorkerIdBitLength = options.WorkerIdBitLength == 0 ? 6 : options.WorkerIdBitLength;
WorkerIdBitLength = options.WorkerIdBitLength;
WorkerId = options.WorkerId;
SeqBitLength = options.SeqBitLength == 0 ? 6 : options.SeqBitLength;
MaxSeqNumber = options.MaxSeqNumber <= 0 ? (1 << SeqBitLength) - 1 : options.MaxSeqNumber;
MinSeqNumber = options.MinSeqNumber;
// TopOverCostCount = options.TopOverCostCount == 0 ? 2000 : options.TopOverCostCount;
TopOverCostCount = options.TopOverCostCount;
switch (options.Precision) {
case 0:
ShiftBits = 0;
break;
case 1:
ShiftBits = 3;
break;
case 2:
ShiftBits = 7;
break;
case 3:
ShiftBits = 10;
break;
case 4:
ShiftBits = 13;
break;
default:
ShiftBits = 0;
break;
}
// 计算睡眠时间,最大1秒
_sleepTime = 1 << ShiftBits;
// 无需等到精度时间,分4次检查
if (_sleepTime > 50) {
_sleepTime = _sleepTime >> 2;
}
_TimestampShift = (byte) (WorkerIdBitLength + SeqBitLength);
_CurrentSeqNumber = MinSeqNumber;

BaseTime = BaseTime >> ShiftBits;
}

private void DoGenIdAction(OverCostActionArg arg) {
Expand Down Expand Up @@ -207,15 +242,15 @@ private long CalcTurnBackId(long useTimeTick) {

protected long GetCurrentTimeTick() {
long millis = System.currentTimeMillis();
return millis - BaseTime;
return (millis >> ShiftBits) - BaseTime;
}

protected long GetNextTimeTick() {
long tempTimeTicker = GetCurrentTimeTick();

while (tempTimeTicker <= _LastTimeTick) {
try {
Thread.sleep(1);
Thread.sleep(_sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

public class DefaultIdGenerator implements IIdGenerator {

private static ISnowWorker _SnowWorker = null;
private ISnowWorker _SnowWorker = null;

public DefaultIdGenerator(IdGeneratorOptions options) throws IdGeneratorException {
if (options == null) {
Expand All @@ -26,19 +26,23 @@ public DefaultIdGenerator(IdGeneratorOptions options) throws IdGeneratorExceptio
}

// 2.WorkerIdBitLength
if (options.WorkerIdBitLength <= 0) {
throw new IdGeneratorException("WorkerIdBitLength error.(range:[1, 21])");
if (options.WorkerIdBitLength < 0) {
throw new IdGeneratorException("WorkerIdBitLength error.(range:[0, 21])");
}
if (options.WorkerIdBitLength + options.SeqBitLength > 22) {
throw new IdGeneratorException("error:WorkerIdBitLength + SeqBitLength <= 22");
}

// 3.WorkerId
int maxWorkerIdNumber = (1 << options.WorkerIdBitLength) - 1;
if (maxWorkerIdNumber == 0) {
maxWorkerIdNumber = 63;
// if (maxWorkerIdNumber == 0) {
// maxWorkerIdNumber = 63;
// }
if (options.WorkerIdBitLength == 0 && options.WorkerId != 0) {
throw new IdGeneratorException(
"WorkerIdBitLength is 0, WorkerId must be 0");
}
if (options.WorkerId < 0 || options.WorkerId > maxWorkerIdNumber) {
else if (options.WorkerId < 0 || options.WorkerId > maxWorkerIdNumber) {
throw new IdGeneratorException(
"WorkerId error. (range:[0, " + (maxWorkerIdNumber > 0 ? maxWorkerIdNumber : 63) + "]");
}
Expand All @@ -62,6 +66,10 @@ public DefaultIdGenerator(IdGeneratorOptions options) throws IdGeneratorExceptio
throw new IdGeneratorException("MinSeqNumber error. (range:[5, " + maxSeqNumber + "]");
}

if (options.Precision < 0 || options.Precision > 4) {
throw new IdGeneratorException("Precision error. (range:[0, 4]");
}

// 7.TopOverCostCount
if (options.TopOverCostCount < 0 || options.TopOverCostCount > 10000) {
throw new IdGeneratorException("TopOverCostCount error. (range:[0, 10000]");
Expand Down
45 changes: 40 additions & 5 deletions Java/source/src/main/java/com/github/yitter/idgen/YitIdHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
*/
package com.github.yitter.idgen;

import java.util.HashMap;
import java.util.Map;

import com.github.yitter.contract.IIdGenerator;
import com.github.yitter.contract.IdGeneratorException;
import com.github.yitter.contract.IdGeneratorOptions;
Expand All @@ -13,17 +16,34 @@
*/
public class YitIdHelper {

private static IIdGenerator idGenInstance = null;
private static final String DEFAULT_INSTANCE_NAME = "default";

private static final Map<String, IIdGenerator> idGenInstanceMap = new HashMap<>();

public static IIdGenerator getIdGenInstance() {
return idGenInstance;
return idGenInstanceMap.get(DEFAULT_INSTANCE_NAME);
}

public static IIdGenerator getIdGenInstance(String instanceName) {
return idGenInstanceMap.get(instanceName);
}

/**
* 设置参数,建议程序初始化时执行一次
*/
public static void setIdGenerator(IdGeneratorOptions options) throws IdGeneratorException {
idGenInstance = new DefaultIdGenerator(options);
idGenInstanceMap.put(DEFAULT_INSTANCE_NAME, new DefaultIdGenerator(options));
}

/**
* 设置参数,建议程序初始化时执行一次
*/
public static void setIdGenerator(String instanceName, IdGeneratorOptions options) throws IdGeneratorException {
if (instanceName == null || instanceName.trim().isEmpty()) {
throw new IdGeneratorException("instanceName cannot be null or empty");
}

idGenInstanceMap.put(instanceName, new DefaultIdGenerator(options));
}

/**
Expand All @@ -37,9 +57,24 @@ public static long nextId() throws IdGeneratorException {
// idGenInstance = new DefaultIdGenerator(new IdGeneratorOptions((short) 1));
// }

if (idGenInstance == null)
var generator = idGenInstanceMap.get(DEFAULT_INSTANCE_NAME);
if (generator == null)
throw new IdGeneratorException("Please initialize Yitter.IdGeneratorOptions first.");

return idGenInstance.newLong();
return generator.newLong();
}

/**
* 生成新的Id
* 调用本方法前,请确保调用了 SetIdGenerator 方法做初始化。
*
* @return
*/
public static long nextId(String instanceName) throws IdGeneratorException {
var generator = idGenInstanceMap.get(instanceName);
if (generator == null)
throw new IdGeneratorException("Please initialize instance: " + instanceName + " with YitIdHelper.setIdGenerator(instanceName, options) first.");

return generator.newLong();
}
}
34 changes: 24 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# 雪花算法中非常好用的数字ID生成器

Java代码相对于原版做了如下改动,适用于中小型项目生成更短和数值更小的ID:

1. 支持不同的时间精度,默认是1毫秒,与原版兼容;
2. WorkIdBitLength支持为0,适用于单机版的小项目;
3. 支持多个generator instance,服务于不同的场景

与原版相比,改进版的ID长度最低可降低到9位,更有效的利用号段资源。

关于改进的更多说明及程序结果可参考博文: [一个改进的Java短ID生成库](https://itlanyan.com/an-improved-short-id-generator/)


------------- 以下是原版说明 --------------


## 💎 最佳实践(置顶)

针对大家在使用中经常出现的性能疑问,我给出以下3组最佳实践:
Expand Down Expand Up @@ -101,7 +115,7 @@
* +-------------------------+--------------+----------+
* | 1.相对基础时间的时间差 | 2.WorkerId | 3.序列数 |
* +-------------------------+--------------+----------+
*
*
* 第1部分,时间差,是生成ID时的系统时间减去 BaseTime 的总时间差(毫秒单位)。
* 第2部分,WorkerId,是区分不同机器或不同应用的唯一ID,最大值由 WorkerIdBitLength(默认6)限定。
* 第3部分,序列数,是每毫秒下的序列数,由参数中的 SeqBitLength(默认6)限定。
Expand Down Expand Up @@ -242,20 +256,20 @@ extern void UnRegister();

## 已实现的语言

| 语言 | github |
| ---- | ---- |
| 🌲 C# | [查看示例][1] |
| 🌲 Java | [查看示例][2] |
| 🌲 Go| [查看示例][3] |
| 🌲 Rust | [查看示例][4] |
| 🌲 Python | [查看示例][10] |
| 语言 | github |
| ---- | ---- |
| 🌲 C# | [查看示例][1] |
| 🌲 Java | [查看示例][2] |
| 🌲 Go| [查看示例][3] |
| 🌲 Rust | [查看示例][4] |
| 🌲 Python | [查看示例][10] |
| 🌲 C | [查看示例][5] |
| 🌲 C (PHP扩展) | [查看示例][7] |
| 🌲 Delphi (Pascal) | [查看示例][6] |
| 🌲 JavaScript | [查看示例][8] |
| 🌲 TypeScript | [查看示例][9] |
| 🌲 V | [查看示例][6] |
| 🌲 D | [查看示例][72] |
| 🌲 V | [查看示例][6] |
| 🌲 D | [查看示例][72] |


## 技术支持
Expand Down