基于Go实现的分布式主键系统
摘要
随着互联网的发展,微服务得到了快速的发展,在微服务架构下,分布式主键开始变得越来越重要。目前分布式主键的实现方式颇多,有基于数据库自增的、基于UUID的、基于Redis自增的、基于数据库号段的。与此同时,越来越多的公司开始普及使用雪花算法,但是在使用的过程中,发现了雪花算法的一些问题:其一、雪花算法存在时间回拨问题;其二、雪花算法存在重复问题;其三、雪花算法不能覆盖所有的使用场景。基于现有的问题,本文开发了分布式主键系统,该系统可以解决以上问题。
1 各分布式主键介绍
1.1 雪花算法
在当前,最受欢迎的分布式主键生成算法是雪花算法,该算法由64位二进制数组成,如下图所示,它通过组合不同的二进制位来生成唯一的主键。
雪花算法生成64位的唯一ID,其中包括一个固定的符号位,41位时间戳(毫秒级),10位机器标识,以及12位序列号。这保证了ID的递增性和分布式环境下的无冲突性。然而,算法的局限性在于最多支持1024个节点,且时钟回拨可能造成ID重复,这些问题在大型分布式系统中尤为突出。因此,有需求去优化现有的算法,以适应更多节点并解决时钟同步和机器标识分配问题。
1.2 基于Redis的分布式主键
Redis的命令是单线程执行的,因此可以多个服务调用递增命令而不会产生重复主键的问题,通过这种方式,程序可以确保系统在分布式环境中生成全局唯一且连续的主键。
1.3 基于MySQL的分布式主键
采用MySQL数据库的号段模式可以满足生成主键的唯一性需求,该模式将数据段分配给特定节点,其他节点无法使用,因此,号段模式能够保证数据的唯一性,实现分布式环境下的主键生成。
1.4 基于新雪花算法的分布式主键
为克服雪花算法的时间回拨和机器号重复问题,新设计的分布式主键算法依然保持64位结构,但布局调整为:最高位废弃,47位用于自增确保递增性,后面16位分为两部分各8位,用于生成随机数以增加随机性。此设计放弃了机器标识,47位自增数字通过Redis或Etcd等中间件实现,并在本地缓存,确保全局唯一且提升系统稳定性与性能。
这种设计考虑了分布式环境的因素,因为在多台机器同时部署的情况下,可以借助第三方组件构建分布式系统,本文采用了与Redis的结合方式,并利用本地缓存,极大地提高了主键的生成速率。
2 分布式主键系统
2.1 涉及技术
HTTP服务端、GRPC、Protobuf、Redis操作、MySQL操作、分布式ID算法、多租户、Etcd操作
2.2 如何快速跑起来
2.2.1 配置相关
位置 | 内容 |
---|---|
/conf/db/ddl.sql | SQL脚本 |
/etc/*.yml | 配置文件 |
2.2.2 项目入口
位置 | 内容 |
---|---|
/guid.go | 项目启动入口 |
/server/server.go | 服务入口 |
/server/gin_server.go | gin服务入口 |
/service/impl/db_serviceImpl.go | 数据库分布式主键实现 |
/service/impl/redis_serviceImpl.go | redis分布式主键实现 |
/service/impl/snow_serviceImpl.go | 雪花算法分布式主键实现 |
/service/impl/new_show_serviceimpl.go | 基于新雪花算法分布式主键实现 |
2.2.3 操作步骤
从gitlab拉下来项目
goland打开项目主目录 进入terminal
执行 go mod tidy
执行 go mod vendor
安装mysql,执行ddl
安装redis和etcd
执行go run guid.go
2.3 项目设计
2.3.1 总体设计
全局唯一键,主流的实现方式有四种:
1,基于数据库的号段模式;
2,基于redis的自增模式;
3,基于雪花算法的实现模式;
4,基于新雪花算法的实现模式;
本项目实现四种方式的全局唯一键,支持集群化部署,可扩展性高
想使用某种唯一键,只需要指定类型就行,唯一键按租户和应用维度隔离
2.3.2详细设计
-
类图
-
grpc设计
syntax = "proto3";option go_package = "gitee.com/liyouqing/guid/pb/guid;guid";message Req{string namespace = 1; // 命名空间TYPE type = 2; // key的类别enum TYPE{ // 主键类型SNOW = 0;REDIS = 1;DB = 2;NEWSHOW = 3;}
}message Res{bool flag =1 ; //成功失败标志string key = 2; //返回的全局id实体
}service PbGuid{rpc GetKey(Req) returns (Res);
}
- 释义:
使用protobuf 定义两个message 一个请求,一个返回
使用grpc定义了一个服务,服务的中有一个方法,使用上述定义的请求和返回作为入参和出参
2.3.3 rpc远程调用
go项目需要该功能时,需要拿到该proto的文件
然后,通过命令生成相关结构体和grpc调用相关的东西
然后参考
/server/server_client_test.go
2.3.4 gin服务 restapi调用
通过 gin服务 添加http查询接口
url localhost:8080/gitee.com/liyouqing/guid/get-key?namespace=nihao&type=2 请求方式 Get
请求参数 type 0:雪花算法 1:redis分布式 2:数据库号段 3:新雪花算法
返回数据
{"status":0,"msg":"success","data": "70001"
}
3 对比试验结果
算法 | 耗时(十万条) | 速度 | 重复次数 | 随机性 | 自增趋势 |
---|---|---|---|---|---|
雪花算法 | 12.47秒 | 快 | 3 | 有 | 有 |
优化的雪花算法 | 12.46秒 | 快 | 0 | 有 | 有 |
基于Redis的主键生成算法 | 21.50秒 | 慢 | 0 | 无 | 有 |
基于MySQL的主键生成算法 | 11.86秒 | 快 | 0 | 无 | 有 |
新雪花算法 | 12.22秒 | 快 | 0 | 有 | 有 |
4 总结
本人进行了大量工作,优化了雪花算法,解决了性能和可靠性问题,还实现了基于数据库和缓存的两种分布式主键生成算法。通过对比实验发现,这些算法都有各自的优势和适用领域。最后,基于这些算法开发了分布式主键系统,提升了数据写入能力和稳定性,避免了重复主键的产生,具有极高的现实价值。
5 附录
gitee项目地址
集成grpc的操作可以参考 go集成grpc
Mysql、Redis、Etcd的安装自行百度,在此不再赘述!!