2

マシン間で一意の9桁の数値IDを作成したいと思います。私は現在これにデータベースシーケンスを使用していますが、データベースシーケンスなしで実行できるかどうか疑問に思っています。シーケンスはX12EDIトランザクションに使用されるため、永久に一意である必要はありません。たぶん24時間だけでもユニークです。

私の唯一のアイデア:

  1. 各サーバーには2桁のサーバー識別子があります。
  2. 各サーバーは、基本的にローカルシーケンスを追跡するファイルを維持します。
  3. id =+<折り返す7桁のシーケンス>

これに関する私の最大の問題は、ハードドライブに障害が発生した場合の対処方法です。どこで中断したのかわかりません。

私の他のすべてのアイデアは、基本的に、一元化されたデータベースシーケンスを再作成することになります。

何かご意見は?

4

6 に答える 6

3

以下

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

ここで、{XX}はマシン番号{dd}は月の日{HHmm}現在の時刻(24時間)、{N}は連番です。

hdのクラッシュには1分以上かかるため、0から再開しても問題はありません。

要件に応じて、{dd}を{ss}に数秒間置き換えることもできます。一意性期間と1分あたりのリクエスト数。

于 2009-07-27T04:33:03.223 に答える
2

GUIDを生成して(一意性を確保)、ある種のハッシュ関数を使用してGUIDを9桁の数字に変換するのはどうですか?
頭のてっぺんから...

于 2009-07-27T04:14:52.807 に答える
2

HDが失敗した場合は、新しい未使用の2桁のサーバー識別子を設定し、番号が一意であることを確認できます(少なくとも24時間)

于 2009-07-27T04:19:55.607 に答える
0

次のバリエーションを使用します。

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

ちょっとした考え。

于 2009-07-27T04:54:15.710 に答える
-1

最近のプロジェクトでは、データベースなしでN桁の長いシーケンス番号を生成するというこの要件にも遭遇しました。

パフォーマンスとソフトウェアのクラッシュ回復について考慮されているため、これは実際には良いインタビューの質問です。興味があればさらに読んでください。

次のコードには次の機能があります。

  1. 各シーケンスにプレフィックスを付けます。

  2. OracleSequenceのようなシーケンスキャッシュ。

  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から続きます。

于 2017-02-20T12:39:45.087 に答える
-2

コールドあなたは次のようなユニークなデータの他のソースの最初の9桁を使用します:

  1. 乱数
  2. システム時刻
  3. 稼働時間

それについて2秒間考えた後、それらのどれもそれ自体で一意ではありませんが、別の回答で提案されているように、ハッシュ関数のシード値として使用できます。

于 2009-07-27T04:15:54.927 に答える