2012-02-24 75 views
2

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

我一直聽到這種做法的主要論點是,如果它們不是按順序生成的,它們可能會使您的索引分段併產生分頁問題(我也聽說這會吹出數據庫的大小,但讓暫且不談)。

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

的問題,它雖然是它創建了一個不符合標準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)是不是最終被迫d和我回到我原來的問題。

我的解決方案是創建自己的定製可逆的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!

回答

0

使用序列生成器,除非您確實需要您的密鑰具有通用的唯一性。

+0

它們需要在分佈式數據庫服務器集羣中是唯一的。我還研究了使用具有順序ID和日期的複合鍵作爲元素的可能性,但是使用UUID看起來更加明顯(因爲其唯一目的是唯一標識實體而不考慮分佈)。 – MacFlecknoe 2012-02-25 04:37:24

+0

http://www.digitilapia.com/custom_uuid_as_primary_key – MacFlecknoe 2012-03-03 06:17:05