10分钟搞定!golang分布式id集合

腾讯云开发者 腾讯云开发者     2022-12-07     370

关键词:

导语 | 本文是基于最近对Golang分布式ID的相关讨论,希望本文内容可以对相关技术感兴趣的开发者提供一点经验和帮助。

一、本地ID生成器

(一)uuid

uuid有两种包:

  • github.com/google/uuid ,仅支持V1和V4版本。

  • github.com/gofrs/uuid ,支持全部五个版本。

下面简单说下五种版本的区别:

  • Version 1,基于mac地址、时间戳。

  • Version 2,based on timestamp,MAC address and POSIX UID/GID (DCE 1.1)

  • Version 3,Hash获取入参并对结果进行MD5。

  • Version 4,纯随机数。

  • Version 5,based on SHA-1 hashing of a named value。

特点

  • 5个版本可供选择。

  • 定长36字节,偏长。

  • 无序。

package mian


import (
    "github.com/gofrs/uuid"
    "fmt"
)


func main() 
    // Version 1:时间+Mac地址
    id, err := uuid.NewV1()
    if err != nil 
        fmt.Printf("uuid NewUUID err:%+v", err)
    
    // id: f0629b9a-0cee-11ed-8d44-784f435f60a4 length: 36
    fmt.Println("id:", id.String(), "length:", len(id.String()))


    // Version 4:是纯随机数,error会在内部报panic
    id, err = uuid.NewV4()
    if err != nil 
        fmt.Printf("uuid NewUUID err:%+v", err)
    
    // id: 3b4d1268-9150-447c-a0b7-bbf8c271f6a7 length: 36
    fmt.Println("id:", id.String(), "length:", len(id.String()))

(二)shortuuid

初始值基于uuid Version4;第二步根据alphabet变量长度(定长57)计算id长度(定长22);第三步依次用DivMod(欧几里得除法和模)返回值与alphabet做映射,合并生成id。

特点

  • 基于uuid,但比uuid的长度短,定长22字节。

package mian


import (
    "github.com/lithammer/shortuuid/v4"
    "fmt"
)


func main() 
    id := shortuuid.New()
    // id: iDeUtXY5JymyMSGXqsqLYX length: 22
    fmt.Println("id:", id, "length:", len(id))


    // V22s2vag9bQEZCWcyv5SzL 固定不变
    id = shortuuid.NewWithNamespace("http://127.0.0.1.com")
    // id: K7pnGHAp7WLKUSducPeCXq length: 22
    fmt.Println("id:", id, "length:", len(id))
    
    // NewWithAlphabet函数可以用于自定义的基础字符串,字符串要求不重复、定长57
    str := "12345#$%^&*67890qwerty/;'~!@uiopasdfghjklzxcvbnm,.()_+·><"
    id = shortuuid.NewWithAlphabet(str)
    // id: q7!o_+y('@;_&dyhk_in9/ length: 22
    fmt.Println("id:", id, "length:", len(id))

(三)xid

xid是由时间戳、进程id、Mac地址、随机数组成。有序性来源于对随机数部分的原子+1。

特点

  • 长度短。

  • 有序。

  • 不重复。

  • 时间戳这个随机数原子+1操作,避免了时钟回拨的问题。

下面的代码根据需求进行了魔改。

package mian


import (
    "github.com/rs/xid"
    "fmt"
)


func main() 
    // hostname+pid+atomic.AddUint32
    id := xid.New()
    containerName := "test"
    // 由于xid默认使用可重复ip地址填充4 5 6位。
    // 实际场景中,服务都是部署在docker中,这里把ip地址位替换成了容器名
    // 这里只取了容器名MD5的前3位,验证会重复,放弃使用
    containerNameID := make([]byte, 3)
    hw := md5.New()
    hw.Write([]byte(containerName))
    copy(containerNameID, hw.Sum(nil))
    id[4] = containerNameID[0]
    id[5] = containerNameID[1]
    id[6] = containerNameID[2]
    
    // id: cbgjhf89htlrr1955d5g length: 12
    fmt.Println("id:", id, "length:", len(id))

