基本思路:
設(shè)計(jì)模式:?jiǎn)卫J?/p>
是否加鎖:是 synchronized
獲取最后一次生成的時(shí)間戳值T0
限定初始時(shí)間為2023-08-01 00:00:00,獲取當(dāng)前時(shí)間時(shí)間戳T1,T1與初始時(shí)間的毫秒差值T2,轉(zhuǎn)為16進(jìn)制,轉(zhuǎn)為字符串為r1,獲取該字符串的長(zhǎng)度L1
獲取L2 (length - L1) ,獲取L2位數(shù)字的16進(jìn)制自增數(shù)值范圍,取最大值max
現(xiàn)數(shù)據(jù)庫(kù)批量導(dǎo)入數(shù)據(jù)速度為 n條/ms
平均步長(zhǎng)為max/n,(0~平均步長(zhǎng))的平均數(shù)為max/n/2,假設(shè)使用平均步長(zhǎng)最為隨機(jī)步長(zhǎng)范圍,最終的值與max相差較遠(yuǎn),大約后一半的數(shù)字沒(méi)有被使用
將平均步長(zhǎng)*2-平均步長(zhǎng)*容錯(cuò)因子(0.1)的值作為我們隨機(jī)步長(zhǎng)的范圍 ?容錯(cuò)因子:減小溢出概率
隨機(jī)步長(zhǎng)step = max/n*2 - max/n*0.1
獲取T1
如果T1 == T0,序列值seqNum = seqNum + step (轉(zhuǎn)為16進(jìn)制),若seqNum > max,該線(xiàn)程暫停1毫秒后刷新r1
如果T1 > T0,序列值seqNum = 0 + step
設(shè)置T0
代碼實(shí)現(xiàn)如下:
/**
* 生成短id
* @author mayu
*/
public class ShortIdWorker {
/**
* 初始時(shí)間限定為2023-08-01 00:00:00
*/
private final static long START_STAMP = 1690819200000L;
/**
* 容錯(cuò)因子
*/
private final static int FAULT_TOLERANCE_FACTOR = 10;
/**
* 默認(rèn)長(zhǎng)度
*/
private final static int DEFAULT_ID_LENGTH = 12;
/**
* 數(shù)據(jù)庫(kù)每毫秒可保存的數(shù)據(jù),結(jié)合列的數(shù)量取值,建議實(shí)測(cè)后更改
*/
private final static int DEFAULT_TRANSFER_SPEED_PER_MILLISECOND = 50;
private final int length;
private final int transferSpeedPerMillisecond;
/**
* 上次運(yùn)行時(shí)間
*/
private long lastStamp = -1L;
/**
* 增長(zhǎng)序列
*/
private int seqNum;
private static ShortIdWorker instance;
/**
* 單例模式
*/
public static ShortIdWorker getInstance() {
if (null == instance) {
instance = new ShortIdWorker();
}
return instance;
}
public static ShortIdWorker newInstance(int length, int transferSpeedPerMillisecond) {
return new ShortIdWorker(length, transferSpeedPerMillisecond);
}
/**
* 默認(rèn)使用12位id,數(shù)據(jù)庫(kù)每毫秒新增數(shù)據(jù)為50條
*/
private ShortIdWorker() {
this(DEFAULT_ID_LENGTH, DEFAULT_TRANSFER_SPEED_PER_MILLISECOND);
}
private ShortIdWorker(int length, int transferSpeedPerMillisecond) {
this.length = length;
this.transferSpeedPerMillisecond = transferSpeedPerMillisecond;
}
/**
* @return 生成后的id
* <p>
* 例:757b12c001d3
* 共length位id,前x位為時(shí)間戳差值的16進(jìn)制,后y位為不固定步長(zhǎng)的自增序列
*/
public synchronized String nextId() {
long now = now();
// 獲取16進(jìn)制時(shí)間戳前綴
String stampPrefix = getStampStr(now);
// 獲取第二段增長(zhǎng)序列的長(zhǎng)度l2
int l2 = this.length - stampPrefix.length();
// 獲取l2位16進(jìn)制的最大值
int max = IntStream.range(0, l2).map(i -> 16).reduce(1, (a, b) -> a * b) - 1;
// 獲取增長(zhǎng)的平均步長(zhǎng)averageStepLength
int averageStepLength = max / this.transferSpeedPerMillisecond;
// 取步長(zhǎng)范圍
// averageStepLength的平均值是averageStepLength/2,累加的情況下會(huì)有后一半的空間浪費(fèi)問(wèn)題,故取值為averageStepLength*2,平均值為averageStepLength
// 取隨機(jī)數(shù)的結(jié)果不可控,上行中列舉的只是近似值,為防止多次溢出影響程序執(zhí)行時(shí)間,再減去容錯(cuò)因子,減小溢出概率(容錯(cuò)因子建議在本地系統(tǒng)實(shí)測(cè)后更改)
int randomStepLengthMax = (averageStepLength << 1) - (averageStepLength / FAULT_TOLERANCE_FACTOR);
// 在步長(zhǎng)范圍內(nèi)獲取隨機(jī)步長(zhǎng)
int randomStepLength = new Random().nextInt(randomStepLengthMax) + 1;
// 當(dāng)上次運(yùn)行時(shí)間小于當(dāng)前時(shí)間或第一次運(yùn)行時(shí),增長(zhǎng)序列賦值為隨機(jī)步長(zhǎng),設(shè)置最后運(yùn)行時(shí)間
if (this.lastStamp < now || this.lastStamp == -1L) {
this.seqNum = randomStepLength;
this.lastStamp = now;
// 當(dāng)上次運(yùn)行時(shí)間與當(dāng)前運(yùn)行時(shí)間處于同一毫秒時(shí)
} else if (this.lastStamp == now) {
// 增長(zhǎng)序列以隨機(jī)步長(zhǎng)為步長(zhǎng)遞增
this.seqNum += randomStepLength;
// 當(dāng)增長(zhǎng)序列大于最大值時(shí)
if (this.seqNum > max) {
// 程序暫停一毫秒
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));
// 重新獲取前綴,增長(zhǎng)序列重新開(kāi)始
this.seqNum = randomStepLength;
Long newNow = now();
this.lastStamp = newNow;
stampPrefix = getStampStr(newNow);
}
} else {
// 時(shí)鐘回?fù)?,?bào)錯(cuò)
throw new IllegalStateException("Clock moved backwards. Reject to generate id");
}
// 將增長(zhǎng)序列轉(zhuǎn)為16進(jìn)制與時(shí)間戳拼接
return stampPrefix + String.format("%0" + l2 + "X", new BigInteger(String.valueOf(this.seqNum), 10));
}
private String hex10To16(String str) {
return String.format("%X", new BigInteger(str, 10));
}
private long now() {
return System.currentTimeMillis();
}
/**
* 獲取傳入時(shí)間與開(kāi)始時(shí)間的間隔毫秒數(shù),將結(jié)果轉(zhuǎn)為16進(jìn)制
* @param now 時(shí)間戳
* @return
*/
private String getStampStr(Long now) {
return hex10To16(String.valueOf(now - START_STAMP));
}
????????8位16進(jìn)制可使用到4201年-03-20 07:32:15,后續(xù)時(shí)間戳所占位數(shù)自動(dòng)變?yōu)?位,id總長(zhǎng)度不變,不用擔(dān)心id用盡的問(wèn)題。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-682609.html
????????代碼中關(guān)于時(shí)間賦值的代碼請(qǐng)謹(jǐn)慎改動(dòng),順序顛倒會(huì)產(chǎn)生bug。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-682609.html
到了這里,關(guān)于生成12位短id,自增且不連續(xù),永不重復(fù),不依賴(lài)數(shù)據(jù)庫(kù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!