关于Snowflake 生成53位ID
阅读原文时间:2023年07月09日阅读:1

1, bug现象: 没有经过处理的Snowflake 生成的是64位bit的唯一ID,但由于多数时候我们前台用到js,但是js只支持53位bit的数值。这样就导致了传到前台的64位的丢失精度。

解决思路:修改SnowFlake 的算法,使它生成 53bit的唯一ID,就可以了,代码如下

package com.wisdombud.product.configure.snowflake;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SnowflakeIdWorker {

private static final Pattern PATTERN\_LONG\_ID = Pattern.compile("^(\[0-9\]{15})(\[0-9a-f\]{32})(\[0-9a-f\]{3})$");

private static final Pattern PATTERN\_HOSTNAME = Pattern.compile("^.\*\\\\D+(\[0-9\]+)$");

private static final long OFFSET = LocalDate.of(2000, 1, 1).atStartOfDay(ZoneId.of("Z")).toEpochSecond();

private static final long MAX\_NEXT = 0b11111\_11111111\_111L;

private static final long SHARD\_ID = getServerIdAsLong();

private static long offset = 0;

private static long lastEpoch = 0;

public static long getNextId() {  
    return nextId(System.currentTimeMillis() / 1000);  
}

private static synchronized long nextId(long epochSecond) {  
    if (epochSecond < lastEpoch) {  
        epochSecond = lastEpoch;  
    }  
    if (lastEpoch != epochSecond) {  
        lastEpoch = epochSecond;  
        reset();  
    }  
    offset++;  
    long next = offset & MAX\_NEXT;  
    if (next == 0) {  
        return nextId(epochSecond + 1);  
    }  
    return generateId(epochSecond, next, SHARD\_ID);  
}

private static void reset() {  
    offset = 0;  
}

private static long generateId(long epochSecond, long next, long shardId) {  
    return ((epochSecond - OFFSET) << 21) | (next << 5) | shardId;  
}

private static long getServerIdAsLong() {  
    try {  
        String hostname = InetAddress.getLocalHost().getHostName();  
        Matcher matcher = PATTERN\_HOSTNAME.matcher(hostname);  
        if (matcher.matches()) {  
            long n = Long.parseLong(matcher.group(1));  
            if (n >= 0 && n < 8) {  
                return n;  
            }  
        }  
    } catch (UnknownHostException e) {

    }  
    return 0;  
}  

}

参考自: https://my.oschina.net/u/2552286/blog/3115621/print

64位的Snowflake 的算法

package com.wisdombud.product.configure.snowflake;

public class SnowflakeIdWorker {

private final long twepoch            = 1420041600000L;

private final long workerIdBits       = 5L;

private final long datacenterIdBits   = 5L;

private final long maxWorkerId        = -1L ^ (-1L << workerIdBits);

private final long maxDatacenterId    = -1L ^ (-1L << datacenterIdBits);

private final long sequenceBits       = 12L;

private final long workerIdShift      = sequenceBits;

private final long datacenterIdShift  = sequenceBits + workerIdBits;

private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

private final long sequenceMask       = -1L ^ (-1L << sequenceBits);

private Long       workerId           = 2L;

private Long       datacenterId       = 1L;

private long       sequence           = 0L;

private long       lastTimestamp      = -1L;

public SnowflakeIdWorker() {

}

public synchronized long nextId() {  
    long timestamp = timeGen();

    if (timestamp < lastTimestamp) {  
        throw new RuntimeException(String  
                .format("Clock moved backwards.  Refusing to generate id for %d milliseconds",  
                        lastTimestamp - timestamp));  
    }

    if (lastTimestamp == timestamp) {  
        sequence = (sequence + 1) & sequenceMask;

        if (sequence == 0) {

            timestamp = tilNextMillis(lastTimestamp);  
        }  
    }

    else {  
        sequence = 0L;  
    }

    lastTimestamp = timestamp;

    return ((timestamp - twepoch) << timestampLeftShift) //  
           | (datacenterId << datacenterIdShift) //  
           | (workerId << workerIdShift) //  
           | sequence;  
}

protected long tilNextMillis(long lastTimestamp) {  
    long timestamp = timeGen();  
    while (timestamp <= lastTimestamp) {  
        timestamp = timeGen();  
    }  
    return timestamp;  
}

protected long timeGen() {  
    return System.currentTimeMillis();  
}

public enum SnowflakeInstance {  
    INSTANCE();

    private SnowflakeIdWorker singleton;

    SnowflakeInstance() {  
        singleton = new SnowflakeIdWorker();  
    }

    public SnowflakeIdWorker getInstance() {  
        return singleton;  
    }  
}

public static Long getNextId() {  
    return SnowflakeInstance.INSTANCE.getInstance().nextId();  
}  

}