searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

用golang实现雪花算法 snowflake

2023-08-10 03:18:55
77
0
import (
   "errors"
   "fmt"
   "time"
)

/**
 * Snowflake<br>
 * SnowFlake的结构如下(每部分用-分开):<br>
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 0000000 - 0000 - 00000000000 <br>
 * 1位标识,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
 * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
 * 得到的值),这里的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序StartSecs)。
   41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
 * 11位的数据机器位,可以部署在2048个节点,包括7位RegionId和4位GroupId<br>
 * 11位序列,毫秒内的计数,11位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生2048个ID序号<br>
 * 加起来刚好64位,为一个Long型。<br>
 * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
 */




const(
   //开始时间戳  2020-05-19
   StartSecs int64 = 1589817600000

   // 大区ID所占的位数
   RegionIdBits int64 = 7

   // 机房ID所占的位数
   GroupIdBits int64 = 4

   // 支持的最大大区id,结果是31
   MaxRegionId int64 = -1 ^ (-1 << RegionIdBits)

   // 支持的最大机房ID,结果是15
   MaxGroupId int64 = -1 ^ (-1 << GroupIdBits)

   // 序列在id中占的位数
   SequenceBits int64 = 11

   // 机房ID向左移11位
   GroupIdShift int64 = SequenceBits

   // 大区ID向左移15位(11+4)
   RegionIdShift int64 = SequenceBits + GroupIdBits

   // 时间截向左移22位(11+4+7)
   TimestampLeftShift = SequenceBits + GroupIdBits + RegionIdBits

   // 生成序列的掩码,这里为2047 (0b11111111111=0x7ff=2047)
   SequenceMask = -1 ^ (-1 << SequenceBits)
)

type SnowflakeIdWorker struct {
   RegionId int64    //大区id
   GroupId int64    //机房id
   LastTimestamp int64
   Sequence int64  // 毫秒内序列(0~2047)
}


//==============================Constructors=====================================
/**
 * 构造函数
 * @param RegionId 大区ID (0~15)
 * @param GroupId 机房序号 (0~127)
 */
func NewSnowflakeIdWorker(RegionId, GroupId int64)(idWork *SnowflakeIdWorker, err error) {
   if RegionId > MaxRegionId || RegionId < 0 {
      return nil, errors.New("err RegionId")
   }

   if GroupId > MaxGroupId || GroupId < 0 {
      return nil, errors.New("err GroupId")
   }


   idWork = &SnowflakeIdWorker{
      GroupId:      GroupId,
      RegionId:  RegionId,
      LastTimestamp: -1,
      Sequence: 0,
   }

   return
}

/**
 * 获得下一个ID
 */
func (idWork *SnowflakeIdWorker) NextId()(id int64, err error) {
   timestamp := timeGen()

   //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
   if timestamp < idWork.LastTimestamp {
      return 0, errors.New("err time")
   }

   //如果是同一时间生成的,则进行毫秒内序列
   if idWork.LastTimestamp == timestamp {
      idWork.Sequence = (idWork.Sequence + 1) & SequenceMask
      //毫秒内序列溢出
      if idWork.Sequence == 0 {
         //阻塞到下一个毫秒,获得新的时间戳
         timestamp = tilNextMillis(idWork.LastTimestamp)
      }
   } else {
      //时间戳改变,毫秒内序列重置
      idWork.Sequence = 0
   }

   //上次生成ID的时间截
   idWork.LastTimestamp = timestamp

   //移位并通过或运算拼到一起组成64位的ID
   id = (timestamp - StartSecs) << TimestampLeftShift | (idWork.RegionId << RegionIdShift)| (idWork.GroupId << GroupIdShift) | idWork.Sequence

   return
}

/**
 * 阻塞到下一个毫秒,直到获得新的时间戳
 * @param lastTimestamp 上次生成ID的时间截
 * @return 当前时间戳
 */
func tilNextMillis(lastTimestamp int64)(timestamp int64) {
   timestamp = timeGen()
   for {
      if timestamp > lastTimestamp {
         break
      }

      timestamp = timeGen()
   }

   return timestamp
}

/**
 * 返回以毫秒为单位的当前时间
 */
func timeGen()int64{
   return time.Now().UnixNano() / int64(time.Millisecond)
}

//==============================Test=============================================
/** 测试 */
func TestGetId() {
   idWorker, _ := NewSnowflakeIdWorker(10, 2)

   for i :=0;i<1000;i++ {
      id, err := idWorker.NextId()
      if err != nil{
         fmt.Println(err)
         continue
      }

      fmt.Println(id)
   }
}
0条评论
0 / 1000
王****成
1文章数
0粉丝数
王****成
1 文章 | 0 粉丝
王****成
1文章数
0粉丝数
王****成
1 文章 | 0 粉丝
原创