(四)ksuid

由随机数和时间戳组成。时间戳占前4字节,后面均为随机数:

package mian


import (
    "github.com/segmentio/ksuid"
    "fmt"
)


func main() 


    id := ksuid.New()
    // id: 2CWvPg766SUvezbiiV9nzrTZsgf length: 20
    fmt.Println("id:", id, "length:", len(id))
    
    id1 := ksuid.New()
    id2 := ksuid.New()
    // id1:2CTqTLRxCh48y7oUQzQHrgONT2k id2:2CTqTHf07C09CXyRMHdGKXnY5HP
    fmt.Println(id1, id2)


    // 支持ID对比,这个功能比较鸡肋了,目前没想到有用的地方
    compareResult := ksuid.Compare(id1, id2)
    fmt.Println(compareResult) // 1


    // 判断顺序性
    isSorted := ksuid.IsSorted([]ksuid.KSUIDid2, id1)
    fmt.Println(isSorted) // true 降序

(五)ulid

随机数和时间戳组成

package mian


import (
    "github.com/oklog/ulid"
    "fmt"
)


func main() 
    t := time.Now().UTC()
    entropy := rand.New(rand.NewSource(t.UnixNano()))
    id := ulid.MustNew(ulid.Timestamp(t), entropy)
    // id: 01G902ZSM96WV5D5DC5WFHF8WY length: 26
    fmt.Println("id:", id.String(), "length:", len(id.String()))

(六)snowflake

大名鼎鼎的雪花算法,这里不做过多介绍了。相对于UUID来说,雪花算法不会暴露MAC地址更安全、生成的ID也不会过于冗余。雪花的一部分ID序列是基于时间戳的,那么时钟回拨的问题就来了。上面提到的xid,一定程度上避时钟回拨的影响。那么什么是时钟回拨,后面会提到。

package main


import(
    "fmt"
    "github.com/bwmarrin/snowflake"
)


func main() 
  node, err := snowflake.NewNode(1)
    if err != nil 
      fmt.Println(err)
      return
    


    id := node.Generate().String()    
    // id: 1552614118060462080 length: 19
    fmt.Println("id:", id, "length:", len(id))

二、数据库自增ID

这里常规是指数据库主键自增索引。特点如下:

  • 架构简单容易实现。

  • ID有序递增,IO写入连续性好。

  • INT和BIGINT类型占用空间较小。

  • 由于有序递增,易暴露业务量。

  • 受到数据库性能限制,对高并发场景不友好。

  • bigint最大是2^64-1,但是数据库单表肯定放不了这么多,那么就涉及到分表。如果业务量真的太大了,主键的自增id涨到头了,会发生什么?报错:主键冲突。


三、Redis生成ID

通过redis的原子操作INCR和INCRBY获得id。相比数据库自增ID,redis性能更好、更加灵活。不过架构强依赖redis,redis在整个架构中会产生单点问题。在流量较大的场景下,网络耗时也可能成为瓶颈。

四、ZooKeeper唯一ID

ZooKeeper是使用了Znode结构中的Zxid实现顺序增ID。Zookeeper类似一个文件系统,每个节点都有唯一路径名(Znode),Zxid是个全局事务计数器,每个节点发生变化都会记录响应的版本(Zxid),这个版本号是全局唯一且顺序递增的。这种架构还是出现了ZooKeeper的单点问题。

五、号段模式

(一)Leaf-segment

把数据库自增主键换成了计数法。每个业务分配一个biz_tag、并记录各业务最大id(max_id)、号段跨度(step)等数据。这样每次取号只需要更新biz_tag对应的max_id,就可以拿到step个id。


优点

  • 除了拥有自增ID的优点之外,在性能上比自增ID更好

  • 扩展灵活。

  • 使用灵活、可配置性强。

  • 缓存机制,突发状况下短时间内能保证服务正常运转。

