2009-07-27 28 views
2

我想創建跨機器唯一的9位數字ID。我目前正在使用一個數據庫序列,但我想知道是否可以在沒有一個的情況下完成。這些序列將用於X12 EDI交易,因此它們不必永遠是唯一的。甚至可能只有24小時獨一無二。生成9位數字ID不帶數據庫序列

我唯一的想法:

  1. 每個服務器有2位的服務器標識符。
  2. 每個服務器都維護一個基本上跟蹤本地序列的文件。
  3. ID = + < 7位序列包裹>

我最大的問題是,如果硬盤驅動器出現故障該怎麼辦。我不知道它從哪裏離開。

我的所有其他想法基本上都是重新創建一個集中的數據庫序列。

有什麼想法?

+2

那麼,如果你的中央數據庫的硬盤驅動器出現故障怎麼辦?這不是每個服務器序列方法所特有的問題。 – 2009-07-27 04:14:44

+1

爲什麼你需要知道櫃檯的停留位置? – 2009-07-27 04:19:23

回答

2

如果高清失敗,你可以設置新的和未使用的2位的服務器標識,並確保該號碼是唯一的(至少24小時)

2

如何生成GUID(確保唯一性),然後使用某種散列函數將GUID轉換爲9位數字?
只是把我的頭頂部...

+0

這是我第一次想到的...... – Kip 2009-07-27 04:22:30

-2

冷使用唯一的數據像其他一些電源的前9位:

  1. 隨機數
  2. 系統時間
  3. 正常運行時間

已經厭倦了它兩秒鐘,這些都是獨一無二的,但你可以使用它們作爲散列函數的種子值,正如建議另一個答案。

3

的繼

{XX}{dd}{HHmm}{N} 

其中{XX}是機器編號{dd}是月份{HHmm}當前時間(24小時)的日期,{N}是連續編號。

高速崩潰將花費超過一分鐘,因此從0開始不是問題。

根據需求,您還可以用{ss}替換{dd}數秒。唯一性期限與每分鐘請求數。

0

使用上的變化:

md5(uniqid(rand(), true)); 

只是一個想法。

-1

在我最近的項目中,我也遇到了這個需求,生成N位長序列號,沒有任何數據庫。

這實際上是一個很好的面試問題,因爲考慮到性能和軟件崩潰恢復。如果有興趣,請致電Further Reading

下面的代碼具有下列特徵:

  1. 前綴與前綴的每個序列。

  2. 序列緩存,如Oracle序列。

  3. 最重要的是,恢復邏輯從軟件崩潰恢復序列。

完全實現附:

import java.util.concurrent.atomic.AtomicLong; 
import org.apache.commons.lang.StringUtils; 

/** 
* This is a customized Sequence Generator which simulates Oracle DB Sequence Generator. However the master sequence 
* is stored locally in the file as there is no access to Oracle database. The output format is "prefix" + number. 
* <p> 
* <u><b>Sample output:</u></b><br> 
* 1. FixLengthIDSequence(null,null,15,0,99,0) will generate 15, 16, ... 99, 00<br> 
* 2. FixLengthIDSequence(null,"K",1,1,99,0) will generate K01, K02, ... K99, K01<br> 
* 3. FixLengthIDSequence(null,"SG",100,2,9999,100) will generate SG0100, SG0101, ... SG8057, (in case server crashes, the new init value will start from last cache value+1) SG8101, ... SG9999, SG0002<br> 
*/ 
public final class FixLengthIDSequence { 

    private static String FNAME; 
    private static String PREFIX; 
    private static AtomicLong SEQ_ID; 
    private static long MINVALUE; 
    private static long MAXVALUE; 
    private static long CACHEVALUE; 

    // some internal working values. 
    private int iMaxLength; // max numeric length excluding prefix, for left padding zeros. 
    private long lNextSnapshot; // to keep track of when to update sequence value to file. 
    private static boolean bInit = false; // to enable ShutdownHook routine after program has properly initialized 