用golang实现雪花算法 snowflake

2023-08-10 03:18:55
77
0
import (
   "errors"
   "fmt"
   "time"
)

/**
 * Snowflake<br>
 * SnowFlake的结构如下(每部分用-分开):<br>
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 0000000 - 0000 - 00000000000 <br>
 * 1位标识,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
 * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
 * 得到的值),这里的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序StartSecs)。
   41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
 * 11位的数据机器位,可以部署在2048个节点,包括7位RegionId和4位GroupId<br>
 * 11位序列,毫秒内的计数,11位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生2048个ID序号<br>
 * 加起来刚好64位,为一个Long型。<br>
 * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
 */




const(
   //开始时间戳  2020-05-19
   StartSecs int64 = 1589817600000

   // 大区ID所占的位数
   RegionIdBits int64 = 7

   // 机房ID所占的位数
   GroupIdBits int64 = 4

   // 支持的最大大区id,结果是31
   MaxRegionId int64 = -1 ^ (-1 << RegionIdBits)

   // 支持的最大机房ID,结果是15
   MaxGroupId int64 = -1 ^ (-1 << GroupIdBits)

   // 序列在id中占的位数
   SequenceBits int64 = 11

   // 机房ID向左移11位
   GroupIdShift int64 = SequenceBits

   // 大区ID向左移15位(11+4)
   RegionIdShift int64 = SequenceBits + GroupIdBits

   // 时间截向左移22位(11+4+7)
   TimestampLeftShift = SequenceBits + GroupIdBits + RegionIdBits

   // 生成序列的掩码,这里为2047 (0b11111111111=0x7ff=2047)
   SequenceMask = -1 ^ (-1 << SequenceBits)
)

type SnowflakeIdWorker struct {
   RegionId int64    //大区id
   GroupId int64    //机房id
   LastTimestamp int64
   Sequence int64  // 毫秒内序列(0~2047)
}


//==============================Constructors=====================================
/**
 * 构造函数
 * @param RegionId 大区ID (0~15)
 * @param GroupId 机房序号 (0~127)
 */
func NewSnowflakeIdWorker(RegionId, GroupId int64)(idWork *SnowflakeIdWorker, err error) {
   if RegionId > MaxRegionId || RegionId < 0 {
      return nil, errors.New("err RegionId")
   }

   if GroupId > MaxGroupId || GroupId < 0 {
      return nil, errors.New("err GroupId")
   }


   idWork = &SnowflakeIdWorker{
      GroupId:      GroupId,
      RegionId:  RegionId,
      LastTimestamp: -1,
      Sequence: 0,
   }

   return
}

/**
 * 获得下一个ID
 */
func (idWork *SnowflakeIdWorker) NextId()(id int64, err error) {
   timestamp := timeGen()

   //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
   if timestamp < idWork.LastTimestamp {
      return 0, errors.New("err time")
   }

   //如果是同一时间生成的,则进行毫秒内序列
   if idWork.LastTimestamp == timestamp {
      idWork.Sequence = (idWork.Sequence + 1) & SequenceMask
      //毫秒内序列溢出
      if idWork.Sequence == 0 {
         //阻塞到下一个毫秒,获得新的时间戳
         timestamp = tilNextMillis(idWork.LastTimestamp)
      }
   } else {
      //时间戳改变,毫秒内序列重置
      idWork.Sequence = 0
   }

   //上次生成ID的时间截
   idWork.LastTimestamp = timestamp

   //移位并通过或运算拼到一起组成64位的ID
   id = (timestamp - StartSecs) << TimestampLeftShift | (idWork.RegionId << RegionIdShift)| (idWork.GroupId << GroupIdShift) | idWork.Sequence

   return
}

/**
 * 阻塞到下一个毫秒,直到获得新的时间戳
 * @param lastTimestamp 上次生成ID的时间截
 * @return 当前时间戳
 */
func tilNextMillis(lastTimestamp int64)(timestamp int64) {
   timestamp = timeGen()
   for {
      if timestamp > lastTimestamp {
         break
      }

      timestamp = timeGen()
   }

   return timestamp
}

/**
 * 返回以毫秒为单位的当前时间
 */
func timeGen()int64{
   return time.Now().UnixNano() / int64(time.Millisecond)
}

//==============================Test=============================================
/** 测试 */
func TestGetId() {
   idWorker, _ := NewSnowflakeIdWorker(10, 2)

   for i :=0;i<1000;i++ {
      id, err := idWorker.NextId()
      if err != nil{
         fmt.Println(err)
         continue
      }

      fmt.Println(id)
   }
}
文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0