关于生成订单号规则的一些思考

关于我为何写这篇文章是由于今天在作订单模块的时候,看到以前的PRD上描述的年月日+用户id2位+企业id位
+四位自增加数。而后竟被我反驳的忽然改为了精确时间+4位自增加数,因而我更失望了。算法

咱们考虑一下,据我所常见的订单基本都14-20位。(年月日时分秒和随机数)基本上就有14位了。虽然通常项目作不到淘宝双11这种
支付峰值达到每秒10万笔订单.可是我以为至少事先能够考虑到,想必当初淘宝或许也没意识到之后发展
得这么好。sql

背景

对于其定订单的生成。我以为要至少要符合如下这三种,全局惟一 ,数据库

在复杂的分布式系统中,不少场景须要的都是全局惟一ID的场景,通常为了防止冲突能够考虑的有36
位的UUID,twitter的snowflake等。服务器

可是能够思考这些问题?网络

  1. 是否是应该有一些其余意义的思考,好比说订单系统有买家的id(取固定几位)
  2. 是否有商品的标识,方便熟悉业务的排查问题或者查询也经过不去系统查找能够有个初步的认识,可是业务量大的话感受就能够排除这我的为的去辨识了。
  3. 我的的见解是主要是惟一,其余关于业务方面的不是太过重要。

查阅了相关资料,主要有如下这几种并发

1.UUID
组成:当前日期+时间+时钟序列+机器识别号(Mac地址或其余)没有mac网卡的话会有别的东西识别。
在分布式系统中,全部元素(WEB服务器)都不须要经过中央控制端来判断数据惟一性。几十年以内能够达到全球惟一性。
snowflake的结构以下(每部分用-分开):less

2.Mysql经过AUTO_INCREMENT实现、Oracle经过Sequence序列实现。
在数据库集群环境下,不一样数据库节点可设置不一样起步值、相同步长来实现集群下生产全局惟1、递增ID分布式

3.Snowflake算法 雪花算法 
41位时间戳+10位机器ID+12位序列号(自增) 转化长度为18位的长整型。
Twitter为知足美秒上万条消息的建立,且ID须要趋势递增,方便客户端排序。
Snowflake虽然有同步锁,可是比uuid效率高。ui

4.Redis自增ID
实现了incr(key)用于将key的值递增1,并返回结果。若是key不存在,建立默认并赋值为0。 具备原子性,保证在并发的时候。this


可是我在这主要想说的是雪花算法生成id

关于序列
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

第一位为未使用,接下来的41位为毫秒级时间(41位的长度可使用69年),而后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每一个节点每毫秒产生4096个ID序号)

一共加起来恰好64位,为一个Long型。(转换成字符串长度为18)

snowflake生成的ID总体上按照时间自增排序,而且整个分布式系统内不会产生ID碰撞(由datacenter和workerId做区分),而且效率较高。听说:snowflake每秒可以产生26万个ID。

如下是代码
部分借鉴与网络
100万个ID 耗时2秒

/**
 * Created by youze on 18-7-5
 */
public class IdWorker {

    /**
     * 起始的时间戳
     */
    private final static long START_STMP = 1530795377086L;

    /**
     * 每一部分占用的位数
     */

    /**
     * 序列号占用的位数
     */
    private final static long SEQUENCE_BIT = 12;

    /**
     * 机器标识占用的位数
     */
    private final static long MACHINE_BIT = 5;

    /**
     * 数据中心占用的位数
     */
    private final static long DATACENTER_BIT = 5;

    /**
     * 每一部分的最大值
     */
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

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

    /**
     * 数据中心
     */
    private long datacenterId;

    /**
     * 机器标识
     */
    private long machineId;
    /**
     * 序列号
     */
    private long sequence = 0L;

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

    public IdWorker(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    /**
     * 产生下一个ID
     * @return
     */
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currStmp == lastStmp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            //不一样毫秒内,序列号置为0
            sequence = 0L;
        }

        lastStmp = currStmp;

        return (
                //时间戳部分
                currStmp - START_STMP) << TIMESTMP_LEFT
                //数据中心部分
                | datacenterId << DATACENTER_LEFT
                //机器标识部分
                | machineId << MACHINE_LEFT
                //序列号部分
                | sequence;
    }

    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }

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

    public static void main(String[] args) {
        IdWorker snowFlake = new IdWorker(2, 3);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            System.out.println(snowFlake.nextId());
        }
        System.out.println(System.currentTimeMillis() - start);
    }
}
相关文章
相关标签/搜索