Golang 1.22.2
在项目中对应数据库的唯一id,我们通常会选择使用数据库自增id,uuid,雪花算法等,每种模式的id都有各自的优缺点,这里将通过Redis来实现一种自定义分布式的id生成模式。
当然这种实现方式也有一定的缺点,那就是过于依赖redis,一旦出现redis不可用,会导致id生成失败,从而影响整个业务的不可用,所以在设计的时候需要考虑,如果redis不可用如何自动启用其他唯一id生成模式。
首先看redis实现部分:
lua// 获取第一个Key
local id_key = KEYS[1]
// 获取形参的第一个数,这里是初始化值
local initial = ARGV[1]
// 获取形参的第二数据,这里每次增长的量
local step = ARGV[2]
// 检查key当前是否存在值
local current = redis.call('get', id_key)
// 如果不存在则返回初始值,并在redis中设置初始值
if current == false then
redis.call('set', id_key, initial)
return initial
end
--redis.log(redis.LOG_NOTICE,' current:'..current..':')
// 如果存在值则在当前值上面加入每次的增量,设置到redis后并返回
local result = tonumber(current)+ tonumber(step)
--redis.log(redis.LOG_NOTICE,' result:'..result..':')
redis.call('set', id_key, result)
return tostring(result)
实现完lua脚本后,我们知道一共需要传递三个参数,有个是业务key,用来标识每个表的,第二参数是id的初始值,比如从1开始或者从1000开始,就是通过第二个参数来设置的,最后一个是每次的增长量:比如我们需要每次在原来的基础+1,那么就传递1,如果需要+10,对应的传递10即可。
脚本有了之后我们开始在golang中实现具体的功能。
gotype Config struct {
Prefix string `json:"prefix"` // 编号前缀
Initial int64 `json:"initial"` // 初始值
Step int `json:"step"` // 每次递增数量
Remark string `json:"remark"` // 备注
Key string `json:"key"` // key名称
}
这里需要定义一个接口来满足不同的id需求。
go// Sequence 序列接口
type Sequence interface {
// GetConfig 获取序列配置
GetConfig() Config
// NextCode 获取下一个序列化
NextCode() string
}
go// 执行lua脚本获取id
func execute(conf Config) string {
luaId := redis.NewScript(`
local id_key = KEYS[1]
local initial = ARGV[1]
local step = ARGV[2]
local current = redis.call('get', id_key)
if current == false then
redis.call('set', id_key, initial)
return initial
end
--redis.log(redis.LOG_NOTICE,' current:'..current..':')
local result = tonumber(current)+ tonumber(step)
--redis.log(redis.LOG_NOTICE,' result:'..result..':')
redis.call('set', id_key, result)
return tostring(result)
`)
var ctx = context.Background()
n, err := luaId.Run(ctx, cache.RedisClient, []string{conf.Key}, conf.Initial, conf.Step).Int64()
if err != nil {
log.Log.Errorf("生成序列id错误:%s", err.Error())
return ""
}
return fmt.Sprintf("%s%d", conf.Prefix, n)
}
gotype GoodsSeq struct {
}
func (u GoodsSeq) GetConfig() Config {
return Config{
Prefix: "SP",
Initial: 300,
Step: 1,
Remark: "商品配置ID生成器",
Key: "goods-id-seq",
}
}
func (u GoodsSeq) NextCode() string {
conf := u.GetConfig()
return execute(conf)
}
go// 生成商品ID
id := sequence.GoodsSeq{}.NextCode()
// 输出值:SP300
到这里,一个分布式自定义的id生成器已经实现,给出的样例中,针对redis不可用的情况并没有做处理,在生产环境中需要考虑。
本文作者:南月星河
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!