V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
sunmoon1983
V2EX  ›  Go 编程语言

snowflake 算法生成的 id 也能重复?

  •  1
     
  •   sunmoon1983 · 2020-02-15 20:23:20 +08:00 · 5306 次点击
    这是一个创建于 1777 天前的主题,其中的信息可能已经有所发展或是发生改变。

    客户端代码:

    func GenerateID() (string, error) {
    	c := connect.Instance() //连接 grpc
    	cc, cancel := c.ServerClient()
    	defer cancel()
        //生成 id
    	r, err := cc.GenerateSnowflakeID(c.Ctx, &serv.GenerateSnowflakeRequest{WorkID:"1"})
    	if err != nil {
    		return "", err
    	}
    	return r.GetMessage(), err
    }
    func main() {
    	data := ""
    	for i:=0;i<=100;i++{
    		id, err := GenerateID()
    		str := gconv.String(id)
    		fmt.Println("ID(运行到第"+gconv.String(i+1)+"条):"+str+",长度:"+gconv.String(len(str)))
    		if strings.Contains(data,","+str+","){
    			fmt.Println("ID 重复(运行到第"+gconv.String(i+1)+"条):"+str)
    			break
    		}else{
    			if err != nil{
    				fmt.Println(err.Error())
    			}
    			data = data+","+str+","
    		}
    	}
    }
    

    生成结果

    sunmoondeMacBook-Pro:client_test sunmoon$ go run main.go 
    ID(运行到第 1 条):1581769199913537536,长度:19
    ID(运行到第 2 条):1581769199913537536,长度:19
    ID 重复(运行到第 2 条):1581769199913537536
    
    

    服务端代码

    // GenerateSnowflakeID 生成 ID
    func (s *Server) GenerateSnowflakeID(ctx context.Context, in *serv.GenerateSnowflakeRequest) (re *serv.GenerateReply, err error) {
    	gen, err := snowflake.New().SetWorkerID(gconv.Int64(in.GetWorkID())).Init()
    	if err != nil {
    		glog.Line(true).Println(err.Error())
    		return
    	}
    	id, err := gen.Generate()
    	if err != nil {
    		glog.Line(true).Println(err.Error())
    		return
    	}
    	//id := s.GetWuid()
    	re = &serv.GenerateReply{Message: gconv.String(id)}
    	return
    }
    
    11 条回复    2020-04-23 10:49:53 +08:00
    lqs
        1
    lqs  
       2020-02-15 20:38:06 +08:00
    每次都 Init 当然会一样了
    OllyDebug
        2
    OllyDebug  
       2020-02-15 22:20:18 +08:00 via iPhone
    代码的锅
    jingege
        3
    jingege  
       2020-02-15 22:36:15 +08:00 via Android
    我知道有个 nug,但是其实很容易发现,我决定先不告诉你
    stevenhawking
        4
    stevenhawking  
       2020-02-15 23:05:03 +08:00
    @jingege 我知道你这句话有个 nug,但是其实很容易发现,我决定先不告诉你
    swulling
        5
    swulling  
       2020-02-16 01:40:01 +08:00 via iPhone
    用法有问题,时间 workerID 序列号都相同
    sunmoon1983
        6
    sunmoon1983  
    OP
       2020-02-16 08:02:12 +08:00
    @lqs init 里面有判断,如果已经初始化了
    if sfg.isHaveInit {
    return sfg, nil
    }
    sunmoon1983
        7
    sunmoon1983  
    OP
       2020-02-16 08:02:55 +08:00
    @swulling 大老,指点一下?循环里面要怎么用?
    sunmoon1983
        8
    sunmoon1983  
    OP
       2020-02-16 08:12:00 +08:00
    感谢大家,
    @lqs 谢谢,我把 INIT 拿出来就可以了
    beiping96
        9
    beiping96  
       2020-02-17 13:19:02 +08:00
    另外还有一个点,如果生成 snowflake ID 的节点 在小于几 ms 内完成重启的话,也是会发生重复的
    yuechen323
        11
    yuechen323  
       2020-04-23 10:49:53 +08:00
    基于 SnowFlake 简单是简单, 但是需要二次开发
    在每个应用内, 多线程情况下, 且高并发是一定会出现重复的, 因此生成 id 的服务一定要做成独立的服务
    且生成 id 的进程一定是单线程, 可以用池化技术进行预分配 id 来优化速度
    如果 id 服务要做成集群, 那么 dataCenterId 一定设置成不一样的, 这就变成了有状态服务
    有状态服务如何优化呢?
    可以在启动 id 服务的时候, 从 redis 啊, zookeeper 里面原子的获取自增 id 来实现
    这样就可以无脑的部署多个点了
    Peace~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2729 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 12:25 · PVG 20:25 · LAX 04:25 · JFK 07:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.