分布式ID生成

本文介绍了Java实现的IdWorker类,用于生成带有时间戳的全局唯一ID,关注了并发控制、时间戳处理和机器标识符的管理。通过实例方法和私有辅助函数,确保了ID的生成既高效又正确。
public class IdWorker {
    private final static Logger logger = LoggerFactory.getLogger(IdWorker.class);

    private final long workerId;
    private final long epoch = 1403854494756L;   // 时间起始标记点,作为基准,一般取系统的最近时间
    private final long workerIdBits = 10L;      // 机器标识位数
    private final long maxWorkerId = -1L ^ -1L << this.workerIdBits;// 机器ID最大值: 1023
    private long sequence = 0L;                   // 0,并发控制
    private final long sequenceBits = 12L;      //毫秒内自增位

    private final long workerIdShift = this.sequenceBits;                             // 12
    private final long timestampLeftShift = this.sequenceBits + this.workerIdBits;// 22
    private final long sequenceMask = -1L ^ -1L << this.sequenceBits;                 // 4095,111111111111,12位
    private long lastTimestamp = -1L;

    private IdWorker(long workerId) {
        System.out.println("workerId:"+workerId);
        if (workerId > this.maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", this.maxWorkerId));
        }
        this.workerId = workerId;
    }

    public synchronized long nextId() throws Exception {
        long timestamp = timeGen();
        if (this.lastTimestamp == timestamp) { // 如果上一个timestamp与新产生的相等,则sequence加一(0-4095循环); 对新的timestamp,sequence从0开始
            this.sequence = this.sequence + 1 & this.sequenceMask;
            if (this.sequence == 0) {
                timestamp = this.tilNextMillis(this.lastTimestamp);// 重新生成timestamp
            }
        } else {
            this.sequence = 0;
        }

        if (timestamp < this.lastTimestamp) {
            logger.error(String.format("clock moved backwards.Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
            throw new Exception(String.format("clock moved backwards.Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
        }

        this.lastTimestamp = timestamp;
        return timestamp - this.epoch << this.timestampLeftShift | this.workerId << this.workerIdShift | this.sequence;
    }

    private static IdWorker instance;

    public static IdWorker getFlowIdWorkerInstance() {
        if (instance == null) {
            synchronized (IdWorker.class) {
                if (instance == null) {
                    instance = new IdWorker(workerIp());
                }
            }
        }
        return instance;
    }


    /**
     * 等待下一个毫秒的到来, 保证返回的毫秒数在参数lastTimestamp之后
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    /**
     * 获得系统当前毫秒数
     */
    private static long timeGen() {
        return System.currentTimeMillis();
    }

    public static Long workerIp() {
        String ipString = IPUtils.getInternalIp();
        Long[] ip = new Long[4];
        int pos1= ipString.indexOf(".");
        int pos2= ipString.indexOf(".",pos1+1);
        int pos3= ipString.indexOf(".",pos2+1);
        ip[3] = Long.parseLong(ipString.substring(pos3+1));
        return ip[3];
    }


}
### 分布式ID生成方案及其实现 #### 常见的分布式ID生成方法 在分布式系统中,为了唯一标识对象或事务,通常会采用特定的ID生成策略。以下是几种常用的分布式ID生成方案: 1. **雪花算法(Snowflake)** 雪花算法是由Twitter提出的用于生成全局唯一ID的一种解决方案[^1]。该算法的核心思想是通过组合多个字段来构建一个64位整数作为ID。具体结构如下: - 1位:符号位,始终为0。 - 41位:时间戳,记录毫秒级的时间差。 - 10位:机器ID,可以区分不同的服务器实例。 - 12位:序列号,在同一毫秒内生成的不同ID可以通过此字段区分。 下面是一个基于Python实现的简单版本的雪花算法: ```python import time class SnowflakeGenerator: def __init__(self, machine_id): self.machine_id = machine_id & 0b1111111111 # 确保machine_id不超过10位 self.sequence = 0 self.last_timestamp = -1 def generate(self): timestamp = int(time.time() * 1000) if timestamp < self.last_timestamp: raise Exception("Clock moved backwards") if timestamp == self.last_timestamp: self.sequence = (self.sequence + 1) & 0xFFF if self.sequence == 0: timestamp = self.wait_next_millis(timestamp) else: self.sequence = 0 self.last_timestamp = timestamp return ((timestamp << 22) | (self.machine_id << 12) | self.sequence) def wait_next_millis(self, last_timestamp): timestamp = int(time.time() * 1000) while timestamp <= last_timestamp: timestamp = int(time.time() * 1000) return timestamp ``` 2. **UUID(通用唯一识别码)** UUID是一种标准的128位长度的编号格式,广泛应用于各种场景下生成唯一标识符。它分为多种版本,最常见的是基于随机数的UUID v4和基于MAC地址与时间戳的UUID v1。尽管UUID能保证全球范围内的唯一性,但由于其无序特性以及较大的存储开销,在某些性能敏感的应用中可能不适用。 3. **数据库自增主键** 使用关系型数据库中的自增列也可以作为一种简单的分布式ID生成方式。然而这种方式存在明显的局限性——当面对大规模高并发请求时,单台数据库可能会成为瓶颈,难以满足高性能需求[^3]。 4. **ZooKeeper/etcd等协调服务** 利用像Apache ZooKeeper或者CoreOS etcd这样的分布式一致性协议工具可以帮助我们设计更复杂的ID分配机制。例如可以在集群环境中维护一个计数器资源,并通过原子操作对其进行更新从而获取新的ID值[^2]。 #### 实现注意事项 无论选择哪种具体的实现技术路线,都需要考虑以下几个方面因素以确保整个系统的稳定高效运行: - **高可用性**:即使部分节点发生故障停机,整体服务仍然能够正常提供功能支持; - **低延迟响应速度**:尤其是在互联网业务场景下,用户体验至关重要,因此要尽量减少每次调用所需耗费的时间成本; - **扩展能力规划**:随着数据规模不断扩大,原有的设计方案是否容易调整适应新增加的工作负载量也是一个重要考量维度; ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值