對于分布式Id來說,在面試過程中也是高頻面試題,所以主要針對分布式id實現方案進行詳細分析下。
應用場景
對于無論是單機還是分布式系統來說,對于很多場景需要全局唯一ID,
- 數據庫id唯一性
- 日志traceId 可以方便找到日志鏈,一般使用uuid 更多一點
- 冪等場景下,mq、api接口層面 保證冪等下
- 業務系統訂單id
UUID
最簡單的就是使用UUID,UUID的規則是一組32位16進制數字構成,形式就是8-4-4-4-8,主要就是利用當前時間和時鐘序列 和 全局唯一的IEEE機器識別。
UUID uuid = UUID.randomUUID();307e16d0-26d4-4fff-ab06-a9397b8369fb
優點:實現簡單,全局唯一,缺點是不具有連續性,并且占用空間比較大,對于mysql主鍵來說不具備友好。
數據庫自增ID
就目前公司內部,其實還是使用的數據庫自增id進行處理,對于數據庫來說的話,就是多個實例操作表自增id,對于業務來說使用自定義時間戳來實現。
還有一種方式就是創建一個單獨的表,通過數據庫本身的自增id來保證。
CREATE TABLE `test_order_id` (`id` bigint NOT NULL AUTO_INCREMENT,`title` char(1) NOT NULL,PRIMARY KEY (`id`),UNIQUE KEY `title` (`title`)
) ENGINE = InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET =utf8;BEGIN;REPLACE INTO test_order_id (title) values ('p') ;
SELECT LAST_INSERT_ID();COMMIT;
這種方式的優點就是 實現比較簡單,但是缺點就是不具備高可用性,如果mysql是單節點部署的話,那么整體就不可用。
高可用數據庫實例
部署多個數據庫實例,通過設置不同的自增,比如mysql實例1,自增是2,從1開始,mysql實例2 從2開始,那么就是 實例1 : 1、3、5、7,實例2: 2、4、6、8。
對于并發量不高的場景來說,可以解決,但是對于高并發場景來說,數據庫實例就是成為瓶頸。如果1秒1000個訂單,那么就需要頻繁訪問1000次數據庫,顯然這個網絡以及IO就成為短板。
號段模型
號段模式,其實就是通過在一個表中唯一一條記錄,然后通過業務區分,每次獲取一個區間的號,這樣比如獲取1-1000的號段,那么就只需要交互一次數據庫,并且通過添加version來避免出現并發修改數據的問題。
Redis
除了借助于mysql,還可以通過redis的自增 incr命令來實現自增ID,并且可以部署一主多從的架構,來保證高可用性。
public long nextId(String item){// 1.生成時間戳LocalDateTime now = LocalDateTime.now();// 格林威治時間差long nowSecond = now.toEpochSecond(ZoneOffset.UTC);// 我們需要獲取的 時間戳 信息long timestamp = nowSecond - BEGIN_TIMESTAMP;// 2.生成序號 --》 從Redis中獲取// 當前當前的日期String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));// 獲取對應的自增的序號Long increment = redisTemplate.opsForValue().increment("id:" + item + ":" + date);return timestamp << 32 | increment;}
具體原理其實就是 當前時間戳-制定時間 << 32 | 自增的id 這樣輸出的就是連續的id了
雪花算法
雪花算法整體結構就是 64位 0-41位位毫秒值,5位是數據中心id,5位機器id,最后12位是毫秒級別內的自增id
分析源碼 可以大概了解其核心流程
雪花算法時鐘回撥
在實際的生產環境中,分布式環境下,其實時鐘是很難保證統一的,所以就可能實現時間不一樣的情況,導致時鐘回撥問題,我們來分析兩類問題
情況1:UTC時間是8點01,實例機器是8點整
情況2:UTC時間是8點01 實例機器是8點03
對于情況1來說,其實就調整一下時間就好,1分鐘內的id生成不用就可以。但是對于第二種情況,相當于需要從3分回退到01分,那么其中的2分鐘的時間已經生成了對應的id,如果在次生成,就會出現id重復的問題?
如何解決呢
其實比較簡單,大方向就是如果時間短,那么就等得阻塞對應的時間就可以,如果超過1S以上,服務直接下線,通過其他實例獲取id就可以。
其他
業界有主流的其他方案,具體可以詳細看
百度、美團對應的解決方案,這里自己查看對應文檔就可以。
總結
從上面的分析可以知道 目前業界主流的方案,我們來抽絲剝繭下,對于設計一個分布式id來說,需要哪些核心點
1.需要順序 這樣可以簡單清晰
2.依賴的服務需要高可用、高性能
3.全局唯一
4.有具體的業務含義,比如針對訂單號:11xxx 開始,還款訂單21xxx,借款訂單31xxx。就比較清晰。