基于Go的雪花算法

发表时间:2022-10-28 08:13:27 作者:赛马牛

最近在写一个GO项目,涉及分库分表,既然分库分表,在分库分表的时候,如果表主键ID依赖自增会有冲突(如果是根据表数来跳Id的方式,那么在表数增加的时候,也会很麻烦),所以在追求后期易扩展和高性能的情况下,那就得说到雪花算法了。雪花算法生成ID的性能是很快的,速度快的机器一秒钟可以生产几十万个,并且可以横向扩展机器。

上代码:

import (
"fmt"
"strconv"
"strings"
"sync"
"time"
)

type Snowflake struct {
sync.Mutex // 锁
timestamp int64 // 时间戳 ,毫秒
DataCenterId int64 // 数据中心机房id
WorkerId int64 // 工作节点
sequence int64 // 序列号
}

const (
epoch = int64(1577808000000) // 设置起始时间(时间戳/毫秒):2020-01-01 00:00:00,有效期69年
timestampBits = uint(41) // 时间戳占用位数
dataCenterIdBits = uint(7) // 数据中心id所占位数
workerIdBits = uint(7) // 机器id所占位数
sequenceBits = uint(12) // 序列所占的位数
timestampMax = int64(-1 ^ (-1 << timestampBits)) // 时间戳最大值
dataCenterIdMax = int64(-1 ^ (-1 << dataCenterIdBits)) // 支持的最大数据中心id数量
workerIdMax = int64(-1 ^ (-1 << workerIdBits)) // 支持的最大机器id数量
sequenceMask = int64(-1 ^ (-1 << sequenceBits)) // 支持的最大序列id数量
workerIdShift = sequenceBits // 机器id左移位数
dataCenterIdShift = sequenceBits + workerIdBits // 数据中心id左移位数
timestampShift = sequenceBits + workerIdBits + dataCenterIdBits // 时间戳左移位数
)

// 获取雪花ID
func (s *Snowflake) GetNewSnowflakeId() int64 {
s.Lock()
now := time.Now().UnixNano() / 1000000 // 转毫秒
// 判断上次使用的时间戳和当前的是否一致,如果一致则把序列号+1,如果一致则序列号使用0
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()
fmt.Errorf("epoch must be between 0 and %d", timestampMax-1)
return 0
}
// 把本次的时间戳(毫秒)给回结构体
b := fmt.Sprintf("%b", t)
fmt.Print("len=", len(b), "\n")
fmt.Print("timestampShift=", timestampShift, "\n")

s.timestamp = now
r := int64((t)<<timestampShift | (s.DataCenterId << dataCenterIdShift) | (s.WorkerId << workerIdShift) | (s.sequence))
s.Unlock()
return r
}

// 根据雪花ID获取数据中心ID
func (s *Snowflake) GetDataCenterId(snowflakeId int64) int {
// 获取雪花ID的二进制
b := fmt.Sprintf("%b", snowflakeId)
// 设置数据中心的二进制值在雪花ID的二进制数据内的位置
start := len(b) - int(sequenceBits) - int(workerIdBits) - int(dataCenterIdBits)
end := start + int(dataCenterIdBits)
// 从雪花ID的二进制中截取出数据中心的数据
subStr := b[start:end]
// 把数据中心的二进制值前面无效的0去掉,二进制都是以1开头的
binaryData := subStr[strings.IndexAny(subStr, "1"):]
binaryDataInt, _ := strconv.Atoi(binaryData)
return BinaryToDecimal(binaryDataInt)
}

// 根据雪花ID获取工作机器ID
func (s *Snowflake) GetWorkerId(snowflakeId int64) int {
// 获取雪花ID的二进制
b := fmt.Sprintf("%b", snowflakeId)
// 设置工作机器ID的二进制值在雪花ID的二进制数据内的位置
start := len(b) - int(sequenceBits) - int(workerIdBits)
end := start + int(workerIdBits)
// 从雪花ID的二进制中截取出工作机器的数据
subStr := b[start:end]
// 把工作机器的二进制值前面无效的0去掉,二进制都是以1开头的
binaryData := subStr[strings.IndexAny(subStr, "1"):]
binaryDataInt, _ := strconv.Atoi(binaryData)
return BinaryToDecimal(binaryDataInt)
}

func (s *Snowflake) GetSnowflakeIdBinaryLen(snowflakeId int64) int {
b := fmt.Sprintf("%b", snowflakeId)
return len(b)
}


// 二进制转换成十进制 /**
func BinaryToDecimal(num int) int {
var remainder int
index := 0
decimalNum := 0
for num != 0 {
remainder = num % 10
num = num / 10
decimalNum = decimalNum + remainder*int(math.Pow(2, float64(index)))
index++
}
return decimalNum
}




复制