雪花算法 Snowflake & Sonyflake

发表于:2021-3-31 09:18

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:佚名    来源:网络转载

  唯一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()
  }


本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。

《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号