這篇文章主要講解了“Go語言如何實現(xiàn)Snowflake雪花算法”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Go語言如何實現(xiàn)Snowflake雪花算法”吧!
創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站建設(shè)、成都做網(wǎng)站與策劃設(shè)計,雅安網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10多年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務涵蓋:雅安等地區(qū)。雅安做網(wǎng)站價格咨詢:18982081108雪花算法的原始版本是scala版,用于生成分布式ID(純數(shù)字,時間順序),訂單編號等。
自增ID:對于數(shù)據(jù)敏感場景不宜使用,且不適合于分布式場景。 GUID:采用無意義字符串,數(shù)據(jù)量增大時造成訪問過慢,且不宜排序。
首先是 UUID ,它是由128位二進制組成,一般轉(zhuǎn)換成十六進制,然后用String表示。為了保證 UUID 的性,規(guī)范定義了包括網(wǎng)卡MAC地址、時間戳、名字空(Namespace)、隨機或偽隨機數(shù)、時序等元素,以及從這些元素生成 UUID 的算法。
UUID 有五個版本:
版本1:基于時間戳和mac地址
版本2:基于時間戳,mac地址和POSIX UID/GID
版本3:基于MD5哈希算法
版本4:基于隨機數(shù)
版本5:基于SHA-1哈希算法
UUID 的優(yōu)缺點:
優(yōu)點是代碼簡單,性能比較好。缺點是沒有排序,無法保證按序遞增;其次是太長了,存儲數(shù)據(jù)庫占用空間比較大,不利于檢索和排序。
如果是使用 mysql 數(shù)據(jù)庫,那么通過設(shè)置主鍵為 auto_increment 是最容易實現(xiàn)單調(diào)遞增的ID 的方法,并且它也方便排序和索引。
但是缺點也很明顯,由于過度依賴數(shù)據(jù)庫,那么受限于數(shù)據(jù)庫的性能會導致并發(fā)性并不高;再來就是如果數(shù)據(jù)量太大那么會給分庫分表會帶來問題;并且如果數(shù)據(jù)庫宕機了,那么這個功能是無法使用的。
Redis 目前已在很多項目中是一個不可或缺的存在,在 Redis 中有兩個命令 Incr、IncrBy ,因為Redis是單線程的所以通過這兩個指令可以能保證原子性從而達到生成值的目標,并且性能也很好。
但是在 Redis 中,即使有 AOF 和 RDB ,但是依然會存在數(shù)據(jù)丟失,有可能會造成ID重復;再來就是需要依賴 Redis ,如果它不穩(wěn)定,那么會影響 ID 生成。
通過上面的一個個分析,終于引出了我們的分布式雪花算法 Snowflake ,它最早是twitter內(nèi)部使用的分布式環(huán)境下的ID生成算法。在2014年開源。開源的版本由scala編寫,大家可以再找個地址找到這版本。
https://github.com/twitter-archive/snowflake/tags
它有以下幾個特點:
能滿足高并發(fā)分布式系統(tǒng)環(huán)境下ID不重復;
基于時間戳,可以保證基本有序遞增;
不依賴于第三方的庫或者中間件;
Snowflake 結(jié)構(gòu)是一個 64bit 的 int64 類型的數(shù)據(jù)。如下:
位置 | 大小 | 作用 |
---|---|---|
0~11bit | 12bits | 序列號,用來對同一個毫秒之內(nèi)產(chǎn)生不同的ID,可記錄4095個 |
12~21bit | 10bits | 10bit用來記錄機器ID,總共可以記錄1024臺機器 |
22~62bit | 41bits | 用來記錄時間戳,這里可以記錄69年 |
63bit | 1bit | 符號位,不做處理 |
上面只是一個將 64bit 劃分的通用標準,一般的情況可以根據(jù)自己的業(yè)務情況進行調(diào)整。例如目前業(yè)務只有機器10臺左右預計未來會增加到三位數(shù),并且需要進行多機房部署,QPS 幾年之內(nèi)會發(fā)展到百萬。
那么對于百萬 QPS 平分到 10 臺機器上就是每臺機器承擔十萬級的請求即可,12 bit 的序列號完全夠用。對于未來會增加到三位數(shù)機器,并且需要多機房部署的需求我們僅需要將 10 bits 的 work id 進行拆分,分割 3 bits 來代表機房數(shù)共代表可以部署8個機房,其他 7bits 代表機器數(shù)代表每個機房可以部署128臺機器。那么數(shù)據(jù)格式就會如下所示:
其實看懂了上面的數(shù)據(jù)結(jié)構(gòu)之后,需要自己實現(xiàn)一個雪花算法是非常簡單,步驟大致如下:
獲取當前的毫秒時間戳;
用當前的毫秒時間戳和上次保存的時間戳進行比較;
如果和上次保存的時間戳相等,那么對序列號 sequence 加一;
如果不相等,那么直接設(shè)置 sequence 為 0 即可;
然后通過或運算拼接雪花算法需要返回的 int64 返回值。
首先我們需要定義一個 Snowflake 結(jié)構(gòu)體:
type Snowflake struct { sync.Mutex // 鎖 timestamp int64 // 時間戳 ,毫秒 workerid int64 // 工作節(jié)點 datacenterid int64 // 數(shù)據(jù)中心機房id sequence int64 // 序列號 }
然后我們需要定義一些常量,方便我們在使用雪花算法的時候進行位運算取值:
const ( epoch = int64(1577808000000) // 設(shè)置起始時間(時間戳/毫秒):2020-01-01 00:00:00,有效期69年 timestampBits = uint(41) // 時間戳占用位數(shù) datacenteridBits = uint(2) // 數(shù)據(jù)中心id所占位數(shù) workeridBits = uint(7) // 機器id所占位數(shù) sequenceBits = uint(12) // 序列所占的位數(shù) timestampMax = int64(-1 ^ (-1 << timestampBits)) // 時間戳較大值 datacenteridMax = int64(-1 ^ (-1 << datacenteridBits)) // 支持的較大數(shù)據(jù)中心id數(shù)量 workeridMax = int64(-1 ^ (-1 << workeridBits)) // 支持的較大機器id數(shù)量 sequenceMask = int64(-1 ^ (-1 << sequenceBits)) // 支持的較大序列id數(shù)量 workeridShift = sequenceBits // 機器id左移位數(shù) datacenteridShift = sequenceBits + workeridBits // 數(shù)據(jù)中心id左移位數(shù) timestampShift = sequenceBits + workeridBits + datacenteridBits // 時間戳左移位數(shù) )
需要注意的是由于 -1 在二進制上表示是:
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
所以想要求得 41bits 的 timestamp 較大值可以將 -1 向左位移 41 位,得到:
11111111 11111111 11111110 00000000 00000000 00000000 00000000 00000000
那么再和 -1 進行 ^異或運算:
00000000 00000000 00000001 11111111 11111111 11111111 11111111 11111111
這就可以表示 41bits 的 timestamp 較大值。datacenteridMax、workeridMax也同理。
接著我們就可以設(shè)置一個 NextVal 函數(shù)來獲取 Snowflake 返回的 ID 了:
func (s *Snowflake) NextVal() int64 { s.Lock() now := time.Now().UnixNano() / 1000000 // 轉(zhuǎn)毫秒 if s.timestamp == now { // 當同一時間戳(精度:毫秒)下多次生成id會增加序列號 s.sequence = (s.sequence + 1) & sequenceMask if s.sequence == 0 { // 如果當前序列超出12bit長度,則需要等待下一毫秒 // 下一毫秒將使用sequence:0 for now <= s.timestamp { now = time.Now().UnixNano() / 1000000 } } } else { // 不同時間戳(精度:毫秒)下直接使用序列號:0 s.sequence = 0 } t := now - epoch if t > timestampMax { s.Unlock() glog.Errorf("epoch must be between 0 and %d", timestampMax-1) return 0 } s.timestamp = now r := int64((t)<<timestampShift | (s.datacenterid << datacenteridShift) | (s.workerid << workeridShift) | (s.sequence)) s.Unlock() return r }
上面的代碼也是非常的簡單,看看注釋應該也能懂,我這里說說最后返回的 r 系列的位運算表示什么意思。
首先 t 表示的是現(xiàn)在距離 epoch 的時間差,我們 epoch 在初始化的時候設(shè)置的是2020-01-01 00:00:00,那么對于 41bit 的 timestamp 來說會在 69 年之后才溢出。對 t 進行向左位移之后,低于 timestampShift 位置上全是0 ,由 datacenterid、workerid、sequence 進行取或填充。
在當前的例子中,如果當前時間是2021/01/01 00:00:00,那么位運算結(jié)果如下圖所示:
感謝各位的閱讀,以上就是“Go語言如何實現(xiàn)Snowflake雪花算法”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對Go語言如何實現(xiàn)Snowflake雪花算法這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
分享題目:Go語言如何實現(xiàn)Snowflake雪花算法-創(chuàng)新互聯(lián)
URL地址:http://chinadenli.net/article12/dhppdc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開發(fā)、ChatGPT、品牌網(wǎng)站制作、電子商務、動態(tài)網(wǎng)站、網(wǎng)站導航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容