优化全局 id 生成效率
This commit is contained in:
@@ -2,8 +2,6 @@
|
||||
|
||||
trade/create 性能问题,缩短事务时间,考虑其他方式实现可靠分布式事务
|
||||
|
||||
需要确认以下 ID.GenSerial 的分布式并发安全性
|
||||
|
||||
jsonb 类型转换问题,考虑一个高效的 any 到 struct 转换工具
|
||||
|
||||
端口资源池的 gc 实现
|
||||
|
||||
@@ -44,48 +44,28 @@ var (
|
||||
)
|
||||
|
||||
func (s *IdService) GenSerial() (string, error) {
|
||||
var ctx = context.Background()
|
||||
|
||||
// 构造Redis键
|
||||
now := time.Now().Unix()
|
||||
key := idSerialKey(now)
|
||||
|
||||
// 使用Redis事务确保原子操作
|
||||
var sequence int64
|
||||
err := g.Redis.Watch(ctx, func(tx *redis.Tx) error {
|
||||
// 脚本实现原子操作
|
||||
script := redis.NewScript(`
|
||||
local current = tonumber(redis.call('GET', KEYS[1])) or 0
|
||||
if current >= tonumber(ARGV[1]) then
|
||||
return redis.error_reply('sequence overflow')
|
||||
end
|
||||
|
||||
// 获取当前序列号
|
||||
currentVal, err := tx.Get(ctx, key).Int64()
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
return err
|
||||
}
|
||||
local sequence = current + 1
|
||||
redis.call('SET', KEYS[1], sequence, 'EX', ARGV[2])
|
||||
|
||||
if errors.Is(err, redis.Nil) {
|
||||
currentVal = 0
|
||||
}
|
||||
sequence = currentVal + 1
|
||||
|
||||
// 检查序列号是否溢出
|
||||
if sequence > maxSequence {
|
||||
return ErrSequenceOverflow
|
||||
}
|
||||
|
||||
// 将更新后的序列号保存回Redis,设置5秒过期时间
|
||||
pipe := tx.Pipeline()
|
||||
pipe.Set(ctx, key, sequence, redisTTL*time.Second)
|
||||
_, err = pipe.Exec(ctx)
|
||||
return err
|
||||
}, key)
|
||||
return sequence
|
||||
`)
|
||||
sequence, err := script.Run(context.Background(), g.Redis, []string{idSerialKey(now)}, maxSequence, redisTTL).Int64()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 组装最终ID
|
||||
id := uint64((now << timestampShift) | sequence)
|
||||
|
||||
idStr := strconv.FormatUint(id, 10)
|
||||
|
||||
return idStr, nil
|
||||
return strconv.FormatUint(id, 10), nil
|
||||
}
|
||||
|
||||
// ParseSerial 解析ID,返回其组成部分
|
||||
@@ -98,7 +78,7 @@ func (s *IdService) ParseSerial(id uint64) (timestamp int64, sequence int64) {
|
||||
|
||||
// idSerialKey 根据时间戳生成Redis键
|
||||
func idSerialKey(timestamp int64) string {
|
||||
return fmt.Sprintf("global:id:serial:%d", timestamp)
|
||||
return fmt.Sprintf("id:serial:%d", timestamp)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
Reference in New Issue
Block a user