18910140161

实现golang全球id snowflake算法

顺晟科技

2021-08-28 09:40:59

162

应用程序通常需要全局ID作为数据库主键。在一个节点上很容易全局。在多个节点上呢?

我有两个想法。

1使用散列函数,如sha256,由时间戳、MAC地址、CPU负载、随机数等组成。id足够长,引入了多种不确定性,冲突概率极低,可以被认为是全球性。例如,uuid是这样的。但是,uuid是字符串格式,DB至少占用两倍的空间,DB的索引需要存储和比较,因此存储空间和查询时间都低于整形。这一点在DB的数据条带数越多越明显。

使用2分割方法。每个节点确保其生成的所有id在内部是的。每个节点都有指定的非重复节点编号,并插入到id中,使所有节点的id都是全局的。

使用3 DB中包含的默认密钥性来确保id是的。但是,db的自我增长id必须等到事务提交后,ID才会被认为有效。一些双向参考数据插入后需要重新更新,很麻烦。

第二种方法是Twitter等Snowflake算法,它为每个系统分配的ID,然后通过时间戳ID自身的增加实现全局ID。该方法的优点是,ID生成算法完全没有状态系统,没有网络调用,高效可靠。缺点是,如果id重复,可能会发生ID冲突。

Snowflake算法使用41bit毫秒时间戳、10bit系统id(最多支持1024台ID服务器)和12bit序列号,理论上最多支持1024台系统,每秒生成4096000个序列号。409万个id每秒,在任何交易平台上现在都足够了。

Twitter的id配置(从更高位置到更低方向):

名,没必要。固定值为0

41位,毫秒时间戳

5位,数据中心ID(用于数据中心编码)

5位,工作器id(用于工作器进程编码)

12位,序列号。用于在同一毫秒内生成id的序列(自我递增ID)

以下是用golang实现的uuid方法(uuid类型为整形)。

Package main

Import(

“Fmt”

Id工作器“github.com/gitstliu/go-id-工作器”

)。

Func main() {

curr woker :=id worker . id worker { }

CurrWoker。InitIdWorker(1000,1)

Newid,err 3360=curr woker . nextid()

If err==nil {

Fmt。Println(newID)

}

}

下载库

gogetgithub.com/gitstliu/go-id-worker

以下是vscode的调试结果

Api服务器侦听AT : 127 . 0 . 0 . 133604442

4917572028174794752

Process exiting with code: 0

使用Mysql中包含的自身附加id生成全局id

Package main

Import(

数据库/SQL

“错误”

“日志”

“时间”

“Fmt”

_ ' github.com/go-SQL-driver/MySQL '

)。

Type logger介面{

错误(错误)

}

//Logger Log介面,若已设定Logger,则使用Logger列印记录,若未设定,则使用内建程式库Log列印记录

Var Logger logger

//ErrTimeOut uid获取超时错误

var err time out=errors . new(' get uid time out ')

类型uid结构{

Db *sql。数据库//数据库连接

业务id字符串//业务id

Ch chan int64 //id缓冲池

Min,max int64 //id段最小值,更大值

}

//创建NewUid Uid。Len:缓冲池大小()

//db:数据库连接

//业务id:业务id

//len:缓冲池大小(长度在控制缓存中保留的id数时加载到数据库中)

Funcnew uid (db * sql.db,业务id字符串,lenint) (* uid,错误){

Lid :=Uid{

Db: db,

业务id :业务id,

Ch: make(chan int64,len),

}

Go lid.productId()

Return lid,nil

}

//Get获取自身id,超时时返回错误,防止阻止大量请求,服务器崩溃

Func (u *Uid) Get() (int64,error) {

Select {

case-time . after(1 * time . second)3360

Return 0,ErrTimeOut

case uid :=-u . ch 3360

Return uid,nil

}

}

//productId生产Id,当ch达到更大容量时,将被阻止,直到ch的id被消耗

Func (u * uid)产品id () {

U.reLoad()

For {

If u.min=u.max {

U.reLoad()

}

U.min

U.ch-u.min

}

}

//reLoad从数据库中获取id段,如果失败,则每秒尝试一次

Func (u * uid) reload()错误{

Var err error

For {

Err=u.getFromDB()

If err==nil {

Return nil

}

//数据库出现异常。请等一秒钟后再试一次

If Logger!=nil {

Logger。错误(err)

} else {

Log。Println(err)

}

Time .Sleep(time)。Second)

}

}

//从getFromDB数据库获取id段

Func (u * uid) getfromdb()错误{

Var(

MaxId int64

Step int64

)。

row 3360=u . db . queryrow(' select max _ id,step from uid'))。

//row=u . db . query row(' select max _ id,step from uid where business _ id=?' FOR UPDATE ',1)

If err :=row。Scan(maxId,STEP);Err!=nil{

FMT . PRINTF(‘Scan Failed,err 3360% v’,Err)。

Return err

}

_,err 3360=u . db . exec(' update uid set max _ id=?'),maxId step)

If err!=nil {

Return err

}

U.min=maxId

U.max=maxId step

Return nil

}

Const(

deviceidbusinessid=' device _ id '/设备id

)。

Var(

设备id uid * uid

)。

Func InitUID(db *sql .DB) {

Var err error

Device id uid,err=new uid (db,deviceidbusinessid,5)

If err!=nil {

Panic(err)

}

}

Func check(err error) {

If err!=nil {

Panic(err)

}

}

Const(

User _ name=“根”

PASS_WORD='123456 '

主机=“localhost”

PORT='3306 '

Database=“测试”

CHARSET='utf8 '

)。

//

Func main() {

//http init

//http API goroutine

URL :=fmt . sprintf(' % s 3360% s @ TCP(% s 3360% s)/% s?Charset=% s ',user _ name,pass _ word,host,port,database,Charset)

Db,err 3360=SQL . open(' MySQL ',URL)

If err!=nil {

Panic(err)

}

InitUID(db)

FOR I :=0;I20I {

Id,err:=设备id.get()

If err==nil {

Fmt。Println('id=',id)

} else {

Fmt。Println(err)

}

}

}

测试结果:

[root@bogon uid]# go build main.go

[root@bogon uid]#。/main

Id=1706

Id=1707

Id=1708

Id=1709

Id=1710

Id=1711

Id=1712

Id=1713

Id=1714

Id=1715

Id=1716

Id=1717

Id=1718

Id=1719

Id=1720

Id=1721

Id=1722

Id=1723

Id=1724

Id=1725

[root@bogon uid]#

增强功能:以上是单机版uid生成器。独立流量可能会有瓶颈。因此,这是实际的商业发行期。如果不符合Uid,则设置单独的http服务器go程序,其他业务节点从该节点获取uid段(step需要从5调整到1w),其他节点一次请求就返回1w uid。无论1w uid是否用完,都不再分配给其他节点

相关文章
我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航