snowFlake算法在生成ID时特别高效,可参考:https://segmentfault.com/a/1190000011282426

SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

  • 1位,不用。二进制中最高位为1的都是负数,但是我们生成的id一般都使用整数,所以这个最高位固定是0
  • 41位,用来记录时间戳(毫秒)。

    • 41位可以表示241−1个数字,
    • 如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:0 至 241−1,减1是因为可表示的数值范围是从0开始算的,而不是1。
    • 也就是说41位可以表示241−1个毫秒的值,转化成单位年则是(241−1)/(1000∗60∗60∗24∗365)=69年
  • 10位,用来记录工作机器id。

    • 可以部署在210=1024个节点,包括5位datacenterId5位workerId
    • 5位(bit)可以表示的最大正整数是25−1=31,即可以用0、1、2、3、....31这32个数字,来表示不同的datecenterId或workerId
  • 12位,序列号,用来记录同毫秒内产生的不同id。

    • 12位(bit)可以表示的最大正整数是212−1=4096,即可以用0、1、2、3、....4095这4096个数字,来表示同一机器同一时间截(毫秒)内产生的4096个ID序号

SnowFlake可以保证:

  • 所有生成的id按时间趋势递增
  • 整个分布式系统内不会产生重复id(因为有datacenterId和workerId来做区分)

但在在某下场影下dataCenterId、workerId并不需要占那么多的位,或是机器没那么多。自己就写了一个各个域的位可以自定义设置的。

https://github.com/liuyongshuai/goSnowFlake

/**
* @author Liu Yongshuai<liuyongshuai@hotmail.com>
* @package tofuutils
* @date 2018-01-25 19:19
*/
package tofuutils import (
"sync"
"fmt"
"time"
) /**
详见测试用例:go test -test.run TestNewIDGenerator
*/ //SnowFlake的结构体
type snowFlakeIdGenerator struct {
workerId int64 //当前的workerId
workerIdAfterShift int64 //移位后的workerId,可直接跟时间戳、序号取位或操作
lastMsTimestamp int64 //上一次用的时间戳
curSequence int64 //当前的序号 timeBitSize uint8 //时间戳占的位数,默认为41位,最大不超过60位
workerIdBitSize uint8 //workerId占的位数,默认10,最大不超过60位
sequenceBitSize uint8 //序号占的位数,默认12,最大不超过60位 lock *sync.Mutex //同步用的
isHaveInit bool //是否已经初始化了 maxWorkerId int64 //workerId的最大值,初始化时计算出来的
maxSequence int64 //最后序列号最大值,初始化时计算出来的
workerIdLeftShift uint8 //生成的workerId只取最低的几位,这里要左移,给序列号腾位,初始化时计算出来的
timestampLeftShift uint8 //生成的时间戳左移几位,给workId、序列号腾位,初始化时计算出来的
} //实例化一个ID生成器
func NewIDGenerator() *snowFlakeIdGenerator {
return &snowFlakeIdGenerator{
workerId: 0,
lastMsTimestamp: 0,
curSequence: 0,
timeBitSize: 41, //默认的时间戳占的位数
workerIdBitSize: 10, //默认的workerId占的位数
sequenceBitSize: 12, //默认的序号占的位数
maxWorkerId: 0, //最大的workerId,初始化时计算出来的
maxSequence: 0, //最大的序号值,初始化的时计算出来的
workerIdLeftShift: 0, //worker id左移位数
timestampLeftShift: 0,
lock: new(sync.Mutex),
isHaveInit: false,
}
} //设置worker id
func (sfg *snowFlakeIdGenerator) SetWorkerId(w int64) *snowFlakeIdGenerator {
sfg.lock.Lock()
defer sfg.lock.Unlock()
sfg.isHaveInit = false
sfg.workerId = w
return sfg
} //设置时间戳占的位数
func (sfg *snowFlakeIdGenerator) SetTimeBitSize(n uint8) *snowFlakeIdGenerator {
sfg.lock.Lock()
defer sfg.lock.Unlock()
sfg.isHaveInit = false
sfg.timeBitSize = n
return sfg
} //设置worker id占的位数
func (sfg *snowFlakeIdGenerator) SetWorkerIdBitSize(n uint8) *snowFlakeIdGenerator {
sfg.lock.Lock()
defer sfg.lock.Unlock()
sfg.isHaveInit = false
sfg.workerIdBitSize = n
return sfg
} //设置序号占的位数
func (sfg *snowFlakeIdGenerator) SetSequenceBitSize(n uint8) *snowFlakeIdGenerator {
sfg.lock.Lock()
defer sfg.lock.Unlock()
sfg.isHaveInit = false
sfg.sequenceBitSize = n
return sfg
} //初始化操作
func (sfg *snowFlakeIdGenerator) Init() (*snowFlakeIdGenerator, error) {
sfg.lock.Lock()
defer sfg.lock.Unlock() //如果已经初始化了
if sfg.isHaveInit {
return sfg, nil
} if sfg.sequenceBitSize < 1 || sfg.sequenceBitSize > 60 {
return nil, fmt.Errorf("Init failed:\tinvalid sequence bit size, should (1,60)")
}
if sfg.timeBitSize < 1 || sfg.timeBitSize > 60 {
return nil, fmt.Errorf("Init failed:\tinvalid time bit size, should (1,60)")
}
if sfg.workerIdBitSize < 1 || sfg.workerIdBitSize > 60 {
return nil, fmt.Errorf("Init failed:\tinvalid worker id bit size, should (1,60)")
}
if sfg.workerIdBitSize+sfg.sequenceBitSize+sfg.timeBitSize != 63 {
return nil, fmt.Errorf("Init failed:\tinvalid sum of all bit size, should eq 63")
} //确定移位数
sfg.workerIdLeftShift = sfg.sequenceBitSize
sfg.timestampLeftShift = sfg.sequenceBitSize + sfg.workerIdBitSize //确定序列号及workerId最大值
sfg.maxWorkerId = -1 ^ (-1 << sfg.workerIdBitSize)
sfg.maxSequence = -1 ^ (-1 << sfg.sequenceBitSize) //移位之后的workerId,返回结果时可直接跟时间戳、序号取或操作即可
sfg.workerIdAfterShift = sfg.workerId << sfg.workerIdLeftShift //判断当前的workerId是否合法
if sfg.workerId > sfg.maxWorkerId {
return nil, fmt.Errorf("Init failed:\tinvalid worker id, should not greater than %d", sfg.maxWorkerId)
} //初始化完毕
sfg.isHaveInit = true
sfg.lastMsTimestamp = 0
sfg.curSequence = 0
return sfg, nil
} //生成时间戳,根据bit size设置取高几位
//即,生成的时间戳先右移几位,再左移几位,就保留了最高的指定位数
func (sfg *snowFlakeIdGenerator) genTs() int64 {
rawTs := time.Now().UnixNano()
diff := 64 - sfg.timeBitSize
ret := (rawTs >> diff) << diff
return ret
} //生成下一个时间戳,如果时间戳的位数较小,且序号用完时此处等待的时间会较长
func (sfg *snowFlakeIdGenerator) genNextTs(last int64) int64 {
for {
cur := sfg.genTs()
if cur > last {
return cur
}
}
} //生成下一个ID
func (sfg *snowFlakeIdGenerator) NextId() (int64, error) {
sfg.lock.Lock()
defer sfg.lock.Unlock() //如果还没有初始化
if !sfg.isHaveInit {
return 0, fmt.Errorf("Gen NextId failed:\tplease execute Init() first")
} //先判断当前的时间戳,如果比上一次的还小,说明出问题了
curTs := sfg.genTs()
if curTs < sfg.lastMsTimestamp {
return 0, fmt.Errorf("Gen NextId failed:\tunknown error, the system clock occur some wrong")
} //如果跟上次的时间戳相同,则增加序号
if curTs == sfg.lastMsTimestamp {
sfg.curSequence = (sfg.curSequence + 1) & sfg.maxSequence
//序号又归0即用完了,重新生成时间戳
if sfg.curSequence == 0 {
curTs = sfg.genNextTs(sfg.lastMsTimestamp)
}
} else {
//如果两个的时间戳不一样,则归0序号
sfg.curSequence = 0
} sfg.lastMsTimestamp = curTs //将处理好的各个位组装成一个int64型
curTs = curTs | sfg.workerIdAfterShift | sfg.curSequence
return curTs, nil
} //解析生成的ID
func (sfg *snowFlakeIdGenerator) Parse(id int64) (int64, int64, int64, error) {
//如果还没有初始化
if !sfg.isHaveInit {
return 0, 0, 0, fmt.Errorf("Parse failed:\tplease execute Init() first")
} //先提取时间戳部分
shift := sfg.sequenceBitSize + sfg.sequenceBitSize
timestamp := (id & (-1 << shift)) >> shift //再提取workerId部分
shift = sfg.sequenceBitSize
workerId := (id & (sfg.maxWorkerId << shift)) >> shift //序号部分
sequence := id & sfg.maxSequence //解析错误
if workerId != sfg.workerId || workerId > sfg.maxWorkerId {
fmt.Printf("workerBitSize=%d\tMaxWorkerId=%d\n", sfg.workerIdBitSize, sfg.maxWorkerId)
return 0, 0, 0, fmt.Errorf("parse failed:invalid id, originWorkerId=%d\tparseWorkerId=%d\n",
sfg.workerId, workerId)
}
if sequence < 0 || sequence > sfg.maxSequence {
fmt.Printf("sequesnceBitSize=%d\tMaxSequence=%d\n", sfg.sequenceBitSize, sfg.maxSequence)
return 0, 0, 0, fmt.Errorf("parse failed:invalid id, parseSequence=%d\n", sequence)
} return timestamp, workerId, sequence, nil
}

测试代码

大约共连续生成了1亿三千多万个ID写到文件里,暂时没有发现重复的。

package tofuutils

import (
"testing"
"fmt"
"time"
"os"
) func TestNewIDGenerator(t *testing.T) {
b := "\t\t\t"
b2 := "\t\t\t\t\t"
d := "=====================================" //第一个生成器
gentor1, err := NewIDGenerator().SetWorkerId(100).Init()
if err != nil {
fmt.Println(err)
t.Error(err)
}
//第二个生成器
gentor2, err := NewIDGenerator().
SetTimeBitSize(48).
SetSequenceBitSize(10).
SetWorkerIdBitSize(5).
SetWorkerId(30).Init()
if err != nil {
fmt.Println(err)
t.Error(err)
} fmt.Printf("%s%s%s\n", d, b, d)
fmt.Printf("workerId=%d lastTimestamp=%d %s workerId=%d lastTimestamp=%d\n",
gentor1.workerId, gentor1.lastMsTimestamp, b,
gentor2.workerId, gentor2.lastMsTimestamp)
fmt.Printf("sequenceBitSize=%d timeBitSize=%d %s sequenceBitSize=%d timeBitSize=%d\n",
gentor1.sequenceBitSize, gentor1.timeBitSize, b,
gentor2.sequenceBitSize, gentor2.timeBitSize)
fmt.Printf("workerBitSize=%d sequenceBitSize=%d %s workerBitSize=%d sequenceBitSize=%d\n",
gentor1.workerIdBitSize, gentor1.sequenceBitSize, b,
gentor2.workerIdBitSize, gentor2.sequenceBitSize)
fmt.Printf("%s%s%s\n", d, b, d) var ids []int64
for i := 0; i < 100; i++ {
id1, err := gentor1.NextId()
if err != nil {
fmt.Println(err)
return
}
id2, err := gentor2.NextId()
if err != nil {
fmt.Println(err)
return
}
ids = append(ids, id2)
fmt.Printf("%d%s%d\n", id1, b2, id2)
} //解析ID
for _, id := range ids {
ts, workerId, seq, err := gentor2.Parse(id)
fmt.Printf("id=%d\ttimestamp=%d\tworkerId=%d\tsequence=%d\terr=%v\n",
id, ts, workerId, seq, err)
}
} //多线程测试
func TestSnowFlakeIdGenerator_MultiThread(t *testing.T) {
f := "./snowflake.txt"
//准备写入的文件
fp, err := os.OpenFile(f, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
if err != nil {
fmt.Println(err)
t.Error(err)
} //初始化ID生成器,采用默认参数
gentor, err := NewIDGenerator().SetWorkerId(100).Init()
if err != nil {
fmt.Println(err)
t.Error(err)
} //启动10个线程,出错就报出来
for i := 0; i < 10; i++ {
go func() {
for {
gid, err := gentor.NextId()
if err != nil {
panic(err)
}
n, err := fp.WriteString(fmt.Sprintf("%d\n", gid))
if err != nil || n <= 0 {
panic(err)
}
}
}()
}
time.Sleep(10 * time.Second)
//time.Sleep(600 * time.Second)
}

twitter的ID生成器的snowFlake算法的自造版的更多相关文章

  1. 分布式id生成器,雪花算法IdWorker

    /** * <p>名称:IdWorker.java</p> * <p>描述:分布式自增长ID</p> * <pre> * Twitter的 ...

  2. Snowflake算法 ID生成

    Snowflake算法 ID生成 http://blog.csdn.net/w200221626/article/details/52064976 使用UUID或者GUID产生的ID没有规则 Snow ...

  3. 全局唯一ID生成器

    分布式环境中,如何保证生成的id是唯一不重复的? twitter,开源出了一个snowflake算法,现在很多企业都按照该算法作为参照,实现了自己的一套id生成器. 该算法的主要思路为: 刚好64位的 ...

  4. 分布式唯一ID生成器

    在应用程序中,经常需要全局唯一的ID作为数据库主键.如何生成全局唯一ID? 首先,需要确定全局唯一ID是整型还是字符串?如果是字符串,那么现有的UUID就完全满足需求,不需要额外的工作.缺点是字符串作 ...

  5. 常用的分布式ID生成器

    为何需要分布式ID生成器 **本人博客网站 **IT小神 www.itxiaoshen.com **拿我们系统常用Mysql数据库来说,在之前的单体架构基本是单库结构,每个业务表的ID一般从1增,通过 ...

  6. 分布式全局ID生成器原理剖析及非常齐全开源方案应用示例

    为何需要分布式ID生成器 **本人博客网站 **IT小神 www.itxiaoshen.com **拿我们系统常用Mysql数据库来说,在之前的单体架构基本是单库结构,每个业务表的ID一般从1增,通过 ...

  7. DonkeyID---php扩展-64位自增ID生成器

    ##原理 参考Twitter-Snowflake 算法,扩展了其中的细节.具体组成如下图: 如图所示,64bits 咱们分成了4个部分. 毫秒级的时间戳,有42个bit.能够使用139年,从1970年 ...

  8. 基于Twitter的Snowflake算法实现分布式高效有序ID生产黑科技(无懈可击)

    参考美团文档:https://tech.meituan.com/2017/04/21/mt-leaf.html Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万 ...

  9. id生成器,分布式ID自增算法(Snowflake 算法)

    接口: /** * id生成器 */ public interface IdGenerator { String next(); } 实现类: /** * 分布式ID自增算法<br/> * ...

随机推荐

  1. HBase跨地区机房的压测小程序——从开发到打包部署(图文版)

    今天做了一个跨地区机房的压测小程序,主要的思路就是基于事先准备好的rowkey文件,利用多线程模拟并发的rowkey查询,可以实现并发数的自由控制.主要是整个流程下来,遇到了点打包的坑,所以特意记录下 ...

  2. js解析xml浏览器兼容性处理

    /****************************************************************************** 说明:xml解析类 ********** ...

  3. Vue 爬坑之路(五)—— 组件进阶

    组件(Component)是 Vue.js 最强大的功能之一,之前的文章都只是用到了基本的封装功能,这次将介绍一些更强大的扩展. 一.基本用法 在使用 vue-cli 创建的项目中,组件的创建非常方便 ...

  4. opensuse安装pycurl失败记录

    早上在opensuse安装pycurl,一直出现如下错误: pepper@VM_56_243_suse:~/code/gitosis-autotest> pip install pycurl C ...

  5. Netty对Protocol Buffer的支持(七)

    Netty对Protocol Buffer的支持(七) 一.简介 在上一篇博文中笔者已经介绍了google的Protocol Buffer的使用,那么本文笔者就开始介绍netty对Protocol B ...

  6. C#设计模式之一单例模式(Singleton Pattern)【创建型】

    一.引言     看了李建忠老师的讲的设计模式已经有一段时间了(这段时间大概有一年多了),自己还没有写过自己的.有关设计模式的文章.这次想写一些关于设计模式的文章,用自己的理解和代码来写,算是复习一遍 ...

  7. iOS学习——属性引用self.xx与_xx的区别

    在iOS开发过程中,我们用@proprety声明一个属性后,在代码中我们可以用self.xx与_xx来获取到这个属性.但是一直有一个疑惑,那就是这两个之间有什么区别呢?最初我一直觉得这两个之间没什么区 ...

  8. JS 时间转换为时间戳

    Date.prototype.format = function(fmt) { var o = { "M+" : this.getMonth()+1, //月份 "d+& ...

  9. spark 1.6 完全分布式平台搭建

    软件环境: scala-2.11.4.tgz spark-1.6.2-bin-hadoop2.6.tgz 操作步骤: 一.  安装scala 1. 解压scala (tar –zxvf  filena ...

  10. windows系统安装securtCRT

    说明:securtCRT可以ssh liunx主机,或者网络设备,如路由器,交换机,防火墙等设备,很多新手不会安装,因为正版要钱啊,对于小老百姓,还是用破解的吧 不说废话,开始搞起来. 软件下载链接: ...