高并发分布式下生成全局ID的几种方法
文章目錄
- 利用全球唯一UUID生成訂單號碼
- 基于數據庫實現全局ID
- 基于redis生成全局ID策略
- 號段模式
- 雪花算法
利用全球唯一UUID生成訂單號碼
**推薦理由: 簡單方便,網上雖然說UUID出現了重復的事件,但是我實際測試,并沒有發現問題的復現,所以他作為一個國際化的ID生成算法,還是很不錯的,生成的ID的性能很好,缺點就是生成的UUID的為字符串,無法實現自增,同時比較暫內存空間
**
UUID的組成部分:當前日期+時鐘序列+隨機數+全局唯一的IEE機器識別號
全局唯一的IEE機器識別號:如果有網卡,從網卡MAC地址獲取,沒有網卡從其他方式進行獲取
用法
前面已經說過UUID的組成部分開頭不是當前日期嗎?為什么這里還有字母呢? 其實他是ASC碼進行轉換的
UUID的適用場景: 生成token的場景比較多 去掉"-"符號
基于數據庫實現全局ID
**如果是2個數據庫 A,B 在高并發的環境下 如果按照主鍵的默認的自增策略的話, 因為主鍵自增唯一的特性,可能會在并發的情況下,創建重復的ID與主鍵的特性違背,那么如何避免這個問題呢?
答案:此時我們可以給每個數據庫設置不同的步長(每次增加多少), 比如A 每次增加的2 起始值為0, B 每次增加2 起始值為 1 每次增加的2 ,那么A的數據庫就是 13 5 7 … B的數據是2,4,6,8 這樣就不會導致兩者之間的沖突問題
那么如何設置步長呢?
查看數據庫的步長**
修改自增的步長
修改起始值
SET @@auto_increment_offset=5;基于redis生成全局ID策略
基于Redis生成生成全局id策略 因為Redis是單線的,天生保證原子性**,可以使用Redis的原子操作 INCR和INCRBY來實現
優點: 不依賴于數據庫,靈活方便,且性能優于數據庫。 數字ID天然排序,對分頁或者需要排序的結果很有幫助。
缺點: 如果系統中沒有Redis,還需要引入新的組件,增加系統復雜度。 需要編碼和配置的工作量比較大。
注意:在Redis集群情況下,同樣和Redis一樣需要設置不同的增長步長,同時key一定要設置有效期 可以使用Redis集群來獲取更高的吞吐量。假如一個集群中有5臺Redis。可以初始化每臺Redis的值分別是1,2,3,4,5,然后步長都是5。各個Redis生成的ID為: A:1,6,11,16,21 B:2,7,12,17,22 C:3,8,13,18,23 D:4,9,14,19,24 E:5,10,15,20,25,
但是redis 需要進行連接,所以連接redis需要占用一定的寬帶
獲取值得方法為**
在生成訂單號碼的時候,會有一個補0的問題就說一個ID 最后一位是五位數,位數不夠0 來湊,那么如何實現呢
15151515151515-00001
具體做法為
String orderId = prefix() + "-" + String.format("%1$05d", andIncrement);其中 5 d中的 5 是指的5位,根據不同的需求可以具體修改
因為redis是單線程的為了提高效率可以做集群,但是 集群也會發生Id重復的問題,前面雖然說得是他是單線程不會有安全的問題,但是那也是對于一臺機器而言.所以對于這個問題,我們也可以設置步長解決Id重復的問題
以上基本上就已經解決了大部分的問題,但同事還需要設置key的有效期
redisAtomicLong.expire(24, TimeUnit.HOURS);號段模式
我們可以使用號段的方式來獲取自增ID,號段可以理解成批量獲取,比如DistributIdService從數據庫獲取ID時,如果能批量獲取多個ID并緩存在本地的話,那樣將大大提供業務應用獲取ID的效率。
比如DistributIdService每次從數據庫獲取ID時,就獲取一個號段,比如(1,1000],這個范圍表示了1000個ID,業務應用在請求DistributIdService提供ID時,DistributIdService只需要在本地從1開始自增并返回即可,而不需要每次都請求數據庫,一直到本地自增到1000時,也就是當前號段已經被用完時,才去數據庫重新獲取下一號段。
所以,我們需要對數據庫表進行改動,如下
CREATE TABLE id_generator (id int(10) NOT NULL,current_max_id bigint(20) NOT NULL COMMENT '當前最大id',increment_step int(10) NOT NULL COMMENT '號段的長度',PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;這個數據庫表用來記錄自增步長以及當前自增ID的最大值(也就是當前已經被申請的號段的最后一個值),因為自增邏輯被移到DistributIdService中去了,所以數據庫不需要這部分邏輯了。 這種方案不再強依賴數據庫,就算數據庫不可用,那么DistributIdService也能繼續支撐一段時間。但是如果DistributIdService重啟,會丟失一段ID,導致ID空洞。 為了提高
DistributIdService的高可用,需要做一個集群,業務在請求DistributIdService集群獲取ID時,會隨機的選擇某一個DistributIdService節點進行獲取,對每一個DistributIdService節點來說,數據庫連接的是同一個數據庫,
那么可能會產生多個DistributIdService節點同時請求數據庫獲取號段,那么這個時候需要利用樂觀鎖來進行控制,比如在數據庫表中增加一個version字段,在獲取號段時使用如下SQL
因為newMaxId是DistributIdService中根據oldMaxId+步長算出來的,只要上面的update更新成功了就表示號段獲取成功了。
為了提供數據庫層的高可用,需要對數據庫使用多主模式進行部署,對于每個數據庫來說要保證生成的號段不重復,這就需要利用最開始的思路,再在剛剛的數據庫表中增加起始值和步長,比如如果現在是兩臺Mysql,那么mysql1將生成號段(1,1001],自增的時候序列為1,3,4,5,7…mysql1將生成號段(2,1002],自增的時候序列為2,4,6,8,10…
在TinyId中還增加了一步來提高效率,在上面的實現中,ID自增的邏輯是在DistributIdService中實現的,而實際上可以把自增的邏輯轉移到業務應用本地,這樣對于業務應用來說只需要獲取號段,每次自增時不再需要請求調用DistributIdService了。
雪花算法
Twitter的snowflake(雪花)算法 snowflake是Twitter開源的分布式ID生成算法,結果是一個long型的ID。
其核心思想是: 高位隨機+毫秒數+機器碼(數據中心+機器id)+10位的流水號碼
Github地址: https://github.com/twitter-archive/snowflake
這個性能最高,本打算拉下來講講.沒想到瀏覽器一直加載,剩下的就交給大家去探索吧,加油! 0.0
總結
以上是生活随笔為你收集整理的高并发分布式下生成全局ID的几种方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 根目录缓存,Androi
- 下一篇: 推荐一个做笔记的软件_印象笔记