分享一个 id 自增生成器,依赖于 redis,求大佬帮忙挑挑毛病

2021-09-17 10:08:03 +08:00
 flycloud

看了雪花算法,服务需要配置标识位(数据中心 ID 、机器 ID ),多一项配置就多一份出错的风险,同时在不依赖其他基础服务的情况下也不太好解决时间回调的问题。然后自己实现了一个简化版的,依赖于 redis 。大佬们帮忙看看有啥问题没有,感谢。

func GlobalIncrId() (int64, error) {
	script := redis.NewScript(`
		local key = KEYS[1]
		local stamp = ARGV[1]
		
		local newValue = redis.call("incr", key)
		if newValue then
			if newValue > tonumber(stamp) then
				return newValue
			else
				local flag = redis.call("set", key, stamp)
				if flag then
					return stamp
				end
			end
		end
	
		return nil
	`)
	stamp := (time.Now().Unix() - 50 * 365 * 86400) << 22
	ret, err := script.Run(RedisIns, []string{sredis.KEY_GLOBAL_INCR_ID}, stamp).Int64()
	return ret, err
}

自增 id 带上 stamp 信息, 是为了防止 redis 的 key 丢失, 或者值被清除。stamp 的计算回拨了 50 年, 因为 32bit 的时间戳到 2038 年就溢出了。只要机器的系统时间回调以及 key 失效这两件事不同时发生, 就能保持自增性。

低 22 位用于 redis incr 自增,高位是时间信息,即 1s 内最多支持 4194304 个 id,超出了也没关系,只是会提前占用高位的时间。

用 22 位来自增是因为某些业务可能会将 id 用在 zadd 中,zadd 的 score 范围是:-(2^53) 至 +(2^53), 即时间信息可以占用 31 位, 保证 score 不会溢出。

5749 次点击
所在节点    程序员
33 条回复
GM
2021-09-17 10:18:40 +08:00
看到那么多发明 id 生成器的,我来分享一个我喜欢用的吧:

select uuid_short() as id;


优点:
纯数字单调递增 id
性能足够好,每秒几万 ID 毫无压力
分布式,多台服务器生成的 id 不会重复
依赖少,只要你用 MySql 就够了。
不怕 id 生成系统失效,只要数据库还活着就能用。如果数据库都挂了,id 生成器活着也没用。
zhaokun
2021-09-17 10:21:27 +08:00
依赖的服务要稳住
雪花可以拿机器 ip 作为机器 id,不需要配置
flycloud
2021-09-17 10:29:30 +08:00
@GM 我们业务没有用 mysql,🤣
flycloud
2021-09-17 10:29:43 +08:00
@zhaokun 有道理
Ariver
2021-09-17 10:39:35 +08:00
redis 集群的话考虑了吗
flycloud
2021-09-17 10:41:26 +08:00
@Ariver 集群或者单实例都支持呀,反正只用到了一个 key 。主要是可以规避时间回调问题。
lysS
2021-09-17 10:45:24 +08:00
@flycloud 这跟 mysql 有啥关系?
不考虑持久化,直接 uint64 自增;根本不会出现碰撞的可能
flycloud
2021-09-17 10:51:29 +08:00
@lysS 没懂你的意思。

我们业务目前没有使用 mysql,不想因为一个 id 生成新增加一个组件。

“不考虑持久化,直接 uint64 自增” 这个分布式环境你咋个自增
bthulu
2021-09-17 12:07:00 +08:00
@zhaokun ip 重复了咋办,我司机器是分布式部署的,直接部署在门店里,门店用的都是家用路由器,ip 都是 192.168.x.xxx ,很容易重复的
enan01
2021-09-17 12:27:53 +08:00
redis 如果使用主从,从节点同步延迟,也会导致 ID 重复吧
XiLingHost
2021-09-17 12:32:34 +08:00
@bthulu 那就用 mac
skies457
2021-09-17 12:59:53 +08:00
return ++i;
draymonder
2021-09-17 13:16:20 +08:00
1. 用脚本,会拖慢 redis 性能,主从之间复制也可能会出问题
2. 另外,请求 redis 失败了呢,多一个依赖,就会降低整体系统稳定性
billly
2021-09-17 13:44:05 +08:00
redis 出问题的几率肯定比我加两个配置出问题的几率大
Kinnice
2021-09-17 16:26:40 +08:00
@bthulu ip+mac+hostname+deviceid
flycloud
2021-09-17 17:52:20 +08:00
@draymonder #13

第一个问题确实存在。
因为本来就会大量用到 redis,所以并没有增加依赖。
flycloud
2021-09-17 17:55:50 +08:00
@enan01 #10 没有这个问题,因为只会在主节点写 redis
enan01
2021-09-17 20:14:12 +08:00
@flycloud 主节点挂掉,切换到从节点,从节点异步复制,没办法保证数据强一致,如果从节点 key 存在,但是 value 没有完成复制,从节点继续 incr,会存在重复的可能
workingonescape
2021-09-17 20:34:43 +08:00
@GM 你这个有链接么,想学习一下
GM
2021-09-17 20:43:53 +08:00
@workingonescape

没什么要学的,就是 mysql 自带的,直接 select uuid_short() 就 完事了。

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

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

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

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

© 2021 V2EX