美文网首页
实现全局唯一id碰到的一个坑

实现全局唯一id碰到的一个坑

作者: 黄云斌huangyunbin | 来源:发表于2018-10-10 10:00 被阅读0次

仿照twitter的snowflake写了全局唯一id的实现。

twitter的是基于毫秒的,每一个ms最大能产生4096个id,因为我们是批量取的,4096有点太小了。我们就改成秒了,一秒就是4096*1000=409w,这个就足够大了。

但是修改后测试却发现出现了负数,平时自己是没发现问题。

最后发现问题出现在int的左移上,4096*1000,意味着需要移动12+10=22位。

int一共就32位,大于等于512的时候,左移22位,第一位为1就是负数了。

一个整数或操作一个负数,还是负数。这就是问题的原因了。

可能有人还会问,我一个很大的long,或一个很小的负数int,为什么就是负数呢
我们来看看二进制的表示:
-1: 11111111111111111111111111111111
-2: 11111111111111111111111111111110
-1L: 1111111111111111111111111111111111111111111111111111111111111111
-2L: 1111111111111111111111111111111111111111111111111111111111111110
int和long或操作的时候,int要转为long。负数转为long就是前面的32位都为1了。所以第一位必为1,就是肯定是负数。

上面说的有点抽象,直接看代码吧,代码就60多行而已。

/**
 * snowflake算法实现
 */
public class SnowFlake {

    /**
     * 起始的时间戳(单位: SECONDS),2018年9月20日
     */
    private final static long START = 1537372800L;

    /**
     * 每一部分占用的位数
     */
    private final static long SEQUENCE_BIT = 22; // 序列号占用的位数
    private final static long MACHINE_BIT = 10; // 机器标识占用的位数

    /**
     * 每一部分的最大值
     */
    private final static long MAX_SEQUENCE = (1 << SEQUENCE_BIT) - 1;

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long TIME_LEFT = SEQUENCE_BIT + MACHINE_BIT;

    private long sequence = 0L; // 序列号
    private long lastTime = -1L;// 上一次时间戳

    /**
     * 产生下一个ID
     * machineId必须为long。因为int在大于等于512左移22位,就会变成负数。
     */
    public synchronized List<Long> nextId(long machineId, int batchSize) {
        List<Long> result = new ArrayList<>(batchSize);
        long currentTime = System.currentTimeMillis() / 1000;
        if (currentTime < lastTime) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currentTime == lastTime && (sequence + batchSize) > MAX_SEQUENCE) {
            // 1秒钟400多万应该是足够了的,还是超过就直接抛异常
            throw new RuntimeException("SnowFlake over " + MAX_SEQUENCE + ",at time " + currentTime);
        }

        if (currentTime > lastTime) {
            sequence = 0L;
        }

        long first = (currentTime - START) << TIME_LEFT // 时间戳部分
                | machineId << MACHINE_LEFT // 机器标识部分
                | (sequence + 1); // 序列号部分
        result.add(first);

        for (int i = 1; i < batchSize; i++) {
            result.add(first + i);
        }
        sequence = sequence + batchSize;
        lastTime = currentTime;
        return result;
    }

相关文章

  • 实现全局唯一id碰到的一个坑

    仿照twitter的snowflake写了全局唯一id的实现。 twitter的是基于毫秒的,每一个ms最大能产生...

  • redis实现全局唯一id

    背景 新公司用mysql开发,对于建表什么的规定特别严格,主键id必须是自增,但是取模分表的过程中,id是不能重复...

  • Redis-全局唯一ID

    零、本文纲要 一、全局唯一ID 二、Redis生成全局唯一ID1、snowflake算法全局唯一ID策略2、Red...

  • 防止mq重复消费

    1. 利用全局唯一id 消息里放入全局唯一id,做完业务后查询id是否存在表里,不存在则将唯一id插入数据库单独...

  • 全局唯一ID

    生成全局唯一ID 通过MySQL的自增主键,作为唯一id; 通过内存中变量AtomicLong的自增来得到唯一id...

  • 04.分布式系统的id生成方式

    分布式ID需要满足那些条件? 全局唯一:必须保证ID是全局性唯一的,基本要求高性能:高可用低延时,ID生成响应要块...

  • 就这?分布式 ID 发号器实战

    分布式 ID 需要满足的条件: 全局唯一:这是最基本的要求,必须保证 ID 是全局唯一的。 高性能:低延时,不能因...

  • 就这?分布式 ID 发号器实战

    分布式 ID 需要满足的条件: 全局唯一:这是最基本的要求,必须保证 ID 是全局唯一的。 高性能:低延时,不能因...

  • 全局唯一ID设计

    在分布式系统中,经常需要使用全局唯一ID查找对应的数据。产生这种ID需要保证系统全局唯一,而且要高性能以及占用相对...

  • 全局唯一ID - snowflake

    为什么需要全局唯一ID 比如以下 分布式下唯一ID 如果数据库分了库/表, 那么表的自增主键就不再唯一, 这时候就...

网友评论

      本文标题:实现全局唯一id碰到的一个坑

      本文链接:https://www.haomeiwen.com/subject/jaauaftx.html