21 June 2024

介绍

  • 分布式系统,分库分表的情况下,即想查询一个用户的所有订单,又想查询每个订单的详情。尽量减少数据库的查询次数

使用

  • 把userid拼接到订单id的后面

原理

  • 一个数取余2的n次方,那么余数就是这个数的二进制的最后n位数。所有我们可以位操作符把高位清零就可以得到余数.
  • pkD3yBF.png

实现

    UniqueIDUtils uniqueIDUtils = new UniqueIDUtils();
    TOrder tOrder = new TOrder();
    tOrder.setBusinessId(112L);
    long userId = 666;
    for (int i = 0; i < 5; i++) {
        tOrder.setUserId(userId + i);
        long orderId = uniqueIDUtils.bulidOrderId(tOrder.getUserId());
        String userIdbak = Long.toBinaryString(tOrder.getUserId());
        String orderIdBak = Long.toBinaryString(orderId);
        System.out.println("第" + (i + 1) + "次userId=" + userIdbak);
        System.out.println("第" + (i + 1) + "次orderId=" + orderIdBak);
        long i1 = Long.parseLong(userIdbak, 2);
        long i2 = Long.parseLong(orderIdBak, 2);
        System.out.println(i1+"===="+i2);
        System.out.println(i1 % 64+"===="+i2 %64);
        System.out.println(i1 % 128+"===="+i2 %128);
    }
public final class UniqueIDUtils {
    private static final Logger logger = LoggerFactory.getLogger(UniqueIDUtils.class);
    // 取IP地址最后一段作为多机分隔段
    private static long SERVER_ID = 255L;

    /**
     * 数据库id
     */
    private static long DB_ID = 63;

    /**
     * 序列号
     */
    private long sequence;

    /**
     * 开始时间戳
     */
    private final long beginTime = 1514736000000L;

    /**
     * 服务器个数比特位个数
     */
    private final long serverBits = 8L;

    /**
     * 数据库个数比特位个数
     */
    private final long dbBits = 6L;
    /**
     * 序列号比特位个数
     */
    private final long sequenceBits = 5L;

    /**
     * 服务器最大个数
     */
    private final long maxServerCount = -1L ^ (-1L << serverBits);
    /**
     * 数据库最大个数
     */
    private final long maxDbCount = -1L ^ (-1L << dbBits);


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

    /**
     * 数据库位移量
     */
    private final long dbShift = sequenceBits;

    /**
     * 服务器位移量
     */
    private final long serverShift = dbShift + dbBits;

    /**
     * 时间戳位移量
     */
    private final long timeStampLeftShift = serverShift + serverBits;

    /**
     * 上次生成id的时间戳
     */
    private long lastTimeStamp = -1L;

    static {
        try {
            String hostAddress = InetAddress.getLocalHost().getHostAddress();
            String[] nums = hostAddress.split("\\.");
            SERVER_ID = Long.parseLong(nums[3]);
        } catch (Exception ex) {
            logger.error("UniqueIDUtils init Exception, Default SERVER_ID = " + SERVER_ID, ex);
        }
    }


    public synchronized long nextId() {
        long timeStamp = timeGen();
        //当前时间小于上次生成id的时间戳,说明系统时钟回退,更新当前时间,保证id生成器可用
        if (timeStamp < lastTimeStamp) {
            timeStamp = tilNextMills(lastTimeStamp);
        }
        //序号与当前时间戳无关,每次递增
        sequence = (sequence + 1) & sequenceMask;
        //当同一时间戳内的并发量大于序号的最大值,则时间戳向后增加
        if (sequence == 0L && timeStamp == lastTimeStamp) {
            timeStamp = tilNextMills(timeStamp);
        }
        return (timeStamp - beginTime) << timeStampLeftShift | SERVER_ID << serverShift | DB_ID << dbShift | sequence;
    }

    protected long tilNextMills(long lastTimeStamp) {
        long timeStamp = timeGen();
        while (timeStamp < lastTimeStamp) {
            timeStamp = timeGen();
        }
        return timeStamp;
    }

    /**
     * 返回当前时间,单位毫秒
     *
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }


    public long bulidOrderId(long userId) {
        //取用户id后4位
        userId = userId & 15;
        //先取60位唯一id
        long uniqueId = this.nextId();
        //唯一id左移4位、拼接userId后4位
        return (uniqueId << 4) | userId;
    }
}

结果

第1次userId=1010011010
第1次orderId=1011111000100011000101100001101111110100000001111111000011010
666====1712602350088158746
26====26
26====26
第2次userId=1010011011
第2次orderId=1011111000100011000101100001101111110100000001111111000101011
667====1712602350088158763
27====43
27====43


blog comments powered by Disqus