日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Go實現分布式唯一ID的生成之雪花算法_Golang

作者:Blockchain210 ? 更新時間: 2022-06-30 編程語言

分布式唯一ID的生成

背景:

在分布式架構下,唯一序列號生成是我們在設計一個尤其是數據庫使用分庫分表的時候會常見的一個問題

特性:

全局唯一,這是基本要求,不能出現重復數字類型,趨勢遞增,后面的ID必須比前面的大長度短,能夠提高查詢效率,這也是從MySQL數據庫規范出發的,尤其是ID作為主鍵時**信息安全,**如果ID連續生成,勢必會泄露業務信息,所以需要無規則不規則高可用低延時,ID生成快,能夠扛住高并發,延時足夠低不至于成為業務瓶頸.

雪花算法:

? snowflake是推特開源的分布式ID生成算法

結果: long 型的ID號(64位的ID號)

核心思想(生成的ID號是64位那么就對64位進行劃分賦予特別的含義):

在這里插入圖片描述

41bit-時間戳決定了該算法生成ID號的可用年限.

10bit-工作機器編號決定了該分布式系統的擴容性即機器數量.

12bit-序列號決定了每毫秒單機系統可以生成的序列號

拓展:什么是時間戳?

北京時間1970年01月01日08時00分00秒到此時時刻的總秒數

優勢:

//實現方法:
package main


import (
	"errors"
	"fmt"
	"sync"
	"time"
)

/*
	雪花算法(snowFlake)的具體實現方案:
 */

type SnowFlake struct{
	mu sync.Mutex
	//雪花算法開啟時的起始時間戳
	twepoch int64

	//每一部分占用的位數
	workerIdBits     int64 //每個數據中心的工作機器的編號位數
	datacenterIdBits int64 //數據中心的編號位數
	sequenceBits     int64 //每個工作機器每毫秒遞增的位數

	//每一部分最大的數值
	maxWorkerId int64
	maxDatacenterId int64
	maxSequence int64

	//每一部分向左移動的位數
	workerIdShift int64
	datacenterIdShift int64
	timestampShift int64

	//當前數據中心ID號
	datacenterId int64
	//當前機器的ID號
	workerId int64
	//序列號
	sequence int64
	//上一次生成ID號前41位的毫秒時間戳
	lastTimestamp int64
}

/*
	獲取毫秒的時間戳
 */
func (s *SnowFlake)timeGen()int64{
	return time.Now().UnixMilli()
}
/*
	獲取比lastTimestamp大的當前毫秒時間戳
 */
func (s *SnowFlake)tilNextMills()int64{
	timeStampMill:=s.timeGen()
	for timeStampMill<=s.lastTimestamp{
		timeStampMill=s.timeGen()
	}
	return timeStampMill
}
func (s *SnowFlake)NextId()(int64,error){
	s.mu.Lock()
	defer s.mu.Unlock()
	nowTimestamp:=s.timeGen()//獲取當前的毫秒級別的時間戳
	if nowTimestamp<s.lastTimestamp{
		//系統時鐘倒退,倒退了s.lastTimestamp-nowTimestamp
		return -1,errors.New(fmt.Sprintf("clock moved backwards, Refusing to generate id for %d milliseconds",s.lastTimestamp-nowTimestamp))
	}
	if nowTimestamp==s.lastTimestamp{
		s.sequence=(s.sequence+1)&s.maxSequence
		if s.sequence==0{
			 //tilNextMills中有一個循環等候當前毫秒時間戳到達lastTimestamp的下一個毫秒時間戳
			nowTimestamp=s.tilNextMills()
		}
	}else{
		s.sequence=0
	}
	s.lastTimestamp=nowTimestamp
	return (nowTimestamp-s.twepoch)<<s.timestampShift| //時間戳差值部分
		s.datacenterId<<s.datacenterIdShift| //數據中心部分
		s.workerId<<s.workerIdShift| //工作機器編號部分
		s.sequence, //序列號部分
		nil
}

