雪花算法 Snowflake & Sonyflake

惟一ID算法Snowflake相信你们都不墨生,他是Twitter公司提出来的算法。很是普遍的应用在各类业务系统里。也由于Snowflake的灵活性和缺点,对他的改造层出不穷,比百度的UidGenerator、美团的Leaf、索尼的Sonyflake等等。这篇帖子主要是讲一下原生的Snowflake算法、缺点及改造方案,并分析索尼的Sonyflake源码对原生Snowflake的改造,git

原生Snowflake

原生Snowflake算法使用一个64 bit的整型数据,根据当前的时间来生成ID。 原生Snowflake结构以下:github

  • 由于最高位是标识位,为1表示为负数,因此最高位不使用。
  • 41bit 保存时间戳,精确到毫秒。也就是说最大可以使用的年限是69年。
  • 10bit 的机器位,能部属在1024台机器节点来生成ID。
  • 12bit 的序列号,一毫秒最大生成惟一ID的数量为4096个。

原生的Snowflake算法是彻底依赖于时间的,若是有时钟回拨的状况发生,会生成重复的ID,市场上的解决方案也是很是多的:算法

  • 最简单的方案,就是关闭生成惟一ID机器的时间同步。
  • 使用阿里云的的时间服务器进行同步,2017年1月1日的闰秒调整,阿里云服务器NTP系统24小时“消化”闰秒,完美解决了问题。
  • 若是发现有时钟回拨,时间很短好比5毫秒,就等待,而后再生成。或者就直接报错,交给业务层去处理。
  • 能够找2bit位做为时钟回拨位,发现有时钟回拨就将回拨位加1,达到最大位后再从0开始进行循环。

我的比较推荐的是最后一个方案服务器

找2bit位做为时钟回拨位,发现有时钟回拨就将回拨位加1,达到最大位后再从0开始进行循环。ui

好比下图这样,从机器位上,均出来2位作回拨位:阿里云

Sonyflake

Snowflake算法是至关灵活的,咱们能够根据本身的业务须要,对63 bit的的各个部分进行增减。索尼公司的Sonyflake对原生的Snowflake进行改进,从新分配了各部分的bit位:spa

  • 39bit 来保存时间戳,与原生的Snowflake不一样的地方是,Sonyflake是以10毫秒为单位来保存时间的。这样的话,可使用的年限为 174年Snowflake长太多了。
const sonyflakeTimeUnit = 1e7 // nsec, i.e. 10 msec
func toSonyflakeTime(t time.Time) int64 {
	return t.UTC().UnixNano() / sonyflakeTimeUnit
}
func currentElapsedTime(startTime int64) int64 {
	return toSonyflakeTime(time.Now()) - startTime
}
  • 8bit 作为序列号,每10毫最大生成256个,1秒最多生成25600个,比原生的Snowflake少好多,若是感受不够用,目前的解决方案是跑多个实例生成同一业务的ID来弥补。code

  • 16bit 作为机器号,默认的是当前机器的私有IP的最后两位blog

sf.machineID, err = lower16BitPrivateIP()
func lower16BitPrivateIP() (uint16, error) {
	ip, err := privateIPv4()
	if err != nil {
		return 0, err
	}
	return uint16(ip[2])<<8 + uint16(ip[3]), nil
}

对于时间回拨的问题Sonyflake简单暴力,就是直接等待 :ip

func (sf *Sonyflake) NextID() (uint64, error) {
	const maskSequence = uint16(1<<BitLenSequence - 1)
	sf.mutex.Lock()
	defer sf.mutex.Unlock()
	current := currentElapsedTime(sf.startTime)
	if sf.elapsedTime < current {
		sf.elapsedTime = current
		sf.sequence = 0
	} else { // sf.elapsedTime >= current
		sf.sequence = (sf.sequence + 1) & maskSequence
		if sf.sequence == 0 {
			sf.elapsedTime++
			overtime := sf.elapsedTime - current
			time.Sleep(sleepTime((overtime)))
		}
	}	
	return sf.toID()
}
相关文章
相关标签/搜索