發號器的設計

2022-08-16 00:18:14 字數 1542 閱讀 9230

資料庫中的每條記錄都需要乙個id,即使在分庫分表後這個id需要全域性唯一性。因此,分庫分表後不能使用mysql自帶的自增id了。因為不通的庫之間的id可能是一樣的。

我們以記錄海量的使用者資訊為例,可能會想到身份證號、**號碼或者email。但是這些資訊是會變的。如果使用者要修改這些資訊,那麼id就失效了。無異於新增一條記錄,刪掉原來的記錄。

雪花演算法可以提供全域性唯一的id。雪花演算法生成的id一般是分幾段的,下圖就是典型的id組成:41位時間戳(一般是毫秒級?)+10位機器id+12位序列號。

41位時間戳毫秒級的大概有69年,10位機器id可以表示乙個由1024臺機器組成的發號器集群。12位序列號表示一毫秒每台機器可以提供4000個不通的id,也就是一秒鐘400w個。。。

這是個好問題,我之前沒考慮過。

確實,uuid是32個16進製制組成的字串,也具有全域性唯一性。

那麼為何不用呢。此處需要考慮業務id需要哪些特性:1、全域性唯一性(uuid符合)2、生成效能要好(uuid符合)3、寫入效能要好 4、最好要有具體的業務含義,方便定位問題。

前兩點uuid都符合,我們都知道mysql對於隨機寫的tps是很差的,對於順序寫效能很好。因為順序寫入節約了一次尋道時間。而寫入是要按照id排序的。uuid是隨機的,不是遞增的,所以如果用uuid作為mysql的id是會嚴重影響寫入效能的。

不用uuid作為mysql id的另乙個原因是uuid不具備業務解釋性,比如我們之前設計的id就是用類似於雪花演算法生成的,時間戳+機器id+其他一些業務資訊,這樣出現了badcase,第一時間拿到id就知道是哪個方面出問題了。比如文章id最後兩位表示文章**,那麼如果badcase的id都集中在某乙個**,那麼直覺告訴我們某些特定業務出問題了,便於排查問題。

大致有兩種做法:

一、嵌入在**裡面,每台機器有自己的機器id,這樣就能生成唯一的id了。這樣做的好處是沒有網路互動,效能好。不過如果機器眾多或者機器重啟,比較難保證每台機器id是唯一的,這樣就要引入zk等元件來保證id的一致性,這樣就引入了新的zk問題。

二、第二種做法是,搭建乙個發號器集群。這樣做每次插入id都會有乙個額外的網路互動,但是對於內網來說,效能損失很小,這方面的損失是可以接受的。由於發號器效能很好,所以只需要少量機器就能提供大量的id,qps很高。這樣可以減少機器數量,id中用於表示機器id的位數可以減少,提供的號就會變多。如果只有一台發號器,多個備份發號器,那麼可以無需機器id。一般情況下有少量的幾台一起提供發號服務,此時由於機器較少,可以把機器id寫到配置檔案中,也不會因為方法一帶來的第三方元件問題。

雪花演算法最大的問題就是他依賴於時間戳,如果時間不准了,那麼就有可能導致產生重複的id,此時需要調整時間,再發號。

還有乙個可能引入的問題是如果qps不高,那麼以毫秒為粒度的id可能分配不均勻,極端例子就是qps為1,每秒的第乙個毫秒產生乙個id,業務方拿到id後根據時間戳去hash到每個庫中,那麼就會導致資料的不均勻。因此,很多實踐過程中都是根據自己的需要進行變種的。可以把雪花演算法前面的時間戳粒度變為秒,而不是毫秒。這樣id至於時間的分布就會相對均勻很多。

分布式發號器 Vesta

1 基於時間戳 比如流水號規則如下 xx yyyymmdd n位隨機數,這也是企業級應用開發常用的規則。此流水號對人比較友好,可識別性高,但容量受後面隨機數的限制,且資料量越大,生成時難度越高。前三部分每天的流水號基本固定,後面的n位隨機數生成後,需要校驗此前不存在,可依賴redis的set機制,每...

Spark中ID發號器實現思路

spark作為乙個分布式處理框架,處理資料非常快。可是我也不知道作者基於什麼樣的設計哲學,限制了使用者對於一些資料的操作。例如你無法改變乙個rdd的內容。無法乙個split把資料集分割成兩份。無法獲得rdd的分割槽資訊,無法再map的時候知道自己處於哪個分割槽。這對於id分配來說實在是太難辦了。甚至...

唯一流水號的產生(分布式發號器)

在網際網路世界裡,產生唯 一流水號的服務系統俗稱發號器。1 uuid雖然能夠保證id的唯一性,但是無法滿足業務系統需要的很多其他特性,例如時間粗略有序性 可反解和可製造性。2 效能較差。uuid比較長 占用空間大,會間接導致資料庫效能下降 如使用資料庫的自增欄位,然而自增字段完全 依賴於資料庫,則在...