    static { 
     // Inspiration from http://stackoverflow.com/questions/22416826/sequence-generator-in-java-for-unique-id#35697336. 
     Runtime.getRuntime().addShutdownHook(new Thread(() -> { 
      if (bInit) { // Without this, saveToLocal may hit NullPointerException. 
       saveToLocal(SEQ_ID.longValue()); 
      } 
     })); 
    } 

    /** 
    * This POJO style constructor should be initialized via Spring Singleton. Otherwise, rewrite this constructor into Singleton design pattern. 
    * 
    * @param sFilename This is the absolute file path to store the sequence number. To reset the sequence, this file needs to be removed manually. 
    * @param prefix The hard-coded identifier. 
    * @param initvalue 
    * @param minvalue 
    * @param maxvalue 
    * @param cache 
    * @throws Exception 
    */ 
    public FixLengthIDSequence(String sFilename, String prefix, long initvalue, long minvalue, long maxvalue, int cache) throws Exception { 
     bInit = false; 
     FNAME = (sFilename==null)?"C:\\Temp\\sequence.txt":sFilename; 
     PREFIX = (prefix==null)?"":prefix; 
     SEQ_ID = new AtomicLong(initvalue); 
     MINVALUE = minvalue; 
     MAXVALUE = maxvalue; iMaxLength = Long.toString(MAXVALUE).length(); 
     CACHEVALUE = (cache <= 0)?1:cache; lNextSnapshot = roundUpNumberByMultipleValue(initvalue, cache); // Internal cache is always 1, equals no cache. 

     // If sequence file exists and valid, restore the saved sequence. 
     java.io.File f = new java.io.File(FNAME); 
     if (f.exists()) { 
      String[] saSavedSequence = loadToString().split(","); 
      if (saSavedSequence.length != 6) { 
       throw new Exception("Local Sequence file is not valid"); 
      } 

      PREFIX = saSavedSequence[0]; 
      //SEQ_ID = new AtomicLong(Long.parseLong(saSavedSequence[1])); // savedInitValue 
      MINVALUE = Long.parseLong(saSavedSequence[2]); 
      MAXVALUE = Long.parseLong(saSavedSequence[3]); iMaxLength = Long.toString(MAXVALUE).length(); 
      CACHEVALUE = Long.parseLong(saSavedSequence[4]); 
      lNextSnapshot = Long.parseLong(saSavedSequence[5]); 

      // For sequence number recovery 
      // The rule to determine to continue using SEQ_ID or lNextSnapshot as subsequent sequence number: 
      // If savedInitValue = savedSnapshot, it was saved by ShutdownHook -> use SEQ_ID. 
      // Else if saveInitValue < savedSnapshot, it was saved by periodic Snapshot -> use lNextSnapshot+1. 
      if (saSavedSequence[1].equals(saSavedSequence[5])) { 
       long previousSEQ = Long.parseLong(saSavedSequence[1]); 
       SEQ_ID = new AtomicLong(previousSEQ); 
       lNextSnapshot = roundUpNumberByMultipleValue(previousSEQ,CACHEVALUE); 
      } else { 
       SEQ_ID = new AtomicLong(lNextSnapshot+1); // SEQ_ID starts fresh from lNextSnapshot+!. 
       lNextSnapshot = roundUpNumberByMultipleValue(SEQ_ID.longValue(),CACHEVALUE); 
      } 
     } 

     // Catch invalid values. 
     if (minvalue < 0) { 
      throw new Exception("MINVALUE cannot be less than 0"); 
     } 
     if (maxvalue < 0) { 
      throw new Exception("MAXVALUE cannot be less than 0"); 
     } 
     if (minvalue >= maxvalue) { 
      throw new Exception("MINVALUE cannot be greater than MAXVALUE"); 
     } 
     if (cache >= maxvalue) { 
      throw new Exception("CACHE value cannot be greater than MAXVALUE"); 
     } 

     // Save the next Snapshot. 
     saveToLocal(lNextSnapshot); 
     bInit = true; 
    } 

