一 資料庫主鍵自增
這種方式就比較簡單直白了,就是透過關係型資料庫的自增主鍵產生來唯一的 ID。
- 優點:實現起來比較簡單、ID 有序遞增、儲存消耗空間小
- 缺點:支援的併發量不大、存在資料庫單點問題(可以使用資料庫叢集解決,不過增加了複雜度)、ID 沒有具體業務含義、安全問題(比如根據訂單 ID 的遞增規律就能推算出每天的訂單量,商業機密啊! )、每次獲取 ID 都要訪問一次資料庫(增加了對資料庫的壓力,獲取速度也慢)
二 資料庫號段自增
資料庫主鍵自增這種模式,每次獲取 ID 都要訪問一次資料庫,ID 需求比較大的時候,肯定是不行的。
如果我們可以批次獲取,然後存在在記憶體裡面,需要用到的時候,直接從記憶體裡面拿就舒服了!這也就是我們說的 基於資料庫的號段模式來生成分散式 ID。
資料庫的號段模式也是目前比較主流的一種分散式 ID 生成方式。像滴滴開源的Tinyid就是基於這種方式來做的。不過,TinyId 使用了雙號段快取、增加多 db 支援等方式來進一步最佳化。
相比於資料庫主鍵自增的方式,資料庫的號段模式對於資料庫的訪問次數更少,資料庫壓力更小。
另外,為了避免單點問題,你可以從使用主從模式來提高可用性。
資料庫號段模式的優缺點:
- 優點:ID 有序遞增、儲存消耗空間小
- 缺點:存在資料庫單點問題(可以使用資料庫叢集解決,不過增加了複雜度)、ID 沒有具體業務含義、安全問題(比如根據訂單 ID 的遞增規律就能推算出每天的訂單量,商業機密啊! )
三 NOSQL(redis)
一般情況下,NoSQL 方案使用 Redis 多一些。我們透過 Redis 的 incr
命令即可實現對 id 原子順序遞增。
Redis 方案的優缺點:
- 優點:效能不錯並且生成的 ID 是有序遞增的
- 缺點:和資料庫主鍵自增方案的缺點類似
四 UUID
UUID 是 Universally Unique Identifier(通用唯一識別符號) 的縮寫。UUID 包含 32 個 16 進位制數字(8-4-4-4-12)。
JDK 就提供了現成的生成 UUID 的方法,一行程式碼就行了。
//輸出示例:cb4a9ede-fa5e-4585-b9bb-d60bce986eaa UUID.randomUUID()
UUID 可以保證唯一性,因為其生成規則包括 MAC 地址、時間戳、名字空間(Namespace)、隨機或偽隨機數、時序等元素,計算機基於這些規則生成的 UUID 是肯定不會重複的。
雖然,UUID 可以做到全域性唯一性,但是,我們一般很少會使用它。
比如使用 UUID 作為 MySQL 資料庫主鍵的時候就非常不合適:
- 資料庫主鍵要儘量越短越好,而 UUID 的消耗的儲存空間比較大(32 個字串,128 位)。
- UUID 是無順序的,InnoDB 引擎下,資料庫主鍵的無序性會嚴重影響資料庫效能。
最後,我們再簡單分析一下 UUID 的優缺點 (面試的時候可能會被問到的哦!) :
- 優點:生成速度比較快、簡單易用
- 缺點:儲存消耗空間大(32 個字串,128 位)、 不安全(基於 MAC 地址生成 UUID 的演算法會造成 MAC 地址洩露)、無序(非自增)、沒有具體業務含義、需要解決重複 ID 問題(當機器時間不對的情況下,可能導致會產生重複 ID)
五 snowflake演算法組成
snowflake是Twitter開源的分散式ID生成演算法,結果是一個long型的ID。其核心思想是:使用41bit作為毫秒數,10bit作為機器的ID(5個bit是資料中心,5個bit的機器ID),12bit作為毫秒內的流水號(意味著每個節點在每毫秒可以產生 4096 個 ID),最後還有一個符號位,永遠是0。
最高位是符號位,始終為0,不可用。
41位的時間序列,精確到毫秒級,41位的長度可以使用69年。時間位還有一個很重要的作用是可以根據時間進行排序。
10位的機器標識,10位的長度最多支援部署1024個節點。
12位的計數序列號,序列號即一系列的自增id,可以支援同一節點同一毫秒生成多個ID序號,12位的計數序列號支援每個節點每毫秒產生4096個ID序號。
總結: 雪花演算法組成結構大致分為了無效位、時間位、機器位和序列號位。其特點是自增、有序、純數字組成查詢效率高且不依賴於資料庫。適合在分散式的場景中應用,可根據需求調整具體實現細節。
我們再來看看 Snowflake 演算法的優缺點:
- 優點:生成速度比較快、生成的 ID 有序遞增、比較靈活(可以對 Snowflake 演算法進行簡單的改造比如加入業務 ID)
- 缺點:需要解決重複 ID 問題(ID 生成依賴時間,在獲取時間的時候,可能會出現時間回撥的問題,也就是伺服器上的時間突然倒退到之前的時間,進而導致會產生重複 ID)、依賴機器 ID 對分散式環境不友好(當需要自動啟停或增減機器時,固定的機器 ID 可能不夠靈活)。
如果你想要使用 Snowflake 演算法的話,一般不需要你自己再造輪子。有很多基於 Snowflake 演算法的開源實現比如美團 的 Leaf、百度的 UidGenerator(後面會提到),並且這些開源實現對原有的 Snowflake 演算法進行了最佳化,效能更優秀,還解決了 Snowflake 演算法的時間回撥問題和依賴機器 ID 的問題。
專案中如何生成一個單據號
20181024-日期
0 - 彈性標識為master
4 - 新平臺
099 - 寫死的系統標識
00000000 - 8位擴充套件碼
21 分庫分表位
00 - 彈性管控位
12345678 - 8位隨機sequece
六 衍生問題1 - traceId
6.1 日誌跟蹤
在分散式服務架構下,一個 Web 請求從閘道器流入,有可能會呼叫多個服務對請求進行處理,拿到最終結果。這個過程中每個服務之間的通訊又是單獨的網路請求,無論請求經過的哪個服務出了故障或者處理過慢都會對前端造成影響。
處理一個 Web 請求要呼叫的多個服務,為了能更方便的查詢哪個環節的服務出現了問題,現在常用的解決方案是為整個系統引入分散式鏈路跟蹤。
在分散式鏈路跟蹤中有兩個重要的概念:跟蹤(trace)和 跨度( span)。trace 是請求在分散式系統中的整個鏈路檢視,span 則代表整個鏈路中不同服務內部的檢視,span 組合在一起就是整個 trace 的檢視。
在整個請求的呼叫鏈中,請求會一直攜帶 traceid 往下游服務傳遞,每個服務內部也會生成自己的 spanid 用於生成自己的內部呼叫檢視,並和 traceid 一起傳遞給下游服務。
6.2 TraceId 生成規則
這種場景下,生成的 ID 除了要求唯一之外,還要求生成的效率高、吞吐量大。traceid 需要具備接入層的伺服器例項自主生成的能力,如果每個 trace 中的 ID 都需要請求公共的 ID 服務生成,純純的浪費網路頻寬資源。且會阻塞使用者請求向下遊傳遞,響應耗時上升,增加了沒必要的風險。所以需要伺服器例項最好可以自行計算 tracid,spanid,避免依賴外部服務。
產生規則:伺服器 IP + ID 產生的時間 + 自增序列 + 當前程序號 ,比如:
0ad1348f1403169275002100356696
前 8 位 0ad1348f 即產生 TraceId 的機器的 IP,這是一個十六進位制的數字,每兩位代表 IP 中的一段,我們把這個數字,按每兩位轉成 10 進位制即可得到常見的 IP 地址表示方式 10.209.52.143,您也可以根據這個規律來查詢到請求經過的第一個伺服器。
後面的 13 位 1403169275002 是產生 TraceId 的時間。之後的 4 位 1003 是一個自增的序列,從 1000 漲到 9000,到達 9000 後回到 1000 再開始往上漲。最後的 5 位 56696 是當前的程序 ID,為了防止單機多程序出現 TraceId 衝突的情況,所以在 TraceId 末尾新增了當前的程序 ID。
6.3 SpanId 生成規則
span 是層的意思,比如在第一個例項算是第一層, 請求代理或者分流到下一個例項處理,就是第二層,以此類推。透過層,SpanId 代表本次呼叫在整個呼叫鏈路樹中的位置。
假設一個 伺服器例項 A 接收了一次使用者請求,代表是整個呼叫的根節點,那麼 A 層處理這次請求產生的非服務呼叫日誌記錄 spanid 的值都是 0,A 層需要透過 RPC 依次呼叫 B、C、D 三個伺服器例項,那麼在 A 的日誌中,SpanId 分別是 0.1,0.2 和 0.3,在 B、C、D 中,SpanId 也分別是 0.1,0.2 和 0.3;如果 C 系統在處理請求的時候又呼叫了 E,F 兩個伺服器例項,那麼 C 系統中對應的 spanid 是 0.2.1 和 0.2.2,E、F 兩個系統對應的日誌也是 0.2.1 和 0.2.2。
根據上面的描述可以知道,如果把一次呼叫中所有的 SpanId 收集起來,可以組成一棵完整的鏈路樹。
spanid 的生成本質:在跨層傳遞透傳的同時,控制大小版本號的自增來實現的。
七 衍生問題2 - 短鏈
短網址主要功能包括網址縮短與還原兩大功能。相對於長網址,短網址可以更方便地在電子郵件,社交網路,微博和手機上傳播,例如原來很長的網址透過短網址服務即可生成相應的短網址,避免折行或超出字元限制。
常用的 ID 生成服務比如:MySQL ID 自增、 Redis 鍵自增、號段模式,生成的 ID 都是一串數字。短網址服務把客戶的長網址轉換成短網址,
實際是在域名後面拼接新產生的數字型別 ID,直接用數字 ID,網址長度也有些長,服務可以透過數字 ID 轉更高進位制的方式壓縮長度。這種演算法在短網址的技術實現上越來越多了起來,它可以進一步壓縮網址長度。