zoukankan      html  css  js  c++  java
  • 雪花算法 Snowflake & Sonyflake

    唯一ID算法Snowflake相信大家都不墨生,他是Twitter公司提出来的算法。非常广泛的应用在各种业务系统里。也因为Snowflake的灵活性和缺点,对他的改造层出不穷,比百度的UidGenerator、美团的Leaf、索尼的Sonyflake等等。这篇帖子主要是讲一下原生的Snowflake算法、缺点及改造方案,并分析索尼的Sonyflake源码对原生Snowflake的改造,

    原生Snowflake

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

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

    原生的Snowflake算法是完全依赖于时间的,如果有时钟回拨的情况发生,会生成重复的ID,市场上的解决方案也是非常多的:

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

    个人比较推荐的是最后一个方案

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

    比如下图这样,从机器位上,均出来2位做回拨位:

    Sonyflake

    Snowflake算法是相当灵活的,我们可以根据自己的业务需要,对63 bit的的各个部分进行增减。索尼公司的Sonyflake对原生的Snowflake进行改进,重新分配了各部分的bit位:

    • 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来弥补。

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

    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简单暴力,就是直接等待 :

    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()
    }
    
  • 相关阅读:
    LeetCode 485. Max Consecutive Ones
    LeetCode 367. Valid Perfect Square
    LeetCode 375. Guess Number Higher or Lower II
    LeetCode 374. Guess Number Higher or Lower
    LeetCode Word Pattern II
    LeetCode Arranging Coins
    LeetCode 422. Valid Word Square
    Session 共享
    java NIO
    非阻塞IO
  • 原文地址:https://www.cnblogs.com/li-peng/p/12124249.html
Copyright © 2011-2022 走看看