業務訂單號生成算法,每秒50W左右,不同機器保證不重復,包含日期可讀性好


  • 參考snowflake算法,基本思路:
  • 序列12位(更格式化的輸出后,性能損耗導致每毫秒生成不了這么多,所以可以考慮減少這里的位,不過留着也並無影響)

  • 機器位10位

  • 毫秒為左移 22位

  • 上述幾個做或運算后得出一個唯一的數,轉10進制后,最大10位,最小7位,string.format來統一為10,format性能影響,導致性能降低3倍左右

FilUtils不想用的話,1太機器可以直接考慮使用1,多機器根據代碼配置id
代碼如下:

package net.gitosc.lianqu1990.utils.code;

import net.gitosc.lianqu1990.utils.date.DateFormatUtils;
import net.gitosc.lianqu1990.utils.date.TimeMark;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

/**
 * 缺陷是,訂單量沒那么大,導致機器碼|序列 后,一般都是4096
 * 通過將毫秒引入序列后修正
 * 后來加了format以后性能受損,比idcenter慢10倍,每秒可以生成50w,idcenter將近500w,不過這也是idcenter極限
 * 夠用,暫不優化
 * @author hanchao
 * @date 2017/4/20 19:01
 */
public class OrderNoCenter {

    public static final Logger logger = LoggerFactory.getLogger(OrderNoCenter.class);
    private static final String WORKERID_PATH = "/etc/workerId";

    private OrderNoCenter() {
    }

    private static class OrderNoCenterHolder{
        private static OrderNoCenter instance = new OrderNoCenter();
    }

    public static OrderNoCenter getInstance() {
        return OrderNoCenterHolder.instance;
    }

    /**
     * 節點 ID 默認取1
     */
    private long workerId = 1;
    /**
     * 序列id 默認取1
     */
    private long sequence = 1;

    /**
     * 機器標識位數
     */
    private final long workerIdBits = 10L;
    /**
     * 機器ID最大值
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits); //結果就是2的workerBits次方-1,能表示的最大數.全部1亦或10位0,就是0開頭最后10位1
    /**
     * 毫秒內自增位
     */
    private final long sequenceBits = 12L;
    /**
     * 機器ID偏左移12位
     */
    private final long workerIdShift = sequenceBits;
    /**
     * 數據中心ID左移17位
     */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

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

    /**
     * 時間毫秒左移22位
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits;

    private long lastTimestamp = -1L;

    public void initParam() {
        // 從默認位置讀取workerId,最大1024
        try {
            File conf = new File(WORKERID_PATH);
            if(conf.exists()){
                String str = FileUtils.readFileToString(conf);
                workerId = Integer.parseInt(str);
            }else{
                logger.warn(" worker id not found,will use default value...");
            }
        } catch(Exception e){
            e.printStackTrace();
        }
        logger.info(" worker id is {}",workerId);
        if (workerId < 0 || workerId > maxWorkerId) {
            throw new IllegalArgumentException("workerId is illegal: "
                    + workerId);
        }
    }

    public long getWorkerId() {
        return workerId;
    }

    public long getTime() {
        return System.currentTimeMillis();
    }

    public String create() {
        return nextNo();
    }

    /**
     * 獲取id 線程安全
     *
     * @return
     */
    private synchronized String nextNo() {
        long timestamp = timeGen();
        // 時間錯誤
        if (timestamp < lastTimestamp) {
            throw new IllegalStateException("Clock moved backwards.");
        }
        // 當前毫秒內,則+1
        if (lastTimestamp == timestamp) {
            // 當前毫秒內計數滿了,則等待下一秒
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }
        lastTimestamp = timestamp;
        // ID偏移組合生成最終的ID,並返回ID,最大十位數

        long id = ((timestamp % 1000) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
        String timestampStr = DateFormatUtils.NUMBER_FORMAT.format(timestamp);
        return timestampStr+String.format("%010d",id);
    }

    /**
     * 等待下一個毫秒的到來
     *
     * @param lastTimestamp
     * @return
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

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

    /**
     * 最大十位,最小7位,補0format
     */
    /*public void test(){
        String t = String.valueOf((1L << 22) | (1 << 12) | 0);
        String t1 = String.valueOf((999L << 22) | (1023 << 12) | 0);
        System.out.println(DateFormatUtils.NUMBER_FORMAT.format(System.currentTimeMillis())+"-"+t);
        System.out.println(DateFormatUtils.NUMBER_FORMAT.format(System.currentTimeMillis())+"-"+t1);
        long l1 = (1L << 22) | (1 << 12) | 0;
        long l2 = (999L << 22) | (1023 << 12) | 0;
        System.out.println(l1);
        System.out.println(l2);
        System.out.println(String.format("%010d",l1));
        System.out.println(String.format("%010d",l2));
    }*/

    public static void main(String[] args){
        for (int i = 0; i < 100; i++) {
            System.out.println(OrderNoCenter.getInstance().create());
        }

        //性能測試
        TimeMark mark = new TimeMark();
        for (int i = 0; i < 1000000; i++) {
            OrderNoCenter.getInstance().create();
        }
        mark.simplePrint();

        mark.mark();

        for (int i = 0; i < 1000000; i++) {
            IdCenter.getInstance().getId();
        }
        mark.simplePrint();
    }
}

缺少代碼的話,請直接使用我的附件代碼

附件:
代碼代碼代碼代碼代碼點擊下載下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM