欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

分布式ID生成方案

程序员文章站 2022-05-04 17:26:02
...

1、方案一:数据库自增长序列或字段
比如用户的ID值,就可以通过在数据库的用户表中新增一个字段,然后这个字段可以设置为每次增长n。比如库1每次增长2,库2每次增长1。(当前系统使用的方案)

alter table t_ott_account add column user_id bigint;
CREATE SEQUENCE user_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
alter sequence user_id_seq restart with 1000000;
alter sequence user_id_seq  increment by 2 ; 
alter table t_ott_account alter column user_id set default nextval('user_id_seq');

优点:简单方便
缺点:
不同数据库语法和实现不同,数据库迁移的时候或多数据库版本支持的时候需要处理。
在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成。有单点故障的风险。
在性能达不到要求的情况下,比较难于扩展。
如果遇见多个系统需要合并或者涉及到数据迁移会相当痛苦。
分表分库的时候会有麻烦。

2、方案二:UUID
全球唯一
优点:简单,代码方便。
缺点:不适合做用户ID这种

3、方案三:Redis生成ID
Redis生成ID的方案可以在数据库自增长序列性能不满足要求的时候来使用。

4、Twitter的snowflack算法
参考:https://github.com/twitter/snowflake
思路:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0
分布式ID生成方案

public class SnowFlake
{
    /**
     * 起始的时间戳
     */
    private final static long twepoch = 1557825652094L;

    /**
     * 每一部分占用的位数
     */
    private final static long workerIdBits = 5L;

    private final static long datacenterIdBits = 5L;

    private final static long sequenceBits = 12L;

    /**
     * 每一部分的最大值
     */
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);

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

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

    /**
     * 每一部分向左的位移
     */
    private final static long workerIdShift = sequenceBits;

    private final static long datacenterIdShift = sequenceBits + workerIdBits;

    private final static long timestampShift = sequenceBits + workerIdBits + datacenterIdBits;

    private long datacenterId; // 数据中心ID

    private long workerId; // 机器ID

    private long sequence = 0L; // ***

    private long lastTimestamp = -1L; // 上一次时间戳

    public SnowFlake(long workerId, long datacenterId)
    {
        if (workerId > maxWorkerId || workerId < 0)
        {
            throw new IllegalArgumentException(
                String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0)
        {
            throw new IllegalArgumentException(String.format(
                "datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    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 (timestamp == lastTimestamp)
        {
            sequence = (sequence + 1) & maxSequence;
            if (sequence == 0L)
            {
                timestamp = tilNextMillis();
            }
        }
        else
        {
            sequence = 0L;
        }
        lastTimestamp = timestamp;

        return (timestamp - twepoch) << timestampShift // 时间戳部分
               | datacenterId << datacenterIdShift // 数据中心部分
               | workerId << workerIdShift // 机器标识部分
               | sequence; // ***部分
    }

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

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

    public static void main(String[] args)
    {
        SnowFlake snowFlake = new SnowFlake(1, 1);
        long nextId = snowFlake.nextId();
        System.out.println(nextId); 
        // 136931497562214400  136931566357188608
    }

}

分布式ID生成方案

另外还有zookeeper生成唯一ID和MongoDB的objectID,可以作为参考