缺点

  • id是有序自增,容易暴露信息,不可用于订单。

  • 在leaf的缓存ID用完再去获取新号段的间隙,性能会有波动。

  • 强依赖DB。

(二)增强版Leaf-segment

增强版是对上面描述的缺点2进行的改进——双cache。在leaf的ID消耗到一定百分比时,常驻的后台进程会预先去号段服务获取新的号段并缓存。具体消耗百分比、及号段step根据业务消耗速度来定。 


(三)Tinyid

和增强版Leaf-segment类似,也是号段模式,提前加载号段。 


(四)Leaf-snowflake

时钟回拨

服务器上的时间突然倒退回之前的时间。可能是人为的调整时间;也可能是服务器之间的时间校对。

实现方案

用Zookeeper顺序增、全局唯一的节点版本号,替换了原有的机器地址。解决了时钟回拨的问题。前面介绍ZooKeeper的缺点,强依赖ZooKeeper、大流量下的网络瓶颈。下图的方案在Leaf-snowflake 中通过缓存一个ZooKeeper文件夹,提高可用性。运行时运行时,时差小于5ms会等待时差两倍时间,如果时差大于5ms报警并停止启动。 

 作者简介

陈冬

腾讯后台开发工程师

腾讯后台开发工程师,目前负责腾讯视频后端中间件开发工作,在消息队列和go性能优化方面有丰富经验。

 推荐阅读

C++异步:asio的coroutine实现!

Go组件:context学习笔记!

第六届 Techo TVP 开发者峰会暨腾讯云大数据峰会来啦!

低代码时代已来,还是又一轮技术炒作?

10分钟搞定!golang分布式id集合(代码片段)

【编者按】本文是基于最近对Golang分布式ID的相关讨论,希望本文内容可以对相关技术感兴趣的开发者提供一点经验和帮助。作者|陈冬,腾讯后台开发工程师   出品|腾讯云开发者本地ID生成器uuiduuid有两种包:github.... 查看详情

三分钟搞定分布式结构服务部署发布,积累总结

1.三重心智模型先给大家科普一个概念,“三重心智模型”。认知科学家斯坦诺维奇,将人的心智模式,分成了三个部分。第一层是自主心智,自主心智是我们通过进化与内隐学习获得。比如,我们看到蛇就会... 查看详情

10分钟搞定react-router

