SEQSVR: Go + MySQL 实现的高性能 ID 生成服务

2018-07-02 10:42:13 +08:00
 qichengzx

SEQSVR

Go + MySQL 实现的 ID 生成服务

GitHub:https://github.com/qichengzx/seqsvr 欢迎 star。

特性

依赖项

本项目使用下列优秀的项目作为必要组件。

安装

注意:需要在启动之前创建数据库并修改配置文件中数据库的配置。

go get 方式:

需保证 $GOPATH/bin 在系统 PATH 中。

go get github.com/qichengzx/seqsvr
seqsvr

单独编译:

git clone git@github.com:qichengzx/seqsvr.git
cd seqsvr
go build .
./seqsvr

Docker 方式:

Dockerfile 使用了 Docker 多阶段构建功能,需保证 Docker 版本在 17.05 及以上。详见:Use multi-stage builds

git clone git@github.com:qichengzx/seqsvr.git
cd seqsvr
docker build seqsvr:latest .
docker run -p 8000:8000 seqsvr:latest

初始化数据库

数据库名称可以自定义,修改 config.yml 即可。

然后导入以下 SQL 生成数据表。

CREATE TABLE `generator_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uuid` char(36) NOT NULL COMMENT '机器识别码',
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`),
  UNIQUE KEY `stub_UNIQUE` (`uuid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

修改配置

配置文件使用 YAML 格式。

#app
port: ':8000'

#service
step: 100

#db
mysql:
  user: 'root'
  password: ''
  host: 'tcp(localhost:3306)'
  database: 'sequence'

可修改端口号及 MySQL 的配置。

使用

curl http://localhost:8000/new

{"code":0,"msg":"ok","data":{"id":101}}

原理

本项目设计原理来自 携程技术中心 的干货 | 分布式架构系统生成全局唯一序列号的一个思路

服务初始化后第一次请求会在 MySQL 数据库中插入一条数据,以生成初始 ID。

后续的请求,都会在内存中进行自增返回,并且保证返回的 ID 不会超过设置的上限,到达上限后会再次从 MySQL 中更新数据,返回新的初始 ID。

核心 SQL
REPLACE INTO `generator_table` (uuid) VALUES ("54f5a3e2-e04c-4664-81db-d7f6a1259d01");

欢迎 star。

3115 次点击
所在节点    Go 编程语言
16 条回复
puritania
2018-07-02 10:59:52 +08:00
这个同样要单独部署吧,引入单点 mysql 和线程锁增加了复杂性,悲观情况下可能会有很多问题,我觉得这方案跟 snowflake 比还是不行
wych
2018-07-02 11:39:20 +08:00
发出来的 ID 是单调递增的么?
qichengzx
2018-07-02 11:56:04 +08:00
@puritania
感谢回复。
1.可以单独部署成一个服务,也可以整理成一个包放到已有项目中。项目目前只是以独立服务作为实现。
2.MySQL 的问题,如果请求量不是特别大应该还好。业务量大也可以通过修改步长或 MySQL 增加机器解决。
3.根据携程的介绍是 Java 版使用情况还不错,而且个人认为 Go 的协程开销应该比 Java 低很多。

这个方案与 snowflake 相比,个人觉得原理和实现都比较简单,适合比较初期的项目。

有说的不对的还请指正。
qichengzx
2018-07-02 11:58:17 +08:00
@wych
感谢回复。
是单调递增的。
glacer
2018-07-02 14:17:37 +08:00
@qichengzx 通常只有分布式系统(如分布式数据库)才需要用到全局唯一 id,如果是初期的项目,往往根本不需要用分布式系统,根本不存在你这个系统的使用场景啊。
pathbox
2018-07-02 14:53:02 +08:00
用 redis 的 inc 可以更快
qiyuey
2018-07-02 15:12:00 +08:00
@puritania snowflake 解决不了时间偏移的问题吧
qichengzx
2018-07-02 15:24:21 +08:00
@glacer
感谢回复。
没有接触过分布式的系统,至少没有参与过。
写这个的初衷是看到携程的那篇文章,觉得这种方案还挺有意思的,就找时间做了实现,至于实际的使用场景,目前我确实不清楚。
根据文章的介绍,携程是用在了账号系统中。
个人觉得比如 MongoDB,没有数字 ID 的系统中,如果要用到数字 ID,这种方案也是适合的。
qichengzx
2018-07-02 15:25:57 +08:00
@pathbox
感谢回复。

关于 Redis 的 incr,原文中也有提到。优缺点引用如下:
优点:
不依赖于数据库,灵活方便,且性能优于数据库。
数字 ID 天然排序,对分页或者需要排序的结果很有帮助。
使用 Redis 集群也可以防止单点故障的问题。

缺点:

如果系统中没有 Redis,还需要引入新的组件,增加系统复杂度。
需要编码和配置的工作量比较大,多环境运维很麻烦,
在开始时,程序实例负载到哪个 redis 实例一旦确定好,未来很难做修改。
zhouquan03
2018-07-02 19:20:19 +08:00
这样做是有很大风险的。内存进行自增的话,高可用怎么保证?服务挂掉重启怎么保证生成的 id 不重复?

还不如搭建个高可用的 redis,进行 incr 操作,redis QPS 还远远高于 mysql。
NUT
2018-07-02 19:24:56 +08:00
要说单调递增的 ID 生成,还数 TiDB 的 TSO 牛逼, 设计很轻巧,性能又不差,实现还简单。
yanaraika
2018-07-02 19:24:57 +08:00
@zhouquan03 redis 也没法保证生成 id 不重复吧
yanaraika
2018-07-02 19:30:53 +08:00
@zhouquan03 除非用基于 Raft 的 floyd,否则 rust-cluster 默认都是最终一致性。但 Raft 引起的性能下降很严重
puritania
2018-07-02 19:41:19 +08:00
@qiyuey #7 系统时间问题我觉得相比引入其他依赖增加调用链路来说出错几率更小。
qichengzx
2018-07-03 09:57:45 +08:00
@zhouquan03 服务挂掉重启后,会从数据库重新写一条记录拿到一个新的 ID 做起点。
rahuahua
2018-07-03 12:04:02 +08:00
如果是单台 mysql,肯定不能算高可用,如果要搭集群 mysql,对于项目初期过于复杂了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/467386

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX