Go分布式集群中生成全局唯一ID

本文探讨了在Go分布式集群中生成全局唯一ID的三种方法:使用UUID、数据库ID和Twitter的Snowflake算法。UUID提供了一种简单但长度较长的唯一标识,而数据库ID则能保证有序性但依赖数据库。Snowflake算法在不依赖数据库的情况下,能生成高性能、递增的ID,但需要关注系统时钟一致性。

使用UUID

UUID是通用唯一识别码(Universally Unique Identifier)的缩写,其目的是让分布式系统中的所有元素,都能有唯一的辨识信息。UUID是由一组32位数的16进制数字构成,所以UUID理论上总数为16^32=2^128
UUID有五个版本:

  • 版本1:基于时间戳和mac地址
  • 版本2:基于时间戳,mac地址和POSIX UID/GID
  • 版本3:基于MD5哈希算法
  • 版本4:基于随机数
  • 版本5:基于SHA-1哈希算法

格式为:xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每个x是一个十六进制数字。例如:8ad85109-fa53-44f4-8719-e49821c718fc

优缺点

优点是代码简单,性能比较好,缺点是:uuid没有排序,无法保证按序递增;其次uuid比较长,存储数据库占用空间比较大,不利于检索和排序。

go的依赖包

package main

import (
    "fmt"
    "github.com/satori/go.uuid"
)

func main() {
    u1 := uuid.Must(uuid.NewV4()).String()
    fmt.Printf("UUIDv4: ", u1)
    // NewV4用来返回一个随机生成的UUID,以及生成过程中遇到的错误
    // Must将返回结果为(UUID, error)类型的值进行包裹,如果error非nil,程序会panic。在生产环境下,不建议使用。
    // String会将UUID类型转化成string类型
    u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
    if err != nil {
        fmt.Println("failed to parse uuid: ", err)
        return
    }
    fmt.Println("successfully parsed:", u2)
    // FromString会解析字符串成uuid
}

// func Must(u UUID, err error) {
//      if err != nil {
//         panic(err)
//      }
//      return u
// }
}

使用数据库id

在分布式系统中,可以用DB proxy请求不同的分库,每个分库设置不同的初始值,步长和分库数量相等。例如:有三个数据库,db1、db2和db3。db1起始值为1,db2起始值为2,db3起始值为3。步长为数据库个数3,那么到时候db1生成的id是1,4,7,db2生成的id是2,4,8,db3生成的为3,6,9等。

优缺点

优点是:实现简单;id有序,对排序有利。缺点是:比较依赖数据库,如果数据库挂掉,服务将变得不可用。

使用twitter的snowflake算法

snowflake生成的id一共分四部分:

  • 第一部分:占用1bit,值始终为0,没有实际作用
  • 第二部分:时间戳,占用41bit,精确到毫秒。
  • 第三部分:工作机器id,占用10bit。最多容纳1024个节点。
  • 第四部分:占用12bit,这个值在同一毫秒,同一节点上从0开始增加。

因此,snowflake算法在同一毫秒内最多产生的id数量:1024*4096=4194304

优缺点

优点是:生成id不依赖db,完全在内存生成,高性能、高可用;其次id递增,后续插入索引数性能比较好。缺点是:依赖于系统时钟的一致性,如果某台机器的系统时钟有问题,可能造成id冲突。

依赖包

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()
    fmt.Println("id is:", id)
    // NewNode创建并返回一个新的snowflake节点,正如前面文章谈到,它是有范围的。在0~1023。
    // func NewNode(node int64) (*Node, error) {
    //      mu.Lock()
    //      ...
    //      if n.node < 0 || n.node > n.nodeMax {
    //          return nil, errors.New("Node number must be between 0 and " + strconv.FormatInt(n.nodeMax, 10))
    //      }
    // }

    // Generate会生成并返回一个唯一的snowflake ID,为了保证其唯一性,你需要保证你的系统的时间要精确
    // 生成的ID上有多个类型转化的接口,例如:String(),Int64()等
}

// id is: 1164423864051044352

参考文章

  1. 漫画:什么是SnowFlake算法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值