1.路由的安装:$npminstall-Sreact-router 2.引入路由文件import{Router,Route,browserHistory}from‘react-router‘;3.配置路由器  平级路由(做跳转用)constrouter=(<Routerhistory={browserHistory}><Routepath="/"component={App}/& 查看详情

10分钟搞定硬链软链(代码片段)

对于软链、硬链的内容整理,是为后续pnpm的内容做准备。示例:cd/Users/ligang/Documents/github/practice/links/sources#在测试目录下创建相应的测试文件touchtest.js&&echo"console.log('links')">test.jsinode查看文件 查看详情

10分钟搞定lync2010和quintumaf集成

接线拓扑接线方法:l 电信直线直接接在QuintumAF的FXO口上l AF采用RJ45接入网络 使用效果:l PSTN用户拨打直线的号码,会听到二次拨号音,然后再输入Lync用户的分机号码就可以直接振铃Lync用户l Lync用户直接拨打外... 查看详情

搭建多节点以太坊私链10分钟搞定(超简洁)

Johnsir亲传 不作任何商业用途首先创建三个文件夹分别为三个节点  每个文件夹准备好一个存放数据的文件夹  然后进入John1cmd进入puppeth 后面的直接回车就可以  到这步开始导出 导出后 文件夹多了四个文件主要用到的就是... 查看详情

5分钟搞定alertmanager接入短信语音等10+种通知渠道

简介: AlertManager是开源监控系统Prometheus中用于处理告警信息的服务,通过将日志服务开放告警配置为AlertManager中的一个Receiver,可以将AlertManager产生的告警消息发送到日志服务。SLS告警管理AlertManager作为Prometheus生态... 查看详情

分分钟轻松搞定ibm系列raid5搭建

分分钟轻松搞定IBM系列RAID5搭建按照以下图片步骤一步步可轻松完成IBM服务器RAID1、5、10等的搭建。此例是以RAID5为例,RAID1和10可举一反三。                   &n... 查看详情

开源项目:揭秘10分钟如何搞定开发部署全流程!

关注+星标公众号,不错过精彩内容来源| 百度飞桨想要应用AI技术进行产业智能化升级,又担心缺乏计算机、数学等理论基础?AI算法训练完成,优化部署上线又是一个趟不过去的大坑?别担心,今天就... 查看详情

十分钟搞定pandas

见原文链接:http://www.cnblogs.com/chaosimple/p/4153083.html本文是对pandas官方网站上《10Minutestopandas》的一个简单的翻译,原文在这里。这篇文章是对pandas的一个简单的介绍,详细的介绍请参考:Cookbook 。习惯上,我们会按下面格式... 查看详情

十分钟搞定pandas

转载自http://www.cnblogs.com/chaosimple/p/4153083.html本文是对pandas官方网站上《10Minutestopandas》的一个简单的翻译,原文在这里。这篇文章是对pandas的一个简单的介绍,详细的介绍请参考:Cookbook。习惯上,我们会按下面格式引入所需要... 查看详情

比alphafold2快10倍!国产开源项目加速蛋白质预测,超长序列22分钟就搞定

22分钟推理出长达4K+的超长蛋白质序列,1分15秒搞定将近2K长的序列预测。这是国产开源项目的最新战绩!最近,由Colossal-AI团队(https://github.com/hpcaitech/ColossalAI)联合百图生科的蛋白质预测模型xTrimoMultimer,正式... 查看详情

十分钟搞定css选择器

在最近的web开发中是不是就会用到一些选择器,发现很多尤其是CSS3新增的不太熟悉,在此总结一下。优先级不同级别1.在属性后面使用!important会覆盖页面内任何位置定义的元素样式。2.作为style属性写在元素内的样式3.id选择器4.... 查看详情

10分钟快速搞定pandas(代码片段)

本文是对pandas官方网站上《10Minutestopandas》的一个简单的翻译,原文在这里。这篇文章是对pandas的一个简单的介绍,详细的介绍请参考:Cookbook。习惯上,我们会按下面格式引入所需要的包:In[1]:importnumpyasnpIn[2]:importpandasaspdIn[3]:i... 查看详情

每天三分钟搞定linuxshell脚本10字符串相关的if判断

参考技术A字符串比较包括注意:这里的符号要转义,否则变成了重定向了。运行结果:注意:上面的等号左右两侧,中括号内侧一定要有空格运行结果:以上的例子,说明小写字母排在字典的后面(类似于ascii的大小)。sort可以... 查看详情

golang概述(代码片段)

...语言程序:完成某个功能的指令的集合学习方向区块链(分布式账本技术)Go服务器端/游戏软件/数据处理/处理大并发Golang分布式/云计算应用领域区块链技术,简称BT(BlockchainTechnology),分布式账本技术后端服务器应用云计算... 查看详情

最简单的r语言绘图,教你10分钟搞定入门绘图

参考技术AR语言上手还是比较简单的,我们哪怕是一个小白,但是对于R,也不应该害怕呀,让我们一起来看看R的绘图吧,小白没关系,跟着我走,包您看懂!让您对R越来越感兴趣!哈哈,数据,我们需要数据!在这里我们无需考... 查看详情

云原生•dockercadvisor+prometheus+grafana10分钟搞定docker容器监控平台(代码片段)

文章目录cAdvisor+Prometheus+Grafana10分钟搞定Docker容器监控平台cAdvisor部署Prometheus部署Grafana部署cAdvisor+Prometheus+Grafana10分钟搞定Docker容器监控平台cAdvisor(ContainerAdvisor)是Google开源的一个容器监控工具,可用于对容器资... 查看详情