Twitter-SnowFlake算法的产生是源于Twitter为了满足自己业务(每秒上万条消息的请求,每条消息都必须分配一条唯一的id,并且在分布式系统中不同机器产生的id必须不同)的需求。
snowflake的结构如下(每部分用-分开):
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号) 一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)
snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。
snowflake ID 生成策略图解
0 | 41bit | 51bit | 64bit |
---|---|---|---|
未使用 | timestamp(毫秒) | datacenterId+workerId | 毫秒内的计数 |
/** * ID 生成策略 * 毫秒级时间41位+机器ID, 10位+毫秒内序列12位。高位始终为0,表示正数。 * 0 41 51 64 * +-----------+------+------+ * |time |pc |inc | * +-----------+------+------+ * 前41bits是以微秒为单位的timestamp。 * 接着10bits是事先配置好的机器ID。 * 最后12bits是累加计数器。 * macheine id(10bits)标明最多只能有1024台机器同时产生ID,sequence number(12bits)也标明1台机器1ms中最多产生4096个ID, * */ PHP代码
class IdWork
{
private static $workerId;
private static $maxWorkerId = 1023; //最大的机器节点, 2^10 - 1
private static $sequence = 0;
private static $sequenceMask = 4095; //最大的序列节点, 2^12 - 1
private static $workerIdShift = 12; //机器ID左移位数,63 - 51
private static $timestampLeftShift = 22; //毫秒时间戳左移位数,63 - 41
private static $twepoch = 1288834974657;
private static $lastTimestamp = -1;
/**
* @var self
*/
private static $self = null;
/**
* @return self
*/
public static function getInstance()
{
if (self::$self == null) {
self::$self = new self();
}
return self::$self;
}
public function setWorkId($workId)
{
if ($workId > self::$maxWorkerId || $workId < 0) {
throw new \Exception("worker Id can't be greater than ".self::$maxWorkerId." or less than 0");
}
self::$workerId = $workId;
return self::$self;
}
private function timeGen()
{
//获得当前时间戳
$time = explode(' ', microtime());
$time2 = substr($time[0], 2, 3);
return $time[1] . $time2;
}
private function tilNextMillis($lastTimestamp)
{
$timestamp = $this->timeGen();
while ($timestamp <= $lastTimestamp) {
$timestamp = $this->timeGen();
}
return $timestamp;
}
public function nextId()
{
$timestamp = $this->timeGen();
//如果存在并发调用,则自增sequence
if (self::$lastTimestamp == $timestamp) {
self::$sequence = (self::$sequence + 1) & self::$sequenceMask;
//如果sequence自增到4095,也就是4096 & 4095 = 0,重新取时间戳
if (self::$sequence == 0) {
$timestamp = $this->tilNextMillis(self::$lastTimestamp);
}
} else {
self::$sequence = 0;
}
if ($timestamp < self::$lastTimestamp) {
throw new \Exception("Clock moved backwards. Refusing to generate id for " . (self::$lastTimestamp - $timestamp) . " milliseconds");
}
self::$lastTimestamp = $timestamp;
if (PHP_INT_SIZE === 4) {
return 0;
}
$nextId = ((sprintf('%.0f', $timestamp) - sprintf(
'%.0f',
self::$twepoch
)) << self::$timestampLeftShift) | (self::$workerId << self::$workerIdShift) | self::$sequence;
return $nextId;
}
}
//用法:
$order_no = IdWork::getInstance()->setWorkId($wordId)->nextId();
java代码
/**
**/
public class SnowFlake {
/**
* 起始时间戳,从2021-12-01开始生成
*/
private final static long START_STAMP = 1638288000000L;
/**
* 序列号占用的位数 12
*/
private final static long SEQUENCE_BIT = 12;
/**
* 机器标识占用的位数
*/
private final static long MACHINE_BIT = 10;
/**
* 机器数量最大值
*/
private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
/**
* 序列号最大值
*/
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;
/**
* 机器标识
*/
private long machineId;
/**
* 序列号
*/
private long sequence = 0L;
/**
* 上一次时间戳
*/
private long lastStamp = -1L;
/**
* 构造方法
* @param machineId 机器ID
*/
public SnowFlake(long machineId) {
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new RuntimeException("机器超过最大数量");
}
this.machineId = machineId;
}
/**
* 产生下一个ID
*/
public synchronized long nextId() {
long currStamp = getNewStamp();
if (currStamp < lastStamp) {
throw new RuntimeException("时钟后移,拒绝生成ID!");
}
if (currStamp == lastStamp) {
// 相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
// 同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStamp = getNextMill();
}
} else {
// 不同毫秒内,序列号置为0
sequence = 0L;
}
lastStamp = currStamp;
return (currStamp - START_STAMP) << TIMESTAMP_LEFT // 时间戳部分
| machineId << MACHINE_LEFT // 机器标识部分
| sequence; // 序列号部分
}
private long getNextMill() {
long mill = getNewStamp();
while (mill <= lastStamp) {
mill = getNewStamp();
}
return mill;
}
private long getNewStamp() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
// 订单ID生成测试,机器ID指定第0台
SnowFlake snowFlake = new SnowFlake(0);
System.out.println(snowFlake.nextId());
}
}
大厂也有雪花算法的案例,并且开源,比如百度、滴滴
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有