无数据库序列生成9位数的id

时间:2009-07-27 04:08:28

标签: x12

我想创建9位数的数字ID,这些数字ID在机器上是唯一的。我目前正在使用数据库序列,但我想知道是否可以在没有数据库的情况下完成。这些序列将用于X12 EDI事务,因此它们不必永远是唯一的。甚至可能只有24小时的独特。

我唯一的想法:

  1. 每台服务器都有一个2位服务器标识符。
  2. 每个服务器都维护一个基本上跟踪本地序列的文件。
  3. id = +<包裹>
  4. 的7位数序列

    我最大的问题是如果硬盘驱动器发生故障该怎么办。我不知道它从何处停止。

    我的所有其他想法最终都会重新创建一个集中的数据库序列。

    有什么想法吗?

6 个答案:

答案 0 :(得分:3)

以下

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

其中{XX}是机器编号{dd}是当月{HHmm}当前时间(24小时)和{N}序号。

高清崩溃需要一分多钟,所以再次从0开始不是问题。

您还可以将{dd}替换为{ss}秒,具体取决于要求。唯一性周期与每分钟请求数。

答案 1 :(得分:2)

如何生成GUID(确保唯一性),然后使用某种哈希函数将GUID转换为9位数字?
就在我的头顶......

答案 2 :(得分:2)

如果HD失败,您只需设置新的和未使用的2位服务器标识符,并确保该号码是唯一的(至少24小时)

答案 3 :(得分:0)

使用以下变体:

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

只是一个想法。

答案 4 :(得分:-1)

在我最近的项目中,我也遇到了这个要求,在没有任何数据库的情况下生成N位长的序列号。

这实际上是一个很好的采访问题,因为有关于性能和软件崩溃恢复的考虑。 Further Reading如果有兴趣的话。

以下代码具有以下功能:

  1. 使用前缀为每个序列添加前缀。

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

  3. 最重要的是,有恢复逻辑可以从软件崩溃中恢复序列。

  4. 附上完整的实施:

    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继续。

答案 5 :(得分:-2)

冷你使用其他一些独特数据来源的前9位数字,如:

  1. 随机数
  2. 系统时间
  3. 正常运行时间
  4. 对它进行了两秒钟的研究,其中没有一个是独一无二的,但你可以将它们用作散列函数的种子值,如另一个答案所示。