
idgo 是一个利用 MySQL 批量生成 ID 的 ID 生成器, 主要有以下特点:
GET和SET获取和设置 key 。不必开发专门的获取 ID 的 SDK ,直接使用 Reids 的 SDK 就可。业界已经有利于 MySQL 生成 ID 的方案,都是通过:
REPLACE INTO Tickets64 (stub) VALUES ('a'); SELECT LAST_INSERT_ID(); 这种方式生成 ID 的弊端就是每生成一个 ID 都需要查询一下 MySQL,当 ID 生成过快时会对 MySQL 造成很大的压力。这正式我开发这个项目的原因。服务端兼容 Redis 协议是为了避免单独开发和 idgo 通信的 SDK 。
idgo 和前端应用是才有 redis 协议通信的,然后每个id_key是存储在 MySQL 数据库中,每个 key 会在 MySQL 中生成一张表,表中只有一条记录。这样做的目的是保证当 idgo 由于意外崩溃后,id_key对应的值不会丢失,这样就避免产生了 id 回绕的风险。 
idgo 目前只支持四个 redis 命令:
1 . SET key value,通过这个操作设置 id 生成器的初始值。 例如: SET abc 123 2. GET key,通过该命令获取 id 。 3. EXISTS key,查看一个 key 是否存在。 4. DEL key,删除一个 key 。 1. 安装 Go 语言环境( Go 版本 1.3 以上),具体步骤请 Google 。 2. 安装 godep 工具, go get github.com/tools/godep 。 2. git clone https://github.com/flike/idgo src/github.com/flike/idgo 3. cd src/github.com/flike/idgo 4. source ./dev.sh 5. make 6. 设置配置文件 7. 运行 idgo 。./bin/idgo -cOnfig=etc/idgo.toml 设置配置文件(etc/idgo.toml):
#idgo 的 IP 和 port addr="127.0.0.1:6389" #log_path: /Users/flike/src #日志级别 log_level="debug" [storage_db] mysql_host="127.0.0.1" mysql_port=3306 db_name="idgo_test" user="root" password="" max_idle_cOnns=64 操作演示:
#启动 idgo idgo git:(master) ./bin/idgo -cOnfig=etc/idgo.toml 2016/04/07 11:51:20 - INFO - server.go:[62] - [server] "NewServer" "Server running" "netProto=tcp|address=127.0.0.1:6389" req_id=0 2016/04/07 11:51:20 - INFO - main.go:[80] - [main] "main" "Idgo start!" "" req_id=0 #启动一个客户端连接 idgo ~ redis-cli -p 6389 redis 127.0.0.1:6389> get abc (integer) 0 redis 127.0.0.1:6389> set abc 100 OK redis 127.0.0.1:6389> get abc (integer) 101 redis 127.0.0.1:6389> get abc (integer) 102 redis 127.0.0.1:6389> get abc (integer) 103 redis 127.0.0.1:6389> get abc (integer) 104 redis 127.0.0.1:6389> 压测环境
|类别|名称| |---|---| |OS |CentOS release 6.4| |CPU |Common KVM CPU @ 2.13GHz| |RAM |2GB| |DISK |50GB| |Mysql |5.1.73|
本地 mac 连接远程该 MySQL 实例压测 ID 生成服务。 每秒中可以生成 20 多万个 ID 。性能方面完全不会有瓶颈。
当 idgo 服务意外宕机后,可以切从库,然后将 idgo 对应的 key 加上适当的偏移量。
MIT
1 flikecn OP 一个兼容 Redis 协议的 ID 生成器 |
2 surfire91 2016-04-07 14:49:52 +08:00 这与直接用 redis ,有什么区别?或者说有什么优势? |
5 skydiver 2016-04-07 15:02:42 +08:00 如果是生成 id 依赖 mysql ,那么为什么不直接用 mysql 如果生成 id 不依赖 mysql ,只是拿 mysql 当存储,那么为啥要用 mysql |
6 noahzh 2016-04-07 15:06:46 +08:00 建议去除 mysql,至于唯一 id 生成可以采用 mysql uuid_short 函数实现 |
7 shiny PRO 还不如装个 redis 来 INCR 呢 |
8 flikecn OP @surfire91 直接用 redis ,一旦机器宕机, id 生成器对应的 key 计数器有可能没有及时保存到磁盘(在内存中),然后重启机器从 rdb 中恢复的话, id 会回绕。重新生成已经生成过的 id 。 |
9 flikecn OP @skydiver 用 MySQL 的原因就是将 ID 值存在 MySQL 中,宕机重启后,这个值不会回退。兼容 Redis 协议的目的是不想让客户端实现一个 SDK 和 idgo 通信。可以直接用 redis 的 sdk 直连 idgo 。 |
14 skydiver 2016-04-07 15:32:23 +08:00 |
15 skydiver 2016-04-07 15:34:28 +08:00 @flikecn redis 可以不用 snapshot 模式,可以用 append only file ,不用担心宕机数据丢失。 |
17 flikecn OP @skydiver 恩,但主要我们线上的机器都是 RDB 方式。而且 AOF 也有可能丢失 1s 的数据。基于这个考虑放弃了用 redis 生成 id 的方案。 |
18 flikecn OP @skydiver 通过事务的方式 update 唯一的一条记录,比如将 1000 修改为 2000 。那么然后 idgo 就可以在本地分配 1000-2000 之间的 id 了。 |
21 flikecn OP @skydiver MySQL 起来后加一个固定的偏移(比如 1000 )后可以保证不会重现重复的 ID 。因为只可能丢失固定的一段 id 值。 |
22 moro 2016-04-07 17:41:37 +08:00 idgo.go 121 行 的 defer 是不是应该放在 err 条件前面一行 rows, err := tx.Query(selectForUpdate) if err != nil { tx.Rollback() return 0, err } defer rows.Close() //line 121 |
24 detailyang 2016-04-07 23:35:14 +08:00 嘿嘿,搭车发下曾花了一个晚上改的,线上稳定跑了一年的、用 redis 改的发号器。。。原理跟你说的一样,开启 aof always ,高可用是靠 lvs 做的四层的负载均衡, 缺点嘛不是严格的单调递增。。。因为每个实例的增长序列不同, https://github.com/detailyang/id-generator |
25 jsq2627 2016-04-08 02:15:45 +08:00 via iPhone sqlserver 有 sequence 这个东西, mysql 还真没什么好替代品。赞楼主! |
26 flikecn OP @detailyang 赞赞,我看看。 |