侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Snowflake算法

2022-06-29 星期三 / 0 评论 / 0 点赞 / 60 阅读 / 8286 字

Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统

Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。

  • 41位毫秒级时间戳
  • 10为工作机器ID
.

严格意义上来说这个bit段的使用可以是进程级,机器级的话你可以使用MAC地址来唯一标示工作机器,工作进程级可以使用IP+Path来区分工作进程。如果工作机器比较少,可以使用配置文件来设置这个id是一个不错的选择,如果机器过多配置文件的维护是一个灾难性的事情。

.
  • 12位自增ID
.

序列号就是一系列的自增id(多线程建议使用atomic),为了处理在同一毫秒内需要给多条消息分配id,若同一毫秒把序列号用完了,则“等待至下一毫秒”。

.

优点

  • 基于内存,高性能高可用
  • ID总体有序,入库性能好

缺点

  • 依赖于系统时钟的一致性。如果某台机器的系统时钟回拨,有可能造成ID冲突,或者ID乱序。

Sequence 实现

/** * <p> * 分布式高效有序ID生产黑科技(sequence) <br> * 优化开源项目:http://git.oschina.net/yu120/sequence * </p> * * @author hubin * @since 2016-08-18 */public class Sequence {    private static final Log logger = LogFactory.getLog(Sequence.class);    /**     * 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)     */    private final long twepoch = 1288834974657L;    /**     * 机器标识位数     */    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 final long workerId;    /**     * 数据标识 ID 部分     */    private final long datacenterId;    /**     * 并发控制     */    private long sequence = 0L;    /**     * 上次生产 ID 时间戳     */    private long lastTimestamp = -1L;    public Sequence() {        this.datacenterId = getDatacenterId(maxDatacenterId);        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);    }    /**     * <p>     * 有参构造器     * </p>     *     * @param workerId     工作机器 ID     * @param datacenterId 序列号     */    public Sequence(long workerId, long datacenterId) {        Assert.isFalse(workerId > maxWorkerId || workerId < 0,            String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));        Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0,            String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));        this.workerId = workerId;        this.datacenterId = datacenterId;    }    /**     * <p>     * 获取 maxWorkerId     * </p>     */    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {        StringBuilder mpid = new StringBuilder();        mpid.append(datacenterId);        String name = ManagementFactory.getRuntimeMXBean().getName();        if (StringUtils.isNotEmpty(name)) {            /*             * GET jvmPid             */            mpid.append(name.split(StringPool.AT)[0]);        }        /*         * MAC + PID 的 hashcode 获取16个低位         */        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);    }    /**     * <p>     * 数据标识id部分     * </p>     */    protected static long getDatacenterId(long maxDatacenterId) {        long id = 0L;        try {            InetAddress ip = InetAddress.getLocalHost();            NetworkInterface network = NetworkInterface.getByInetAddress(ip);            if (network == null) {                id = 1L;            } else {                byte[] mac = network.getHardwareAddress();                if (null != mac) {                    id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;                    id = id % (maxDatacenterId + 1);                }            }        } catch (Exception e) {            logger.warn(" getDatacenterId: " + e.getMessage());        }        return id;    }    /**     * 获取下一个ID     *     * @return     */    public synchronized long nextId() {        long timestamp = timeGen();        //闰秒        if (timestamp < lastTimestamp) {            long offset = lastTimestamp - timestamp;            if (offset <= 5) {                try {                    wait(offset << 1);                    timestamp = timeGen();                    if (timestamp < lastTimestamp) {                        throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));                    }                } catch (Exception e) {                    throw new RuntimeException(e);                }            } else {                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));            }        }        if (lastTimestamp == timestamp) {            // 相同毫秒内,序列号自增            sequence = (sequence + 1) & sequenceMask;            if (sequence == 0) {                // 同一毫秒的序列数已经达到最大                timestamp = tilNextMillis(lastTimestamp);            }        } else {            // 不同毫秒内,序列号置为 1 - 3 随机数            sequence = ThreadLocalRandom.current().nextLong(1, 3);        }        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 SystemClock.now();    }}
.
.

广告 广告

评论区