huang_0_3 2023-03-28 10:06 采纳率: 0%
浏览 3

atomic.AddInt64 生成了重复ID

chatGPT 也无法解答我的疑惑?

这是一段生成唯一自增ID的代码,查看日志的时候,发现有极小的概率生成重复的ID。

type IdGenFun func(step int64) (start,end int64,err error)
type IdGen struct {
    start int64
    end int64
    id int64
    step   int64
    genFun IdGenFun
    lastTime time.Time
    dur time.Duration
    ch  chan struct{}
}

func NewIdGen(step int64,fun IdGenFun) *IdGen {
    g := &IdGen{
        start:  0,
        end:    0,
        id:     0,
        step:   step,
        genFun: fun,
        lastTime: time.Now(),
        dur: time.Minute*10,
        ch: make(chan struct{},1),
    }
    if step <1 {
        g.step = 1
    }
    g.ch<- struct{}{}
    return g
}

func (g *IdGen) Id() (int64,error) {
    <-g.ch
    refresh := false
    if time.Since(g.lastTime)>g.dur{
        refresh = true
    }
    if atomic.LoadInt64(&g.id)>= atomic.LoadInt64(&g.end) || refresh  {
        err := g.gen(refresh)
        if err != nil {
            return 0,err
        }
    }
    g.lastTime = time.Now()
    atomic.AddInt64(&g.id,1)   
    g.ch<- struct{}{}
    return g.id,nil  
}
func (g *IdGen) gen(refresh bool) error {
    if atomic.LoadInt64(&g.id) < atomic.LoadInt64(&g.end) && !refresh {
        return nil
    }
    logger.Infof("from start:%d,end:%d,refresh:%v \n",g.start,g.end,refresh)
    start,end,err := g.genFun(g.step) //从redis 中获取新的一段ID范围。
    if err != nil {
        return err
    }
    g.start = start
    g.end = end
    g.id = g.start
    logger.Infof("end start:%d,end:%d,refresh:%v \n",g.start,g.end,refresh)
    return  nil
}

//实例化后,高并发情况下调用 Id() 会有一定几率生成重复ID (在同一个范围内生成的重复ID),

chatGPT 的回答:
GPT: 3-27 18:26:14
这是一个用于生成唯一 ID 的代码,采用了加锁和信道的方式来实现并发安全。

NewIdGen 函数中,通过初始化信道 ch 的大小为 1 来保证只有一个协程可以访问并执行其它方法。这种方式可以视为互斥锁的一种简单实现。

Id 方法中,首先通过 <-g.ch 操作从信道中获取一个令牌,以控制同时只有一个协程能调用 Id 方法。然后再检查当前缓冲池中的 ID 是否已经用完,如果用完了,则需要重新生成一批 ID。重新生成时会调用 gen 方法,并在其中通过加锁的方式保证同步安全。

当加锁后成功生成新的 ID 区间后,还需要将当前 ID 的计数器置为 start 的值,然后再在 Id 方法中增加 1 并返回。为了防止程序长时间运行导致生成的 ID 范围过大,gen 方法中还增加了定期更新 ID 区间的功能,定期时间可以通过 g.dur 自定义。

总的来说,这个代码实现了 ID 的并发生成和唯一性,哪怕多个协程同时调用 Id 方法也不会生成相同的 ID。同时也考虑了性能问题和 ID 长期稳定生成的问题。

sam: 3-27 18:27:09
但这个还是没有办法保证唯一,现实情况是确实产生了不唯一的情况

GPT: 3-27 18:27:30
在多个协程并发生成唯一 ID 时,即使使用了锁和信道来确保每个 ID 只会被生成一次,但其唯一性还是有可能受到多个因素的影响:

  1. 系统时间戳精度不够,两个协程在同一时间内生成的时间戳相同,导致生成的 ID 相同;
  2. 生成的 ID 采用的算法和分配的位数不一致,批量生成的 ID 有重复部分,而出现 ID 重复的概率会随着生成的 ID 数量增加而增加;
  3. 协程读取变量的值和修改变量的值之间存在很小的时间间隔,这种情况下多个协程可能读取到相同的初始值,导致重复生成相同的 ID;

为解决这些问题,可以通过以下方式来增强 ID 的唯一性:

  1. 在生成 ID 时增加随机数或随机字符串作为 ID 的后缀,增加 ID 的“唯一度”;
  2. 对生成的 ID 进行去重和排序的操作,确保每个 ID 只出现一次,同时避免生成的 ID 过长;
  3. 使用分布式 ID 生成算法(例如:Snowflake 算法)来实现 ID 的唯一性,同时具备良好的性能和可扩展性。

针对你的现状问题,可以通过增加一个去重和排序的操作来避免生成相同的 ID,或者考虑使用分布式 ID 生成算法来实现ID的唯一性。同时建议加强日志输出,记录每次生成的 ID 区间以及 ID 的具体值,以方便问题排查。

  • 写回答

1条回答 默认 最新

  • CSDN-Ada助手 CSDN-AI 官方账号 2023-03-28 19:16
    关注
    不知道你这个问题是否已经解决, 如果还没有解决的话:

    如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^
    评论

报告相同问题?

问题事件

  • 创建了问题 3月28日

悬赏问题

  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)
  • ¥15 AIC3204的示例代码有吗,想用AIC3204测量血氧,找不到相关的代码。
  • ¥20 CST怎么把天线放在座椅环境中并仿真