廣州企業(yè)推廣seo工資待遇 seo工資多少
基本思路:
設(shè)計模式:單例模式
是否加鎖:是 synchronized
獲取最后一次生成的時間戳值T0
限定初始時間為2023-08-01 00:00:00,獲取當前時間時間戳T1,T1與初始時間的毫秒差值T2,轉(zhuǎn)為16進制,轉(zhuǎn)為字符串為r1,獲取該字符串的長度L1
獲取L2 (length - L1) ,獲取L2位數(shù)字的16進制自增數(shù)值范圍,取最大值max
現(xiàn)數(shù)據(jù)庫批量導(dǎo)入數(shù)據(jù)速度為 n條/ms
平均步長為max/n,(0~平均步長)的平均數(shù)為max/n/2,假設(shè)使用平均步長最為隨機步長范圍,最終的值與max相差較遠,大約后一半的數(shù)字沒有被使用
將平均步長*2-平均步長*容錯因子(0.1)的值作為我們隨機步長的范圍 ?容錯因子:減小溢出概率
隨機步長step = max/n*2 - max/n*0.1
獲取T1
如果T1 == T0,序列值seqNum = seqNum + step (轉(zhuǎn)為16進制),若seqNum > max,該線程暫停1毫秒后刷新r1
如果T1 > T0,序列值seqNum = 0 + step
設(shè)置T0
代碼實現(xiàn)如下:
/*** 生成短id* @author mayu*/
public class ShortIdWorker {/*** 初始時間限定為2023-08-01 00:00:00*/private final static long START_STAMP = 1690819200000L;/*** 容錯因子*/private final static int FAULT_TOLERANCE_FACTOR = 10;/*** 默認長度*/private final static int DEFAULT_ID_LENGTH = 12;/*** 數(shù)據(jù)庫每毫秒可保存的數(shù)據(jù),結(jié)合列的數(shù)量取值,建議實測后更改*/private final static int DEFAULT_TRANSFER_SPEED_PER_MILLISECOND = 50;private final int length;private final int transferSpeedPerMillisecond;/*** 上次運行時間*/private long lastStamp = -1L;/*** 增長序列*/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);}/*** 默認使用12位id,數(shù)據(jù)庫每毫秒新增數(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位為時間戳差值的16進制,后y位為不固定步長的自增序列*/public synchronized String nextId() {long now = now();// 獲取16進制時間戳前綴String stampPrefix = getStampStr(now);// 獲取第二段增長序列的長度l2int l2 = this.length - stampPrefix.length();// 獲取l2位16進制的最大值int max = IntStream.range(0, l2).map(i -> 16).reduce(1, (a, b) -> a * b) - 1;// 獲取增長的平均步長averageStepLengthint averageStepLength = max / this.transferSpeedPerMillisecond;// 取步長范圍// averageStepLength的平均值是averageStepLength/2,累加的情況下會有后一半的空間浪費問題,故取值為averageStepLength*2,平均值為averageStepLength// 取隨機數(shù)的結(jié)果不可控,上行中列舉的只是近似值,為防止多次溢出影響程序執(zhí)行時間,再減去容錯因子,減小溢出概率(容錯因子建議在本地系統(tǒng)實測后更改)int randomStepLengthMax = (averageStepLength << 1) - (averageStepLength / FAULT_TOLERANCE_FACTOR);// 在步長范圍內(nèi)獲取隨機步長int randomStepLength = new Random().nextInt(randomStepLengthMax) + 1;// 當上次運行時間小于當前時間或第一次運行時,增長序列賦值為隨機步長,設(shè)置最后運行時間if (this.lastStamp < now || this.lastStamp == -1L) {this.seqNum = randomStepLength;this.lastStamp = now;// 當上次運行時間與當前運行時間處于同一毫秒時} else if (this.lastStamp == now) {// 增長序列以隨機步長為步長遞增this.seqNum += randomStepLength;// 當增長序列大于最大值時if (this.seqNum > max) {// 程序暫停一毫秒LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));// 重新獲取前綴,增長序列重新開始this.seqNum = randomStepLength;Long newNow = now();this.lastStamp = newNow;stampPrefix = getStampStr(newNow);}} else {// 時鐘回撥,報錯throw new IllegalStateException("Clock moved backwards. Reject to generate id");}// 將增長序列轉(zhuǎn)為16進制與時間戳拼接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ù),將結(jié)果轉(zhuǎn)為16進制* @param now 時間戳* @return*/private String getStampStr(Long now) {return hex10To16(String.valueOf(now - START_STAMP));}
????????8位16進制可使用到4201年-03-20 07:32:15,后續(xù)時間戳所占位數(shù)自動變?yōu)?位,id總長度不變,不用擔(dān)心id用盡的問題。
????????代碼中關(guān)于時間賦值的代碼請謹慎改動,順序顛倒會產(chǎn)生bug。