簡體   English   中英

自定義UUID作為主鍵

[英]Custom UUID as primary key

我一直在閱讀使用UUID作為數據庫中的主鍵的優缺點。

我一直聽到反對這種做法的主要論據是,如果不按順序生成它們,它們可能會使您的索引碎片化,並導致分頁出現問題(我也聽說這會炸毀數據庫的大小,但請您不要理會現在)。

MSSQL Server允許您使用自定義方法(例如CREATE TABLE MyUniqueTable(UniqueColumn UNIQUEIDENTIFIER DEFAULT NEWSEQUENTIALID())在數據庫中創建順序UUID。

但是,它的問題是它創建了一個不符合標准的UUID,該UUID顯然不是順序的。 我對格式進行了反向工程,並將其封裝在一個生成器類中以供使用或研究:

/**
 * <p>
 * Reverse engineering effort to replicate how SQL Server creates ordered 
 * UUIDs so that we may construct them within the application. The builder will 
 * only accept version 1 and version 14 (Microsoft specific) uuid objects as a 
 * seed.
 * </p>
 * <p>
 * The algorithm is reversible so that a version 1 uuid may be created from a version
 * 14 uuid and vice versa.
 * </p>
 * @author Michael Lambert
 *
 */
public static class MsSqlOrderedUuidBuilder {

    private static final TimeBasedGenerator generator = Generators.timeBasedGenerator();

    private final UUID uuid;

    public MsSqlOrderedUuidBuilder(UUID uuid) {

        if(uuid.version() != 1 && uuid.version() != 14) {
            throw new IllegalArgumentException(String.format("UUID is not a version 1 UUID (version is %d)", uuid.version()));
        }
        this.uuid = uuid;
    }

    public MsSqlOrderedUuidBuilder() {
        this(generator.generate());
    }

    private long getMostSignificantBits() {

        ByteBuffer buffer = ByteBuffer.wrap(new byte[8]);

        buffer.putLong(uuid.getMostSignificantBits());
        buffer.rewind();

        byte[] timeLow = new byte[4];
        buffer.get(timeLow);

        byte[] timeMid = new byte[2];
        buffer.get(timeMid);

        byte[] timeHigh = new byte[2]; // time_high and version
        buffer.get(timeHigh);

        buffer.clear();

        buffer.order(buffer.order().equals(ByteOrder.LITTLE_ENDIAN) ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);

        buffer.put(timeHigh);
        buffer.put(timeMid);
        buffer.put(timeLow);

        return buffer.getLong(0);
    }

    private long getLeastSignificantBits() {
        return uuid.getLeastSignificantBits();
    }

    public UUID build() {
        return new UUID(getMostSignificantBits(), getLeastSignificantBits());
    }
}

如果我嘗試使用此類將結果UUID存儲在其他數據庫中(我也必須寫到MySQL),最終並不會受到命令,這又回到了我原來的問題。

我的解決方案是創建自己的可逆自定義UUID,當序列化為字節數組時,該UUID將按順序排序:

/**
 * <p>
 * Creates a custom UUID type with sequential bytes. The builder must be seeded with a version 1 uuid and the
 * algorithm is reversible.
 * </p>
 * @author Michael Lambert
 *
 */
public static class SequentialUuidBuilder {

    private static final TimeBasedGenerator generator = Generators.timeBasedGenerator();

    private final UUID uuid;

    public SequentialUuidBuilder(UUID uuid) {

        if(uuid.version() != 1 && uuid.version() != 13) {
            throw new IllegalArgumentException(String.format("UUID is not a version 1 UUID (version is %d)", uuid.version()));
        }
        this.uuid = uuid;
    }

    public SequentialUuidBuilder() {
        this(generator.generate());
    }

    private long getVersion13MostSignificantBits() {

        if(uuid.version() == 1) {

            // System.out.println(String.format("original: %x", version1.getMostSignificantBits()));
            //
            // System.out.println(String.format("lowa %x", timeLowA));
            //
            // 0xAAAAA00000000000L
            // 0x0000000AAAAA0000L
            //
            long timeLowPartA = (uuid.getMostSignificantBits() & 0xFFFFF00000000000L) >>> 28;
            //
            // 0x00000BBB00000000L
            // 0x0000000000000BBBL
            //
            long timeLowPartB = (uuid.getMostSignificantBits() & 0x00000FFF00000000L) >>> 32;
            //
            // System.out.println(String.format("lowb %x", timeLowB));
            //
            // 0x00000000MMMM0000L
            // 0x000MMMM000000000L
            //
            long timeMid = (uuid.getMostSignificantBits() &  0x00000000FFFF0000L) << 20;
            //
            // System.out.println(String.format("med %x", (timeMid)));
            //
            // 0x0000000000000HHHL
            // 0xHHH0000000000000L
            //
            long timeHigh = (uuid.getMostSignificantBits() & 0x0000000000000FFFL) << 52;
            //
            // System.out.println(String.format("high %x", timeHigh));
            //
            // 0x000000000000V000L
            // 0x000000000000V000L
            //
            // long version = (version1.getMostSignificantBits() &  0x000000000000F000L);
            //
            // System.out.println(String.format("version %x", version));
            //
            // 0x0000000AAAAA0000L
            // 0x0000000000000BBBL
            // 0x000MMMM000000000L
            // 0xHHH0000000000000L
            // 0x000000000000V000L <-- we don't change where the version is stored because we want to respect that part of the spec
            // ____________________
            // 0xHHHMMMMAAAAAVBBBL
            //
            long ordered = timeLowPartA | timeLowPartB | timeMid | timeHigh | 0x000000000000D000L; // custom version

            return ordered;
        }
        return 0;
    }

    public long getVersion1MostSignificantBits() {
        //
        // 0xHHHMMMMAAAAAVBBBL
        //
        long timeLowPartA = (uuid.getMostSignificantBits() & 0x0000000FFFFF0000L) << 28;
        long timeLowPartB = (uuid.getMostSignificantBits() & 0x0000000000000FFFL) << 32;
        long timeMid = (uuid.getMostSignificantBits() &  0x000FFFF000000000L) >> 20;
        long timeHigh = (uuid.getMostSignificantBits() & 0xFFF0000000000000L) >> 52;
        //
        // 0xAAAAA00000000000L
        // 0x00000000MMMM0000L
        // 0x00000BBB00000000L
        // 0x0000000000000HHHL
        // 0x000000000000V000L
        // ___________________
        // 0xAAAAABBBMMMMVHHHL
        //
        long bits = timeLowPartA | timeLowPartB | timeMid | timeHigh | 0x0000000000001000L; // reinstate version

        return bits;
    }

    private long getMostSignificantBits() {
        return (uuid.version() == 13) ? getVersion1MostSignificantBits() : getVersion13MostSignificantBits();
    }

    private long getLeastSignificantBits() {
        return uuid.getLeastSignificantBits();
    }

    public UUID build() {
        return new UUID(uuid.version() == 13 ? getVersion1MostSignificantBits() : getMostSignificantBits(), getLeastSignificantBits());
    }
}

我的問題是:這是可以接受的做法嗎? 我可以使用BINARY(16)來存儲主鍵嗎,可以通過這種方式使用自定義標識符嗎?

謝謝大家。 Vive la Stackoverflow!

除非確實需要密鑰具有唯一性,否則請使用序列生成器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM