雪花算法生成唯一ID

Best博客 · · 2999 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

能做啥?

能够在分布式场景中为我们在每毫秒里面生成4096个纯数字的有序的唯一id,但只能连续使用69年(代码在69年后就不能保证生成的是唯一id了),并且你的分布式机器小于1024台。当然这里面出现的4096,69,1024都是可以通过参数配置调大调小的。

golang版本实现

image.png

最高位是符号位,始终为0,不可用。
41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
10位的机器标识,10位的长度最多支持部署1024个节点。
12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

直接上代码

package main

import (
    "errors"
    "fmt"
    "sync"
    "time"
)
//你需要先去了解下 golang中   & |  <<  >> 几个符号产生的运算意义
const (
    workerBits  uint8 = 10   //10bit工作机器的id,如果你发现1024台机器不够那就调大次值
    numberBits  uint8 = 12  //12bit 工作序号,如果你发现1毫秒并发生成4096个唯一id不够请调大次值
    workerMax   int64 = -1 ^ (-1 << workerBits)
    numberMax   int64 = -1 ^ (-1 << numberBits)
    timeShift   uint8 = workerBits + numberBits
    workerShift uint8 = numberBits
 // 如果在程序跑了一段时间修改了epoch这个值 可能会导致生成相同的ID,
//这个值请自行设置为你系统准备上线前的精确到毫秒级别的时间戳,因为雪花时间戳保证唯一的部分最多管69年(2的41次方),
//所以此值设置为你当前时间戳能够保证你的系统是从当前时间开始往后推69年
    startTime   int64 = 1525705533000
)

type Worker struct {
    mu        sync.Mutex
    timestamp int64
    workerId  int64
    number    int64
}

func NewWorker(workerId int64) (*Worker, error) {
    if workerId < 0 || workerId > workerMax {
        return nil, errors.New("Worker ID excess of quantity")
    }
    // 生成一个新节点
    return &Worker{
        timestamp: 0,
        workerId:  workerId,
        number:    0,
    }, nil
}

func (w *Worker) GetId() int64 {
    w.mu.Lock()
    defer w.mu.Unlock()
    now := time.Now().UnixNano() / 1e6
    if w.timestamp == now {
        w.number++
        if w.number > numberMax {
            for now <= w.timestamp {
                now = time.Now().UnixNano() / 1e6
            }
        }
    } else {
        w.number = 0
        w.timestamp = now
    }
//以下表达式才是主菜
//  (now-startTime)<<timeShift   产生了 41 + (10 + 12)的效应但却并不保证唯一
//  | (w.workerId << workerShift)  保证了与其他机器不重复
//  | (w.number))  保证了自己这台机不会重复
    ID := int64((now-startTime)<<timeShift | (w.workerId << workerShift) | (w.number))
    return ID
}
func main() {
    // 生成节点实例,当你分布式的部署你的服务的时候,这个NewWorker的参数记录不同的node配置的值应该不一样
    node, err := NewWorker(1)
    if err != nil {
        panic(err)
    }
    for {
        fmt.Println(node.GetId())
        return
    }
}

总结

1.自增ID:对于数据敏感场景不宜使用,且不适合于分布式场景。
2.GUID:采用无意义字符串,数据量增大时造成访问过慢,且不宜排序。

  1. 雪花算法能够满足分布式场景生成纯数字的有序的数字,数据敏感场景也能使用,且纯数字相比字符串的GUID可小8倍+

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Best博客

查看原文:雪花算法生成唯一ID

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2999 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传