zoukankan      html  css  js  c++  java
  • snowflake 雪花算法 分布式实现全局id生成

    snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。

    这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等。

    其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0。

    比如在snowflake中的64-bit分别表示如下图(图片来自网络)所示:

    整个结构是64位,所以我们在Java中可以使用long来进行存储。 该算法实现基本就是二进制操作,单机每秒内理论上最多可以生成1024*(2^12),也就是409.6万个ID(1024 X 4096 = 4194304)

    0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 

        1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
        41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
        10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId。10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。
       12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号。12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。
       加起来刚好64位,为一个Long型。

    优点:

      整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。

    • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
    • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
    • 可以根据自身业务特性分配bit位,非常灵活

    缺点:

    • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

    Go实现

      sonyflake

     

    下载 

    go get github.com/sony/sonyflake
    

    Demo

    package main
    
    import (
    	"fmt"
    	"github.com/sony/sonyflake"
    )
    
    var (
    	LSonyFlake *sonyflake.Sonyflake
    	machineId  uint16 // 真正的分布式环境下必须zookeeper或etcd中获取
    )
    
    func getMachineID() (uint16, error) {
    	return machineId, nil
    }
    func Init(mid uint16) (err error) {
    	machineId = mid
    	st := sonyflake.Settings{}
    	st.MachineID = getMachineID
    	LSonyFlake = sonyflake.NewSonyflake(st)
    	return
    }
    func GetID()(id uint64,err error)  {
    	if LSonyFlake == nil{
    		err = fmt.Errorf("No Init
    ")
    		return
    	}
    	return LSonyFlake.NextID()
    }
    
    func main()  {
    	Init(0)
    	id,err:=GetID()
    	if err != nil {
    		fmt.Println(err)
    	}
    	fmt.Println(id)
    }
    

      

     

     

     

     

    Songzhibin
  • 相关阅读:
    [转]进程与线程及其区别
    [转]工厂模式
    [转]Filter实现处理中文乱码,转义html标签,过滤敏感词
    [转]JAVA设计模式之单例模式
    [转]Servlet 中文乱码问题及解决方案剖析
    Servlet作业2-将表单提交的商品信息输出到页面中
    Servlet作业1-实现注册登录
    [转] ServletContext 与application的异同
    [转]servlet中的service, doGet, doPost方法的区别和联系
    [转]Servlet 3.0 新特性详解
  • 原文地址:https://www.cnblogs.com/binHome/p/12072433.html
Copyright © 2011-2022 走看看