func NewSnowFlake(workerId int64,datacenterId int64)(*SnowFlake,error){
	mySnow:=new(SnowFlake)
	mySnow.twepoch=time.Now().Unix() //返回當前時間的時間戳(時間戳是指北京時間1970年01月01日8時0分0秒到此時時刻的總秒數)
	if workerId<0||datacenterId<0{
		return nil,errors.New("workerId or datacenterId must not lower than 0 ")
	}
	/*
		標準的雪花算法
	 */
	mySnow.workerIdBits =5
	mySnow.datacenterIdBits=5
	mySnow.sequenceBits=12

	mySnow.maxWorkerId=-1^(-1<<mySnow.workerIdBits)         //64位末尾workerIdBits位均設為1,其余設為0
	mySnow.maxDatacenterId=-1^(-1<<mySnow.datacenterIdBits) //64位末尾datacenterIdBits位均設為1,其余設為0
	mySnow.maxSequence=-1^(-1<<mySnow.sequenceBits)  //64位末尾sequenceBits位均設為1,其余設為0

	if workerId>=mySnow.maxWorkerId||datacenterId>=mySnow.maxDatacenterId{
		return nil,errors.New("workerId or datacenterId must not higher than max value ")
	}
	mySnow.workerIdShift=mySnow.sequenceBits
	mySnow.datacenterIdShift=mySnow.sequenceBits+mySnow.workerIdBits
	mySnow.timestampShift=mySnow.sequenceBits+mySnow.workerIdBits+mySnow.datacenterIdBits

	mySnow.lastTimestamp=-1
	mySnow.workerId=workerId
	mySnow.datacenterId=datacenterId

	return mySnow,nil
}



func main(){
	//模擬實驗是生成并發400W個ID,所需要的時間
	mySnow,_:=NewSnowFlake(0,0)//生成雪花算法
	group:=sync.WaitGroup{}
	startTime:=time.Now()
	generateId:=func (s SnowFlake,requestNumber int){
		for i:=0;i<requestNumber;i++{
			s.NextId()
			group.Done()
		}
	}
	group.Add(4000000)
	//生成并發的數為4000000
	currentThreadNum:=400
	for i:=0;i<currentThreadNum;i++{
		generateId(*mySnow,10000)
	}
	group.Wait()
	fmt.Printf("time: %v\n",time.Now().Sub(startTime))
}

在這里插入圖片描述

以上分析生成400WID號只需要803.1006ms(所以單機上可以每秒生成的ID數在400W以上)

優點:

毫秒數在高位,自增序列在低位,整個ID都是趨勢遞增不依賴數據庫等第三方系統,以服務的方式部署,穩定性更高,生成的ID性能也是非常高的可以根據自身業務特性分配bit位,非常靈活

缺陷:

1. 依賴機器時鐘,如果**機器時鐘回撥**,會導致重復ID生成.
2. 在單機上是遞增的,但是由于設計到分布式環境下,每臺機器上的時鐘不可能完全同步,有時候會出現不是全局遞增的情況.

如何解決單機系統中時鐘回撥問題:

? 可以分為兩種情況:

1. 如果**時間回撥時間較短,比如配置5ms以內**,那么可以直接等候一定的時間,讓機器時間追上來
2. 如果**時間回撥時間較長**,我們不能接收這么長的阻塞等候,那么就有兩個策略,直接拒絕,拋出異常;或者通過RD時鐘回滾

布式環境下,每臺機器上的時鐘不可能完全同步,有時候會出現不是全局遞增的情況.

如何解決單機系統中時鐘回撥問題:

? 可以分為兩種情況:

1. 如果**時間回撥時間較短,比如配置5ms以內**,那么可以直接等候一定的時間,讓機器時間追上來
2. 如果**時間回撥時間較長**,我們不能接收這么長的阻塞等候,那么就有兩個策略,直接拒絕,拋出異常;或者通過RD時鐘回滾

參考博客高并發情況下,雪花ID一秒400W個,以及分布式ID算法(詳析)

原文鏈接:https://blog.csdn.net/Blockchain210/article/details/124217374

欄目分類
最近更新