# :-: 分布式ID生成器解決方
## 一、介紹
### 1\. 什么情況下我們需要ID生成器
* 數據庫水平拆分的情況下,主鍵由于需要作為業務標識使用,需要唯一。
* 業務編號需要暴露給用戶,但是又不想被用戶猜到需要被隱藏的業務編號
* 業務編號需要體現業務信息,比如訂單分類訂單渠道等等
### 2\. ID生成器設計目標
* 全局唯一
* 每秒可生成100W+
* 趨于遞增(對索引友好)
* 高可用
* 可伸縮
## 二、常見ID生成方案
### 1\. UUID
UUID 是 通用唯一識別碼(Universally Unique Identifier)的縮寫,是一種軟件建構的標準,亦為開放軟件基金會組織在分布式計算環境領域的一部分????UUID是由一組32位數的16進制數字所構成,是故UUID理論上的總數為1632=2128,約等于3.4 x 1038。也就是說若每納秒產生1兆個UUID,要花100億年才會將所有UUID用完。????UUID的標準型式包含32個16進制數字,以連字號分為五段,形式為8-4-4-4-12的32個字符。示例: 550e8400-e29b-41d4-a716-446655440000**每秒產生10億筆UUID,100年后只產生一次重復的機率是50%**
* 優點:
* 本地生成,沒有網絡消耗
* 可以任意水平擴展
* 生成效率高
* 生成節點不限
* 缺點
* 沒有排序,無法保證趨勢遞增。
* UUID往往是使用字符串存儲,查詢的效率比較低。
* 存儲空間比較大,如果是海量數據庫,就需要考慮存儲量的問題。
* 傳輸數據量大
* 不可讀
### 2\. 數據庫自增列
可以通過設置bigint類型的數據庫自增列,在事務中通過Insert操作獲取主鍵Id
* 優點
* 可以實現ID完全遞增
* 部署簡單,有DB就可以
* 缺點
* 生成效率差,取決于數據庫性能指標,每秒生成一萬ID都很難
* 依賴于數據庫,如果DB發生故障,在做主從切換的時候可能會引發BUG
### 3\. Redis生成ID
當使用數據庫來生成ID性能不夠要求的時候,我們可以嘗試使用Redis來生成ID。這主要依賴于Redis是單線程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來實現。????????可以使用Redis集群來獲取更高的吞吐量。假如一個集群中有5臺Redis。可以初始化每臺Redis的值分別是1,2,3,4,5,然后步長都是5。各個Redis生成的ID為:A:1,6,11,16,21B:2,7,12,17,22C:3,8,13,18,23D:4,9,14,19,24E:5,10,15,20,25????????這個,隨便負載到哪個機確定好,未來很難做修改。但是3-5臺服務器基本能夠滿足器上,都可以獲得不同的ID。但是步長和初始值一定需要事先需要了。使用Redis集群也可以方式單點故障的問題。????????另外,比較適合使用Redis來生成每天從0開始的流水號。比如訂單號=日期+當日自增長號。可以每天在Redis中生成一個Key,使用INCR進行累加。
* 優點
* 不依賴于數據庫,靈活方便,且性能優于數據庫。
* 數字ID天然排序,對分頁或者需要排序的結果很有幫助。
* 缺點
* 如果系統中沒有Redis,還需要引入新的組件,增加系統復雜度。
* 需要編碼和配置的工作量比較大。
### 4\. MongoDB的ObjectId
MongoDB的ObjectId和snowflake算法類似。它設計成輕量型的,不同的機器都能用全局唯一的同種方法方便地生成它。MongoDB 從一開始就設計用來作為分布式數據庫,處理多個節點是一個核心要求。使其在分片環境中要容易生成得多。其格式如下:????????前4 個字節是從標準紀元開始的時間戳,單位為秒。時間戳,與隨后的5 個字節組合起來,提供了秒級別的唯一性。由于時間戳在前,這意味著ObjectId 大致會按照插入的順序排列。這對于某些方面很有用,如將其作為索引提高效率。這4 個字節也隱含了文檔創建的時間。絕大多數客戶端類庫都會公開一個方法從ObjectId 獲取這個信息。????????接下來的3 字節是所在主機的唯一標識符。通常是機器主機名的散列值。這樣就可以確保不同主機生成不同的ObjectId,不產生沖突。為了確保在同一臺機器上并發的多個進程產生的ObjectId 是唯一的,接下來的兩字節來自產生ObjectId 的進程標識符(PID)。????????前9 字節保證了同一秒鐘不同機器不同進程產生的ObjectId 是唯一的。后3 字節就是一個自動增加的計數器,確保相同進程同一秒產生的ObjectId 也是不一樣的。同一秒鐘最多允許每個進程擁有2563(16 777 216)個不同的ObjectId。
### 5\. Twitter的snowflake算法
snowflake是Twitter開源的分布式ID生成算法,結果是一個long型的ID。其核心思想是:使用41bit作為毫秒數,10bit作為機器的ID(5個bit是數據中心,5個bit的機器ID),12bit作為毫秒內的流水號(意味著每個節點在每毫秒可以產生 4096 個 ID),最后還有一個符號位,永遠是0。
| 分段 | 作用 | 說明 |
| --- | --- | --- |
| 1bit | 保留 | — |
| 41bit | 時間戳,精確到毫秒 | 可以支持69年的跨度 |
| 5bit | DatacenterId | 可以最多支持32個節點 |
| 5bit | WorkerId | 可以最多支持32個節點 |
| 12bit | 毫秒內的計數 | 支持每個節點每毫秒產生4096個ID |
理論上單機每秒400W+,最多每秒可以生成41億+的ID
* 優點
* ID趨勢遞增
* 生成效率高,單機每秒400W+
* 支持線性擴充
* 穩定性高,不依賴DB等服務
* 缺點
* 依賴服務器時間,如果服務器時間發生回撥,可能導致生成重復ID
* 在單機上是遞增的,但是由于涉及到分布式環境,每臺機器上的時鐘不可能完全同步,也許有時候也會出現不是全局遞增的情況
- 項目介紹
- 項目聲明
- 項目簡介
- 架構設計
- 項目亮點功能介紹
- 技術棧介紹
- 核心功能
- 運行環境
- 項目更新日志
- 文檔更新日志
- F&Q
- 部署教程
- 環境準備
- JDK安裝
- JDK1.8,17共存
- maven
- 分布式緩存Redis
- 單機版
- 集群
- 注冊&配置中心alibaba/nacos
- 介紹
- Nacos安裝
- Nacos配置中心
- Nacos注冊發現
- Nacos生產部署方案
- 服務監控-BootAdmin
- 基本介紹
- 如何使用
- 整合Admin-Ui
- 客戶端配置
- 鏈路追蹤
- 基本介紹
- SkyWalking-1
- Skywalking-1
- 消息隊列
- Kafka
- docker安裝kafka
- Linux集群
- Maven私服
- nexus安裝部署
- nexus使用介紹
- 全文搜索elasticsearch
- windows集群搭建
- docker安裝es
- ElasticHD
- linux集群部署
- 統一日志解決方案
- 日志解決方案設計
- 介紹與相關資料
- ELK安裝部署
- elasticsearch 7.5
- logstash-7.5
- kibana-7.5
- filebeat
- 服務監控-Prometheus
- Prometheus安裝配置
- Prometheus介紹
- grafana
- 持續集成部署CICD
- 自動化部署Jenkins
- 安裝部署win
- 打包發布遠程執行
- 安裝部署linux
- jenkins+gitlab+docker容器化工程自動化部署
- Git
- CICD說明
- 阿里云效
- CentOS_MYSQL安裝
- docker
- 安裝
- Docker安裝Nginx
- Docker部署啟動springboot
- dockerCompose
- harbor
- Docker私有鏡像倉庫
- Portainer
- Docker遠程連接設置
- 打包工程
- 必要啟動模塊
- 核心模塊
- 登錄認證
- 緩存功能
- 日志模塊
- 分布式鎖
- 消息隊列
- 異常處理
- 系統接口
- 參數驗證
- es檢索
- 數據導出
- 系統設計
- 系統總體架構
- 擴展模塊(可選)
- 限流熔斷alibaba/sentinel
- 使用Sentinel實現gateway網關及服務接口限流
- Sentinel使用Nacos存儲規則及同步
- 服務調用Feign
- Feign基本介紹
- 如何使用
- 負載均衡
- 請求超時
- 請求攔截器
- 分布式任務調度
- XXL-JOB
- 分布式事務
- TX-LCN
- Seata
- Seata原理解析
- 數據庫分庫分表
- swagger文檔
- 分布式ID生成器解決方案
- 服務網關CloudGateway
- 基本介紹
- 使用網關
- 路由配置
- 全局過濾器
- 服務認證授權架構設計
- 認證服務流程
- 授權服務流程
- 系統冪等性設計與實踐
- 分布式日志鏈路跟蹤
- 實時搜索系統設計
- 應用性能
- 壓力測試工具
- Apache JMeter介紹和安裝
- ApacheJMeter使用
- JVM
- JVM性能調優
- 常見JVM內存錯誤及解決方案
- JVM 分析工具詳解
- Spring Cloud性能調優
- Linux運維
- Linux 常用命令
- Linux開啟端口