關於我爲何寫這篇文章是由於今天在作訂單模塊的時候,看到以前的PRD上描述的年月日+用戶id2位+企業id位
+四位自增加數。而後竟被我反駁的忽然改爲了精確時間+4位自增加數,因而我更失望了。算法
咱們考慮一下,據我所常見的訂單基本都14-20位。(年月日時分秒和隨機數)基本上就有14位了。雖然通常項目作不到淘寶雙11這種
支付峯值達到每秒10萬筆訂單.可是我以爲至少事先能夠考慮到,想必當初淘寶或許也沒意識到之後發展
得這麼好。sql
對於其定訂單的生成。我以爲要至少要符合如下這三種,全局惟一 ,數據庫
在複雜的分佈式系統中,不少場景須要的都是全局惟一ID的場景,通常爲了防止衝突能夠考慮的有36
位的UUID,twitter的snowflake等。服務器
可是能夠思考這些問題?網絡
查閱了相關資料,主要有如下這幾種併發
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); } }