分享一个 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 不会溢出。

5764 次点击
所在节点    程序员
33 条回复
JakeZou
2021-09-17 21:59:15 +08:00
@GM 确实不错
seakingii
2021-09-17 22:15:13 +08:00
最大的问题是用 REDIS
xingzhi
2021-09-18 01:21:21 +08:00
@GM 这方法确实不错

本质上感觉跟雪花算法差不多,机器 ID+时间戳+自增序列
区别在于雪花算法用了 ZK 来管理机器 ID 了
wangritian
2021-09-18 02:51:18 +08:00
数据中心 ID 配置不麻烦吧,机器 ID 可以用 redis incr 搞定啊,生成就不要依赖网络了,也可以用 k8s-statefulset 的 hostname 最后数字作为机器 ID
Rocketer
2021-09-18 03:23:59 +08:00
uuid 的问题在于多台机器之间无法实现递增关系,雪花算法主要就是为了解决这个问题的,后生成的 id 一定大于之前生成的 id 。

如果对雪花的性能不满意,可以考虑做个 id 微服务,闲余时提前生好,存在内存里,需要时取第一个发下去就行了,这样不会重复,也保证了递增关系。关键是算法可以很简单(比如就是数字递增),从而保证这个微服务有极强的承载能力。
hanxiV2EX
2021-09-18 06:22:42 +08:00
https://github.com/hanxi/skynet-demo/commit/b4ef9dd1b8e835318da8f17bec885bb4b58d5b73

我也实现了一个雪花算法,分布式不依赖数据库。用本地定时存文件的方式解决时间回调问题。
hanxiV2EX
2021-09-18 06:34:29 +08:00
既然用 redis 了,还不如直接用 lua 实现集成在 redis 里,用 EVALSHA 获取 id
fpure
2021-09-18 08:15:08 +08:00
@GM 学到了👍
JamesChen
2021-09-18 08:36:09 +08:00
路过提一嘴,“if newValue then”和“if flag then”永远都是 True 。lua 不是 js,别写混了
GTim
2021-09-18 09:25:06 +08:00
@GM
flycloud
2021-09-18 10:10:28 +08:00
@JamesChen #29 谢谢,看了文档,确实啊。如果 redis.call 命令有错误会直接抛出异常了,如果执行成功,返回值一定是 True
egfegdfr
2021-09-18 18:30:45 +08:00
mybatis-plus 自带的 ID_WORKER 及挺好用的,不需要太多配置,直接注解就行,生产的是 有序但是不连续的 id,还可防止被人恶意爆破的问题
cp19890714
2021-12-13 22:02:09 +08:00
1. 中心化代替分布式
雪花算法三要素:1 时间戳 2 自增序列 3 全局唯一标识
“全局唯一标识” 实现了分布式能力, 而你的算法删掉了这个要素,用中心化的 redis 替代了。

2.多依赖代替单依赖
雪花算法依赖 服务器时间的同步性。
你的算法依赖 网络和 redis 。
雪花算法仅在启动的时候 workId 依赖 Redis, 而你的是时刻都依赖 redis 。

总结:删掉对服务器时间的依赖,却大大降低了高可用性。性能也肯定降了一大截。

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

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

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

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

© 2021 V2EX