關於生成訂單號規則的一些思考

關於我爲何寫這篇文章是由於今天在作訂單模塊的時候,看到以前的PRD上描述的年月日+用戶id2位+企業id位
+四位自增加數。而後竟被我反駁的忽然改爲了精確時間+4位自增加數,因而我更失望了。算法

咱們考慮一下,據我所常見的訂單基本都14-20位。(年月日時分秒和隨機數)基本上就有14位了。雖然通常項目作不到淘寶雙11這種
支付峯值達到每秒10萬筆訂單.可是我以爲至少事先能夠考慮到,想必當初淘寶或許也沒意識到之後發展
得這麼好。sql

背景

對於其定訂單的生成。我以爲要至少要符合如下這三種,全局惟一 ,數據庫

在複雜的分佈式系統中,不少場景須要的都是全局惟一ID的場景,通常爲了防止衝突能夠考慮的有36
位的UUID,twitter的snowflake等。服務器

可是能夠思考這些問題?網絡

  1. 是否是應該有一些其餘意義的思考,好比說訂單系統有買家的id(取固定幾位)
  2. 是否有商品的標識,方便熟悉業務的排查問題或者查詢也經過不去系統查找能夠有個初步的認識,可是業務量大的話感受就能夠排除這我的爲的去辨識了。
  3. 我的的見解是主要是惟一,其餘關於業務方面的不是太過重要。

查閱了相關資料,主要有如下這幾種併發

1.UUID
組成:當前日期+時間+時鐘序列+機器識別號(Mac地址或其餘)沒有mac網卡的話會有別的東西識別。
在分佈式系統中,全部元素(WEB服務器)都不須要經過中央控制端來判斷數據惟一性。幾十年以內能夠達到全球惟一性。
snowflake的結構以下(每部分用-分開):less

2.Mysql經過AUTO_INCREMENT實現、Oracle經過Sequence序列實現。
在數據庫集羣環境下,不一樣數據庫節點可設置不一樣起步值、相同步長來實現集羣下生產全局惟1、遞增ID分佈式

3.Snowflake算法 雪花算法 
41位時間戳+10位機器ID+12位序列號(自增) 轉化長度爲18位的長整型。
Twitter爲知足美秒上萬條消息的建立,且ID須要趨勢遞增,方便客戶端排序。
Snowflake雖然有同步鎖,可是比uuid效率高。ui

4.Redis自增ID
實現了incr(key)用於將key的值遞增1,並返回結果。若是key不存在,建立默認並賦值爲0。 具備原子性,保證在併發的時候。this


可是我在這主要想說的是雪花算法生成id

關於序列
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

第一位爲未使用,接下來的41位爲毫秒級時間(41位的長度可使用69年),而後是5位datacenterId和5位workerId(10位的長度最多支持部署1024個節點) ,最後12位是毫秒內的計數(12位的計數順序號支持每一個節點每毫秒產生4096個ID序號)

一共加起來恰好64位,爲一個Long型。(轉換成字符串長度爲18)

snowflake生成的ID總體上按照時間自增排序,而且整個分佈式系統內不會產生ID碰撞(由datacenter和workerId做區分),而且效率較高。聽說:snowflake每秒可以產生26萬個ID。

如下是代碼
部分借鑑與網絡
100萬個ID 耗時2秒

/**
 * Created by youze on 18-7-5
 */
public class IdWorker {

    /**
     * 起始的時間戳
     */
    private final static long START_STMP = 1530795377086L;

    /**
     * 每一部分佔用的位數
     */

    /**
     * 序列號佔用的位數
     */
    private final static long SEQUENCE_BIT = 12;

    /**
     * 機器標識佔用的位數
     */
    private final static long MACHINE_BIT = 5;

    /**
     * 數據中心佔用的位數
     */
    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;

    /**
     * 數據中心
     */
    private long datacenterId;

    /**
     * 機器標識
     */
    private long machineId;
    /**
     * 序列號
     */
    private long sequence = 0L;

    /**
     * 上一次時間戳
     */
    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;
    }

    /**
     * 產生下一個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 {
            //不一樣毫秒內,序列號置爲0
            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);
    }
}
相關文章
相關標籤/搜索