20200103 高併發情況下id唯一生成器

互聯網快速發展的今天,分佈式應用系統已經見怪不怪,在分佈式系統中,我們需要各種各樣的ID,既然是ID那麼必然是要保證全局唯一,除此之外,不同的業務還需要不同的特性,比如像併發巨大的業務要求ID生成效率高,吞吐大;比如某些銀行類業務,需要按每日日期制定交易流水號;又比如我們希望用戶的ID是隨機的,無序的,純數字的,且位數長度是小於10位的。等等,不同的業務場景需要的ID特性各不一樣,於是,衍生了各種ID生成器。本文講的訂單號就是其中一種業務id,下面結合訂單業務需求,介紹訂單號的生成策略。

特徵:全局唯一,不重複;安全性;性能;id生成效率高。訂單號生策略。

支付中心多臺部署,能夠保證訂單id唯一麼?

 

業務需求:

1)訂單號不能重複

2)訂單號沒有規則,即編碼規則不能加入任何和公司運營相關的數據,外部人員無法通過訂單ID猜測到訂單量。不能被遍歷。

3)訂單號長度固定,且不能太長

4)易讀,易溝通,不要出現數字字母換亂現象

5)生成耗時(生成效率高低)

 

關於訂單號的生成,一些比較簡單的方案:

1、數據庫自增長ID

優勢:無需編碼

缺陷:

1)大表不能做水平分表,否則插入刪除時容易出現問題(單表處理,不能分表)。

2)高併發下插入數據需要加入事務機制

3)在業務操作父、子表(關聯表)插入時,先要插入父表,再插入子表;

 

2、時間戳+隨機數

優勢:編碼簡單

缺陷:隨機數存在重複問題,即使在相同的時間戳下。每次插入數據庫前需要校驗下是否已經存在相同的數值。(每次校驗是否存在該訂單號,不存在才能插入,同一張表)

 

3、時間戳+會員ID

優勢:同一時間,一個用戶不會存在兩張訂單

缺陷:會員ID也會透露運營數據,雞生蛋,蛋生雞的問題

例如:S+yyMMddHHmmss+Math.abs(memberId.hashCode());[說明memberId爲uuid的,String的hashCode唯一,而hashcode可能爲負數] 哈希值長度不同。

 

4、UUID

優勢:簡單。劣勢:用戶不友好,索引關聯效率較低。

UUID全稱:Universally Unique Identifier,即通用唯一識別碼。

UUID是由一組32位數的16進制數字所構成,所以理論上UUID的總數爲16^32=2^128,約等於3.4*10^38。也就是說每納秒產生1兆個UUID,要花100億年纔會將所有UUID用完。

UUID的標準形式包含32個16進制數字,以連字號分爲五段,形式爲8-4-4-4-12的32個字符,如:550e8400-e19b-41d4-a716-446655440000。

 

5、twitter的SnowFlake

Twitter-Snowflake算法產生的背景相當簡單,爲了滿足Twitter每秒上萬條消息的請求,每條消息都必須分配一條唯一的id,這些id還需要一些大致的順序(方便客戶端排序),並且在分佈式系統中不同機器產生的id必須不同.Snowflake算法核心把時間戳,工作機器id,序列號(毫秒級時間41位+機器ID 10位+毫秒內序列12位)組合在一起。

 

 

除了最高位bit標記爲不可用以外,其餘三組bit佔位均可浮動,看具體的業務需求而定。默認情況下41bit的時間戳可以支持該算法使用到2082年,10bit的工作機器id可以支持1023臺機器,序列號支持1毫秒產生4095個自增序列id。

時間戳+機器id+序列號。

snowflake算法是一款本地生成的(ID生成過程不依賴任何中間件,無網絡通信),保證ID全局唯一,並且ID總體有序遞增,性能每秒生成300w+。 總體有序遞增。

 

此方法可能存在id重複的問題,謹慎使用

        StringBuilder idBuilder = new StringBuilder();
        idBuilder.append(System.currentTimeMillis());
        System.out.println(idBuilder.toString());


        String userIdStr = "123456789".toString();
        if (userIdStr.length() <= 4) {
            idBuilder.append(userIdStr);
        } else {
            idBuilder.append(userIdStr.substring(userIdStr.length() - 4));
        }

        Random random = new Random();
        int randomPart = random.nextInt(99) + 1;
        if (randomPart < 10) {
            idBuilder.append('0');
        }
        idBuilder.append(randomPart);
        System.out.println(  Long.parseLong(idBuilder.toString()));

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章