10分钟搞定!Golang分布式ID集合

10分钟搞定!Golang分布式ID集合
文章图片
【编者按】本文是基于最近对Golang分布式ID的相关讨论 , 希望本文内容可以对相关技术感兴趣的开发者提供一点经验和帮助 。
作者|陈冬 , 腾讯后台开发工程师出品|腾讯云开发者本地ID生成器
uuiduuid有两种包:
github.com/google/uuid , 仅支持V1和V4版本 。
github.com/gofrs/uuid , 支持全部五个版本 。
下面简单说下五种版本的区别:
Version1 , 基于mac地址、时间戳 。
Version2 , basedontimestamp , MACaddressandPOSIXUID/GID(DCE1.1)
Version3 , Hash获取入参并对结果进行MD5 。
Version4 , 纯随机数 。
Version5 , basedonSHA-1hashingofanamedvalue 。
特点5个版本可供选择 。
定长36字节 , 偏长 。
无序 。
packagemianimport("github.com/gofrs/uuid""fmt")funcmain(){//Version1:时间+Mac地址id,err:=uuid.NewV1()iferr!=nil{fmt.Printf("uuidNewUUIDerr:%+v",err)}//id:f0629b9a-0cee-11ed-8d44-784f435f60a4length:36fmt.Println("id:",id.String(),"length:",len(id.String()))//Version4:是纯随机数,error会在内部报panicid,err=uuid.NewV4()iferr!=nil{fmt.Printf("uuidNewUUIDerr:%+v",err)}//id:3b4d1268-9150-447c-a0b7-bbf8c271f6a7length:36fmt.Println("id:",id.String(),"length:",len(id.String()))}shortuuid初始值基于uuidVersion4;第二步根据alphabet变量长度(定长57)计算id长度(定长22);第三步依次用DivMod(欧几里得除法和模)返回值与alphabet做映射 , 合并生成id 。
特点
基于uuid , 但比uuid的长度短 , 定长22字节 。
packagemianimport("github.com/lithammer/shortuuid/v4""fmt")funcmain(){id:=shortuuid.New()//id:iDeUtXY5JymyMSGXqsqLYXlength:22fmt.Println("id:",id,"length:",len(id))//V22s2vag9bQEZCWcyv5SzL固定不变id=shortuuid.NewWithNamespace("http://127.0.0.1.com")//id:K7pnGHAp7WLKUSducPeCXqlength:22fmt.Println("id:",id,"length:",len(id))//NewWithAlphabet函数可以用于自定义的基础字符串 , 字符串要求不重复、定长57str:="12345#$%^&*67890qwerty/;'~!@uiopasdfghjklzxcvbnm,.()_+·>id=shortuuid.NewWithAlphabet(str)//id:q7!o_+y('@;_&dyhk_in9/length:22fmt.Println("id:",id,"length:",len(id))}xidxid是由时间戳、进程id、Mac地址、随机数组成 。 有序性来源于对随机数部分的原子+1 。
10分钟搞定!Golang分布式ID集合
文章图片
特点长度短 。
有序 。
不重复 。
时间戳这个随机数原子+1操作 , 避免了时钟回拨的问题 。
下面的代码根据需求进行了魔改 。
packagemianimport("github.com/rs/xid""fmt")funcmain(){//hostname+pid+atomic.AddUint32id:=xid.New()containerName:="test"//由于xid默认使用可重复ip地址填充456位 。 //实际场景中 , 服务都是部署在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:cbgjhf89htlrr1955d5glength:12fmt.Println("id:",id,"length:",len(id))}ksuid
由随机数和时间戳组成 。 时间戳占前4字节 , 后面均为随机数:
packagemiaimport("github.com/segmentio/ksuid""fmt")funcmain(){id:=ksuid.New()//id:2CWvPg766SUvezbiiV9nzrTZsgflength:20fmt.Println("id:",id,"length:",len(id))id1:=ksuid.New()id2:=ksuid.New()//id1:2CTqTLRxCh48y7oUQzQHrgONT2kid2:2CTqTHf07C09CXyRMHdGKXnY5HPfmt.Println(id1,id2)//支持ID对比 , 这个功能比较鸡肋了,目前没想到有用的地方compareResult:=ksuid.Compare(id1,id2)fmt.Println(compareResult)//1//判断顺序性isSorted:=ksuid.IsSorted([]ksuid.KSUID{id2,id1})fmt.Println(isSorted)//true降序}ulid随机数和时间戳组成packagemianimport("github.com/oklog/ulid""fmt")funcmain(){t:=time.Now().UTC()entropy:=rand.New(rand.NewSource(t.UnixNano()))id:=ulid.MustNew(ulid.Timestamp(t),entropy)//id:01G902ZSM96WV5D5DC5WFHF8WYlength:26fmt.Println("id:",id.String(),"length:",len(id.String()))}snowflake