生活随笔
收集整理的這篇文章主要介紹了
关于生成订单号规则的一些思考
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
關(guān)于我為什么寫這篇文章是因?yàn)榻裉煸谧鲇唵文K的時(shí)候,看到之前的PRD上描述的訂單生成規(guī)則是由 年月日+用戶id2位+企業(yè)id位
+四位自增長數(shù)。然后竟被我反駁的突然改成了精確時(shí)間+4位自增長數(shù),于是我更失望了。
我們考慮一下,據(jù)我所常見的訂單基本都14-20位。(年月日時(shí)分秒和隨機(jī)數(shù))基本上就有14位了。雖然一般項(xiàng)目做不到淘寶雙11這種
支付峰值達(dá)到每秒10萬筆訂單.但是我覺得至少事先可以考慮到,想必當(dāng)初淘寶或許也沒意識(shí)到以后發(fā)展
得這么好。
背景
為了達(dá)到業(yè)務(wù)訂單的生成。我覺得要至少要符合以下這三種,
1. 全局唯一
2. 一定不能重復(fù)
在復(fù)雜的分布式系統(tǒng)中,很多場(chǎng)景需要的都是全局唯一ID的場(chǎng)景,一般為了防止沖突可以考慮的有36
位的UUID,twitter的snowflake等。
但是可以思考這些問題?
1. 是不是應(yīng)該有一些其他意義的思考,比如說訂單系統(tǒng)有買家的id(取固定幾位)
2. 是否有商品的標(biāo)識(shí),方便熟悉業(yè)務(wù)的排查問題或者查詢也通過不去系統(tǒng)查找可以有個(gè)初步的認(rèn)識(shí),但是業(yè)務(wù)量大的話感覺就可以排除這個(gè)人為的去辨識(shí)了。
3. 個(gè)人的看法是主要是唯一,其他關(guān)于業(yè)務(wù)方面的不是太太重要。
查閱了相關(guān)資料,主要有以下這幾種
UUID, 組成:當(dāng)前日期+時(shí)間+時(shí)鐘序列+機(jī)器識(shí)別號(hào)(Mac地址或其他)沒有mac網(wǎng)卡的話會(huì)有別的東西識(shí)別。
在分布式系統(tǒng)中,所有元素(WEB服務(wù)器)都不需要通過中央控制端來判斷數(shù)據(jù)唯一性。幾十年之內(nèi)可以達(dá)到全球唯一性。
snowflake的結(jié)構(gòu)如下(每部分用-分開):
Mysql通過AUTO_INCREMENT實(shí)現(xiàn)、Oracle通過Sequence序列實(shí)現(xiàn)。
在數(shù)據(jù)庫集群環(huán)境下,不同數(shù)據(jù)庫節(jié)點(diǎn)可設(shè)置不同起步值、相同步長來實(shí)現(xiàn)集群下生產(chǎn)全局唯一、遞增ID
Snowflake算法 雪花算法
41位時(shí)間戳+10位機(jī)器ID+12位序列號(hào)(自增) 轉(zhuǎn)化長度為18位的長整型。
Twitter為滿足美秒上萬條消息的創(chuàng)建,且ID需要趨勢(shì)遞增,方便客戶端排序。
Snowflake雖然有同步鎖,但是比uuid效率高。
Redis自增ID
實(shí)現(xiàn)了incr(key)用于將key的值遞增1,并返回結(jié)果。如果key不存在,創(chuàng)建默認(rèn)并賦值為0。 具有原子性,保證在并發(fā)的時(shí)候。
但是我在這主要想說的是雪花算法生成id,至于為什么,就測(cè)試了一下其他的,感覺這種生成方式個(gè)人比較喜歡。
Snowflake算法
規(guī)則如下
使用41bit作為毫秒數(shù),10bit作為機(jī)器的ID(5個(gè)bit是數(shù)據(jù)中心,5個(gè)bit的機(jī)器ID),12bit作為毫秒內(nèi)的流水號(hào)(意味著每個(gè)節(jié)點(diǎn)在每毫秒可以產(chǎn)生 4096 個(gè) ID),最后還有一個(gè)符號(hào)位,永遠(yuǎn)是0。
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
該算法實(shí)現(xiàn)基本是二進(jìn)制操作。
一共加起來剛好64位,為一個(gè)Long型。(轉(zhuǎn)換成字符串長度為18)
snowflake生成的ID整體上按照時(shí)間自增排序,并且整個(gè)分布式系統(tǒng)內(nèi)不會(huì)產(chǎn)生ID碰撞(由datacenter和workerId作區(qū)分),并且效率較高。據(jù)說:snowflake每秒能夠產(chǎn)生26萬個(gè)ID。
以下是代碼
部分借鑒與網(wǎng)絡(luò)
100萬個(gè)ID 耗時(shí)2秒
/*** Created by youze on 18-7-5*/
public class IdWorker {/*** 起始的時(shí)間戳*/private final static long START_STMP =
1530795377086L;
/*** 每一部分占用的位數(shù)*//*** 序列號(hào)占用的位數(shù)*/private final static long SEQUENCE_BIT =
12;
/*** 機(jī)器標(biāo)識(shí)占用的位數(shù)*/private final static long MACHINE_BIT =
5;
/*** 數(shù)據(jù)中心占用的位數(shù)*/private final static long DATACENTER_BIT =
5;
/*** 每一部分的最大值*/private final static long MAX_DATACENTER_NUM = -
1L ^ (-
1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = -
1L ^ (-
1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = -
1L ^ (-
1L << SEQUENCE_BIT);
/*** 每一部分向左的位移*/private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
/*** 數(shù)據(jù)中心*/private long datacenterId;
/*** 機(jī)器標(biāo)識(shí)*/private long machineId;
/*** 序列號(hào)*/private long sequence =
0L;
/*** 上一次時(shí)間戳*/private long lastStmp = -
1L;
public IdWorker(
long datacenterId,
long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId <
0) {
throw new IllegalArgumentException(
"datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");}
if (machineId > MAX_MACHINE_NUM || machineId <
0) {
throw new IllegalArgumentException(
"machineId can't be greater than MAX_MACHINE_NUM or less than 0");}
this.datacenterId = datacenterId;
this.machineId = machineId;}
/*** 產(chǎn)生下一個(gè)ID* @return*/public synchronized long nextId() {
long currStmp = getNewstmp();
if (currStmp < lastStmp) {
throw new RuntimeException(
"Clock moved backwards. Refusing to generate id");}
if (currStmp == lastStmp) {sequence = (sequence +
1) & MAX_SEQUENCE;
if (sequence ==
0L) {currStmp = getNextMill();}}
else {sequence =
0L;}lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT| datacenterId << DATACENTER_LEFT| machineId << MACHINE_LEFT| sequence;}
private long getNextMill() {
long mill = getNewstmp();
while (mill <= lastStmp) {mill = getNewstmp();}
return mill;}
private long getNewstmp() {
return System.currentTimeMillis();}
public static void main(String[] args) {IdWorker snowFlake =
new IdWorker(
2,
3);
long start = System.currentTimeMillis();
for (
int i =
0; i <
1000000; i++) {System.out.println(snowFlake.nextId());}System.out.println(System.currentTimeMillis() - start);}
}
最后大家可以看這也有更詳細(xì)的解釋
總結(jié)
以上是生活随笔為你收集整理的关于生成订单号规则的一些思考的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。