    /** 
    * Equivalent to Oracle Sequence nextval. 
    * @return String because Next Value is usually left padded with zeros, e.g. "00001". 
    */ 
    public String nextVal() { 
     if (SEQ_ID.longValue() > MAXVALUE) { 
      SEQ_ID.set(MINVALUE); 
      lNextSnapshot = roundUpNumberByMultipleValue(MINVALUE,CACHEVALUE); 
     } 

     if (SEQ_ID.longValue() > lNextSnapshot) { 
      lNextSnapshot = roundUpNumberByMultipleValue(lNextSnapshot,CACHEVALUE); 
      saveToLocal(lNextSnapshot); 
     } 

     return PREFIX.concat(StringUtils.leftPad(Long.toString(SEQ_ID.getAndIncrement()),iMaxLength,"0")); 
    } 

    /** 
    * Store sequence value into the local file. This routine is called either by Snapshot or ShutdownHook routines.<br> 
    * If called by Snapshot, currentCount == Snapshot.<br> 
    * If called by ShutdownHook, currentCount == current SEQ_ID. 
    * @param currentCount - This value is inserted by either Snapshot or ShutdownHook routines. 
    */ 
    private static void saveToLocal (long currentCount) { 
     try (java.io.Writer w = new java.io.BufferedWriter(new java.io.OutputStreamWriter(new java.io.FileOutputStream(FNAME), "utf-8"))) { 
      w.write(PREFIX + "," + SEQ_ID.longValue() + "," + MINVALUE + "," + MAXVALUE + "," + CACHEVALUE + "," + currentCount); 
      w.flush(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    /** 
    * Load the sequence file content into String. 
    * @return 
    */ 
    private String loadToString() { 
     try { 
      return new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(FNAME))); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return ""; 
    } 

    /** 
    * Utility method to round up num to next multiple value. This method is used to calculate the next cache value. 
    * <p> 
    * (Reference: http://stackoverflow.com/questions/18407634/rounding-up-to-the-nearest-hundred) 
    * <p> 
    * <u><b>Sample output:</b></u> 
    * <pre> 
    * System.out.println(roundUpNumberByMultipleValue(9,10)); = 10 
    * System.out.println(roundUpNumberByMultipleValue(10,10)); = 20 
    * System.out.println(roundUpNumberByMultipleValue(19,10)); = 20 
    * System.out.println(roundUpNumberByMultipleValue(100,10)); = 110 
    * System.out.println(roundUpNumberByMultipleValue(109,10)); = 110 
    * System.out.println(roundUpNumberByMultipleValue(110,10)); = 120 
    * System.out.println(roundUpNumberByMultipleValue(119,10)); = 120 
    * </pre> 
    * 
    * @param num Value must be greater and equals to positive integer 1. 
    * @param multiple Value must be greater and equals to positive integer 1. 
    * @return 
    */ 
    private long roundUpNumberByMultipleValue(long num, long multiple) { 
     if (num<=0) num=1; 
     if (multiple<=0) multiple=1; 
     if (num % multiple != 0) { 
      long division = (long) ((num/multiple) + 1); 
      return division * multiple; 
     } else { 
      return num + multiple; 
     } 
    } 

    /** 
    * Main method for testing purpose. 
    * @param args 
    */ 
    public static void main(String[] args) throws Exception { 
     //FixLengthIDSequence(Filename, prefix, initvalue, minvalue, maxvalue, cache) 
     FixLengthIDSequence seq = new FixLengthIDSequence(null,"H",50,1,999,10); 
     for (int i=0; i<12; i++) { 
      System.out.println(seq.nextVal()); 
      Thread.sleep(1000); 
      //if (i==8) { System.exit(0); } 
     } 
    } 

} 

測試代碼,讓程序正常運行。您可以按Ctrl + C來模擬服務器崩潰。下一個序列號將從NextSnapshot + 1繼續。