最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java語(yǔ)言自行實(shí)現(xiàn)ULID過(guò)程底層原理詳解

 更新時(shí)間:2022年10月17日 09:58:09   作者:Throwable  
這篇文章主要為大家介紹了java語(yǔ)言自行實(shí)現(xiàn)ULID過(guò)程底層原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前提

最近發(fā)現(xiàn)各個(gè)頻道推薦了很多ULID相關(guān)文章,這里對(duì)ULID的規(guī)范文件進(jìn)行解讀,并且基于Java語(yǔ)言自行實(shí)現(xiàn)ULID,通過(guò)此實(shí)現(xiàn)過(guò)程展示ULID的底層原理。

ULID出現(xiàn)的背景

ULID全稱(chēng)是Universally Unique Lexicographically Sortable Identifier,直譯過(guò)來(lái)就是通用唯一按字典排序的標(biāo)識(shí)符,它的原始倉(cāng)庫(kù)是https://github.com/ulid/javascript,該項(xiàng)目由前端開(kāi)發(fā)者alizain發(fā)起,基于JavaScript語(yǔ)言編寫(xiě)。從項(xiàng)目中的commit歷史來(lái)看已經(jīng)超過(guò)了5年,理論上得到充分的實(shí)踐驗(yàn)證。ULID出現(xiàn)的原因是一些開(kāi)發(fā)者認(rèn)為主流的UUID方案在許多場(chǎng)景下可能不是最優(yōu)的,存在下面的原因:

  • UUID不是128 bit隨機(jī)編碼(由128 bit隨機(jī)數(shù)通過(guò)編碼生成字符串)的最高效實(shí)現(xiàn)方式
  • UUIDv1/v2實(shí)現(xiàn)在許多環(huán)境中是不切實(shí)際的,因?yàn)檫@兩個(gè)版本的的實(shí)現(xiàn)需要訪問(wèn)唯一的、穩(wěn)定的MAC地址
  • UUIDv3/v5實(shí)現(xiàn)需要唯一的種子,并且產(chǎn)生隨機(jī)分布的ID,這可能會(huì)導(dǎo)致在許多數(shù)據(jù)結(jié)構(gòu)中出現(xiàn)碎片
  • UUIDv4除了隨機(jī)性之外不需要提供其他信息,隨機(jī)性可能會(huì)在許多數(shù)據(jù)結(jié)構(gòu)中導(dǎo)致碎片

這里概括一下就是:UUIDv1/v2實(shí)現(xiàn)依賴(lài)唯一穩(wěn)定MAC地址不現(xiàn)實(shí),v3/v4/v5實(shí)現(xiàn)因?yàn)殡S機(jī)性產(chǎn)生的ID會(huì)"碎片化"。

基于此提出了ULID,它用起來(lái)像這樣:

ulid() // 01ARZ3NDEKTSV4RRFFQ69G5FAV

ULID的特點(diǎn)如下:

  • 設(shè)計(jì)為128 bit大小,與UUID兼容
  • 每毫秒生成1.21e+24個(gè)唯一的ULID(高性能)
  • 按字典順序(字母順序)排序
  • 標(biāo)準(zhǔn)編碼為26個(gè)字符的字符串,而不是像UUID那樣需要36個(gè)字符
  • 使用Crockfordbase32算法來(lái)提高效率和可讀性(每個(gè)字符5 bit
  • 不區(qū)分大小寫(xiě)
  • 沒(méi)有特殊字符串(URL安全,不需要進(jìn)行二次URL編碼)
  • 單調(diào)排序(正確地檢測(cè)并處理相同的毫秒,所謂單調(diào)性,就是毫秒數(shù)相同的情況下,能夠確保新的ULID隨機(jī)部分的在最低有效位上加1位)

ULID規(guī)范

下面的ULID規(guī)范在ULID/javascript類(lèi)庫(kù)中實(shí)現(xiàn),此二進(jìn)制格式目前沒(méi)有在JavaScript中實(shí)現(xiàn):

 01AN4Z07BY      79KA1307SR9X4MV3
|----------|    |----------------|
 Timestamp          Randomness
   48bits             80bits

組成

時(shí)間戳(Timestamp)

  • 占據(jù)48 bit(high)
  • 本質(zhì)是UNIX-time,單位為毫秒
  • 直到公元10889年才會(huì)用完

隨機(jī)數(shù)(Randomness)

  • 占據(jù)80 bit(low)
  • 如果可能的話,使用加密安全的隨機(jī)源

排序

"最左邊"的字符必須排在最前面,"最右邊"的字符排在最后(詞法順序,或者俗稱(chēng)的字典排序),并且所有字符必須使用默認(rèn)的ASCII字符集。在相同的毫秒(時(shí)間戳)內(nèi),無(wú)法保證排序順序。

規(guī)范的表示形式

ULID規(guī)范的字符串表示形式如下:

ttttttttttrrrrrrrrrrrrrrrr
where
t is Timestamp (10 characters)
r is Randomness (16 characters)

也就是:

  • 時(shí)間戳占據(jù)高(左邊)10個(gè)(編碼后的)字符
  • 隨機(jī)數(shù)占據(jù)低(右邊)16個(gè)(編碼后的)字符

ULID規(guī)范的字符串表示形式的長(zhǎng)度是確定的,共占據(jù)26個(gè)字符。

編碼

使用Crockford Base32編碼算法,這個(gè)編碼算法的字母表如下:

0123456789ABCDEFGHJKMNPQRSTVWXYZ

該字母表排除了I、 L、O、U字母,目的是避免混淆和濫用。此算法實(shí)現(xiàn)不難,它的官網(wǎng)有詳細(xì)的算法說(shuō)明(https://www.crockford.com/base32.html):

單調(diào)性

(如果啟用了單調(diào)性這個(gè)特性為前提下)當(dāng)在相同的毫秒內(nèi)生成多個(gè)ULID時(shí),可以保證排序的順序。也就是說(shuō),如果檢測(cè)到相同的毫秒,則隨機(jī)分量在最低有效位上加1位(帶進(jìn)位)。例如:

monotonicUlid()  // 01BX5ZZKBKACTAV9WEVGEMMVRZ
monotonicUlid()  // 01BX5ZZKBKACTAV9WEVGEMMVS0

溢出錯(cuò)誤處理

從技術(shù)實(shí)現(xiàn)上來(lái)看,26個(gè)字符的Base32編碼字符串可以包含130 bit信息,而ULID只包含128 bit信息,所以該編碼算法是能完全滿(mǎn)足ULID的需要?;?code>Base32編碼能夠生成的最大的合法ULID其實(shí)就是7ZZZZZZZZZZZZZZZZZZZZZZZZZ,并且使用的時(shí)間戳為epoch time281474976710655或者說(shuō)2 ^ 48 - 1。對(duì)于任何對(duì)大于此值的ULID進(jìn)行解碼或編碼的嘗試都應(yīng)該被所有實(shí)現(xiàn)拒絕,以防止溢出錯(cuò)誤。

二進(jìn)制布局

二進(jìn)制布局的多個(gè)部分被編碼為16 byte,每個(gè)部分都以最高字節(jié)優(yōu)先(網(wǎng)絡(luò)字節(jié)序,也就是big-endian)進(jìn)行編碼,布局如下:

0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      32_bit_uint_time_high                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     16_bit_uint_time_low      |       16_bit_uint_random      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       32_bit_uint_random                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       32_bit_uint_random                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

ULID使用

對(duì)于script標(biāo)簽引用:

<script src="https://unpkg.com/ulid@latest/dist/index.umd.js"></script>
<script>
    ULID.ulid()
</script>

NPM安裝:

npm install --save ulid

TypeScript, ES6+, Babel, Webpack, Rollup等等下使用:

// import
import { ulid } from 'ulid'
ulid()
// CommonJS env
const ULID = require('ulid')
ULID.ulid()

后端Maven項(xiàng)目中使用需要引入依賴(lài),這里選用ulid-creator實(shí)現(xiàn):

<dependency>
  <groupId>com.github.f4b6a3</groupId>
  <artifactId>ulid-creator</artifactId>
  <version>5.0.2</version>
</dependency>

然后調(diào)用UlidCreator#getUlid()系列方法:

// 常規(guī)
Ulid ulid = UlidCreator.getUlid();
// 單調(diào)排序
Ulid ulid = UlidCreator.getMonotonicUlid();

實(shí)現(xiàn)ULID

前面已經(jīng)提到ULID的規(guī)范,其實(shí)具體實(shí)現(xiàn)ULID就是對(duì)著規(guī)范里面的每一個(gè)小節(jié)進(jìn)行編碼實(shí)現(xiàn)。先看二進(jìn)制布局,由于使用128 bit去存儲(chǔ),可以借鑒UUID那樣,使用兩個(gè)long類(lèi)似的成員變量存儲(chǔ)ULID的信息,看起來(lái)像這樣:

public final class ULID {
    /*
     * The most significant 64 bits of this ULID.
     *
     */
    private final long msb;
    /*
     * The least significant 64 bits of this ULID.
     *
     */
    private final long lsb;
    public ULID(long msb, long lsb) {
        this.msb = msb;
        this.lsb = lsb;
    }
}

按照ULID的組成來(lái)看,可以提供一個(gè)入?yún)闀r(shí)間戳和隨機(jī)數(shù)字節(jié)數(shù)組的構(gòu)造:

public ULID(long timestamp, byte[] randomness) {
    if ((timestamp & TIMESTAMP_MASK) != 0) {
        throw new IllegalArgumentException("Invalid timestamp");
    }
    if (Objects.isNull(randomness) || RANDOMNESS_BYTE_LEN != randomness.length) {
        throw new IllegalArgumentException("Invalid randomness");
    }
    long msb = 0;
    long lsb = 0;
    // 時(shí)間戳左移16位,低位補(bǔ)零準(zhǔn)備填入部分隨機(jī)數(shù)位,即16_bit_uint_random
    msb |= timestamp << 16;
    // randomness[0]左移0位填充到16_bit_uint_random的高8位,randomness[1]填充到16_bit_uint_random的低8位
    msb |= (long) (randomness[0x0] & 0xff) << 8;
    // randomness[1]填充到16_bit_uint_random的低8位
    msb |= randomness[0x1] & 0xff;
    // randomness[2] ~ randomness[9]填充到剩余的bit_uint_random中,要左移相應(yīng)的位
    lsb |= (long) (randomness[0x2] & 0xff) << 56;
    lsb |= (long) (randomness[0x3] & 0xff) << 48;
    lsb |= (long) (randomness[0x4] & 0xff) << 40;
    lsb |= (long) (randomness[0x5] & 0xff) << 32;
    lsb |= (long) (randomness[0x6] & 0xff) << 24;
    lsb |= (long) (randomness[0x7] & 0xff) << 16;
    lsb |= (long) (randomness[0x8] & 0xff) << 8;
    lsb |= (randomness[0x9] & 0xff);
    this.msb = msb;
    this.lsb = lsb;
}

這是完全按照規(guī)范的二進(jìn)制布局編寫(xiě)代碼,可以像UUID的構(gòu)造那樣精簡(jiǎn)一下:

long msb = 0;
long lsb = 0;
byte[] data = new byte[16];
byte[] ts = ByteBuffer.allocate(8).putLong(0, timestamp << 16).array();
System.arraycopy(ts, 0, data, 0, 6);
System.arraycopy(randomness, 0, data, 6, 10);
for (int i = 0; i < 8; i++)
    msb = (msb << 8) | (data[i] & 0xff);
for (int i = 8; i < 16; i++)
    lsb = (lsb << 8) | (data[i] & 0xff);

接著可以簡(jiǎn)單添加下面幾個(gè)方法:

public long getMostSignificantBits() {
    return this.msb;
}
public long getLeastSignificantBits() {
    return this.lsb;
}
// 靜態(tài)工廠方法,由UUID實(shí)例生成ULID實(shí)例
public static ULID fromUUID(UUID uuid) {
    return new ULID(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
}
// 實(shí)例方法,當(dāng)前ULID實(shí)例轉(zhuǎn)換為UUID實(shí)例
public UUID toUUID() {
    return new UUID(this.msb, this.lsb);
}

接著需要覆蓋toString()方法,這是ULID的核心方法,需要通過(guò)Crockford Base32編碼生成規(guī)范的字符串表示形式。由于128 bit要映射為26 char,這里可以考慮分三段進(jìn)行映射,也就是48 bit時(shí)間戳映射為10 char,剩下的兩部分隨機(jī)數(shù)分別做40 bit8 char的映射,加起來(lái)就是26 char

 |----------|      |----------------|
  Timestamp     Randomness[split to 2 part]
48bit => 10char    80bit => 16char

編寫(xiě)方法:

/**
  * Default alphabet of ULID
  */
private static final char[] DEFAULT_ALPHABET = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
        'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'};
/**
  * Default alphabet mask
  */
private static final int DEFAULT_ALPHABET_MASK = 0b11111;
/**
  * Character num of ULID
  */
private static final int ULID_CHAR_LEN = 0x1a;
@Override
public String toString() {
    return toCanonicalString(DEFAULT_ALPHABET);
}
public String toCanonicalString(char[] alphabet) {
    char[] chars = new char[ULID_CHAR_LEN];
    long timestamp = this.msb >> 16;
    // 第一部分隨機(jī)數(shù)取msb的低16位+lsb的高24位,這里(msb & 0xffff) << 24作為第一部分隨機(jī)數(shù)的高16位,所以要左移24位
    long randMost = ((this.msb & 0xffffL) << 24) | (this.lsb >>> 40);
    // 第二部分隨機(jī)數(shù)取lsb的低40位,0xffffffffffL是2^40-1
    long randLeast = (this.lsb & 0xffffffffffL);
    // 接著每個(gè)部分的偏移量和DEFAULT_ALPHABET_MASK(31)做一次或運(yùn)算就行,就是char[index] = alphabet[(part >> (step * index)) & 31]
    chars[0x00] = alphabet[(int) (timestamp >>> 45 & DEFAULT_ALPHABET_MASK)];
    chars[0x01] = alphabet[(int) (timestamp >>> 40 & DEFAULT_ALPHABET_MASK)];
    chars[0x02] = alphabet[(int) (timestamp >>> 35 & DEFAULT_ALPHABET_MASK)];
    chars[0x03] = alphabet[(int) (timestamp >>> 30 & DEFAULT_ALPHABET_MASK)];
    chars[0x04] = alphabet[(int) (timestamp >>> 25 & DEFAULT_ALPHABET_MASK)];
    chars[0x05] = alphabet[(int) (timestamp >>> 20 & DEFAULT_ALPHABET_MASK)];
    chars[0x06] = alphabet[(int) (timestamp >>> 15 & DEFAULT_ALPHABET_MASK)];
    chars[0x07] = alphabet[(int) (timestamp >>> 10 & DEFAULT_ALPHABET_MASK)];
    chars[0x08] = alphabet[(int) (timestamp >>> 5 & DEFAULT_ALPHABET_MASK)];
    chars[0x09] = alphabet[(int) (timestamp & DEFAULT_ALPHABET_MASK)];
    chars[0x0a] = alphabet[(int) (randMost >>> 35 & DEFAULT_ALPHABET_MASK)];
    chars[0x0b] = alphabet[(int) (randMost >>> 30 & DEFAULT_ALPHABET_MASK)];
    chars[0x0c] = alphabet[(int) (randMost >>> 25 & DEFAULT_ALPHABET_MASK)];
    chars[0x0d] = alphabet[(int) (randMost >>> 20 & DEFAULT_ALPHABET_MASK)];
    chars[0x0e] = alphabet[(int) (randMost >>> 15 & DEFAULT_ALPHABET_MASK)];
    chars[0x0f] = alphabet[(int) (randMost >>> 10 & DEFAULT_ALPHABET_MASK)];
    chars[0x10] = alphabet[(int) (randMost >>> 5 & DEFAULT_ALPHABET_MASK)];
    chars[0x11] = alphabet[(int) (randMost & DEFAULT_ALPHABET_MASK)];
    chars[0x12] = alphabet[(int) (randLeast >>> 35 & DEFAULT_ALPHABET_MASK)];
    chars[0x13] = alphabet[(int) (randLeast >>> 30 & DEFAULT_ALPHABET_MASK)];
    chars[0x14] = alphabet[(int) (randLeast >>> 25 & DEFAULT_ALPHABET_MASK)];
    chars[0x15] = alphabet[(int) (randLeast >>> 20 & DEFAULT_ALPHABET_MASK)];
    chars[0x16] = alphabet[(int) (randLeast >>> 15 & DEFAULT_ALPHABET_MASK)];
    chars[0x17] = alphabet[(int) (randLeast >>> 10 & DEFAULT_ALPHABET_MASK)];
    chars[0x18] = alphabet[(int) (randLeast >>> 5 & DEFAULT_ALPHABET_MASK)];
    chars[0x19] = alphabet[(int) (randLeast & DEFAULT_ALPHABET_MASK)];
    return new String(chars);
}

上面的方法toCanonicalString()看起來(lái)很"臃腫",但是能保證性能比較高。接著添加常用的工廠方法:

public static ULID ulid() {
    return ulid(System::currentTimeMillis, len -> {
        byte[] bytes = new byte[len];
        ThreadLocalRandom.current().nextBytes(bytes);
        return bytes;
    });
}
public static ULID ulid(Supplier<Long> timestampProvider,
                        IntFunction<byte[]> randomnessProvider) {
    return new ULID(timestampProvider.get(), randomnessProvider.apply(RANDOMNESS_BYTE_LEN));
}

默認(rèn)使用ThreadLocalRandom生成隨機(jī)數(shù),如果是JDK17以上,還可以選用更高性能的新型PRNG實(shí)現(xiàn),對(duì)應(yīng)接口是RandomGenerator,默認(rèn)實(shí)現(xiàn)是L32X64MixRandom。編寫(xiě)一個(gè)main方法運(yùn)行一下:

public static void main(String[] args) {
    System.out.println(ULID.ulid());
}
// 某次執(zhí)行結(jié)果
01GFGGMBFGB5WKXBN7S84ATRDG

最后實(shí)現(xiàn)"單調(diào)遞增"的ULID構(gòu)造,先提供一個(gè)"增長(zhǎng)"方法:

/**
  * The least significant 64 bits increase overflow, 0xffffffffffffffffL + 1
  */
private static final long OVERFLOW = 0x0000000000000000L;
public ULID increment() {
    long msb = this.msb;
    long lsb = this.lsb + 1;
    if (lsb == OVERFLOW) {
        msb += 1;
    }
    return new ULID(msb, lsb);
}

其實(shí)就是低位加1,溢出后高位加1。接著添加一個(gè)靜態(tài)內(nèi)部子類(lèi)和響應(yīng)方法如下:

// 構(gòu)造函數(shù)
public ULID(ULID other) {
    this.msb = other.msb;
    this.lsb = other.lsb;
}
public static byte[] defaultRandomBytes(int len) {
    byte[] bytes = new byte[len];
    ThreadLocalRandom.current().nextBytes(bytes);
    return bytes;
}
public static MonotonicULIDSpi monotonicUlid() {
    return monotonicUlid(System::currentTimeMillis, ULID::defaultRandomBytes);
}
public static MonotonicULIDSpi monotonicUlid(Supplier&lt;Long&gt; timestampProvider,
                                              IntFunction&lt;byte[]&gt; randomnessProvider) {
    return new MonotonicULID(timestampProvider, randomnessProvider, timestampProvider.get(),
            randomnessProvider.apply(RANDOMNESS_BYTE_LEN));
}
// @SPI MonotonicULID
public interface MonotonicULIDSpi {
  ULID next();
}
private static class MonotonicULID extends ULID implements MonotonicULIDSpi {
    @Serial
    private static final long serialVersionUID = -9158161806889605101L;
    private volatile ULID lastULID;
    private final Supplier&lt;Long&gt; timestampProvider;
    private final IntFunction&lt;byte[]&gt; randomnessProvider;
    public MonotonicULID(Supplier&lt;Long&gt; timestampProvider,
                          IntFunction&lt;byte[]&gt; randomnessProvider,
                          long timestamp,
                          byte[] randomness) {
        super(timestamp, randomness);
        this.timestampProvider = timestampProvider;
        this.randomnessProvider = randomnessProvider;
        this.lastULID = new ULID(timestamp, randomness);
    }
    // 這里沒(méi)設(shè)計(jì)好,子類(lèi)緩存了上一個(gè)節(jié)點(diǎn),需要重寫(xiě)一下increment方法,父類(lèi)可以移除此方法 
    @Override
    public ULID increment() {
        long newMsb = lastULID.msb;
        long newLsb = lastULID.lsb + 1;
        if (newLsb == OVERFLOW) {
            newMsb += 1;
        }
        return new ULID(newMsb, newLsb);
    }
    @Override
    public synchronized ULID next() {
        long lastTimestamp = lastULID.getTimestamp();
        long timestamp = getTimestamp();
        // 這里做了一個(gè)恒為true的判斷,后面再研讀其他代碼進(jìn)行修改
        if (lastTimestamp &gt;= timestamp || timestamp - lastTimestamp &lt;= 1000) {
            this.lastULID = this.increment();
        } else {
            this.lastULID = new ULID(timestampProvider.get(), randomnessProvider.apply(RANDOMNESS_BYTE_LEN));
        }
        return new ULID(this.lastULID);
    }
}

關(guān)于上一個(gè)ULID和下一個(gè)ULID之間的時(shí)間戳判斷,這里從規(guī)范文件沒(méi)看出細(xì)節(jié)實(shí)現(xiàn),先簡(jiǎn)單做一個(gè)永遠(yuǎn)為true的分支判斷,后面再深入研究然后修改。使用方式如下:

public static void main(String[] args) {
    MonotonicULIDSpi spi = ULID.monotonicUlid();
    System.out.println(spi.next());
    System.out.println(spi.next());
    System.out.println(spi.next());
    System.out.println(spi.next());
}
// 某次運(yùn)行輸出
01GFGASXXQXD5ZJ26PKSCFGNPF
01GFGASXXQXD5ZJ26PKSCFGNPG
01GFGASXXQXD5ZJ26PKSCFGNPH
01GFGASXXQXD5ZJ26PKSCFGNPJ

這里為了更加靈活,沒(méi)有采用全局靜態(tài)屬性緩存上一個(gè)ULID實(shí)例,而是簡(jiǎn)單使用繼承方式實(shí)現(xiàn)。

ULID性能評(píng)估

引入JMH做了一個(gè)簡(jiǎn)單的性能測(cè)試,代碼如下:

@Fork(1)
@Threads(10)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 1, time = 1)
@Measurement(iterations = 5, time = 3)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class BenchmarkRunner {
    private static ULID.MonotonicULIDSpi SPI;
    @Setup
    public void setup() {
        SPI = ULID.monotonicUlid();
    }
    @Benchmark
    public UUID createUUID() {
        return UUID.randomUUID();
    }
    @Benchmark
    public String createUUIDToString() {
        return UUID.randomUUID().toString();
    }
    @Benchmark
    public ULID createULID() {
        return ULID.ulid();
    }
    @Benchmark
    public String createULIDToString() {
        return ULID.ulid().toString();
    }
    @Benchmark
    public ULID createMonotonicULID() {
        return SPI.next();
    }
    @Benchmark
    public String createMonotonicULIDToString() {
        return SPI.next().toString();
    }
    public static void main(String[] args) throws Exception {
        new Runner(new OptionsBuilder().build()).run();
    }
}

某次測(cè)試報(bào)告如下(開(kāi)發(fā)環(huán)境Intel 6700K 4C8T 32G,使用OpenJDK-19):

Benchmark                                     Mode  Cnt       Score       Error   Units
BenchmarkRunner.createMonotonicULID          thrpt    5   20335.118 ±  1656.772  ops/ms
BenchmarkRunner.createMonotonicULIDToString  thrpt    5   13091.975 ±  1207.403  ops/ms
BenchmarkRunner.createULID                   thrpt    5  152574.703 ± 23740.021  ops/ms
BenchmarkRunner.createULIDToString           thrpt    5   51559.800 ±  3608.085  ops/ms
BenchmarkRunner.createUUID                   thrpt    5     819.890 ±    15.508  ops/ms
BenchmarkRunner.createUUIDToString           thrpt    5     786.072 ±    44.770  ops/ms

小結(jié)

本文就ULID的規(guī)范進(jìn)行解讀,通過(guò)規(guī)范和參考現(xiàn)有類(lèi)庫(kù)進(jìn)行ULIDJava實(shí)現(xiàn)。ULID適用于一些"排序ID"生成或者需要"單調(diào)ID"生成的場(chǎng)景,可以考慮用于數(shù)據(jù)庫(kù)鍵設(shè)計(jì)、順序號(hào)設(shè)計(jì)等等場(chǎng)景。從實(shí)現(xiàn)上看它性能會(huì)優(yōu)于UUID(特別是單調(diào)ULID,因?yàn)椴恍枰匦芦@取隨機(jī)數(shù)部分,吞吐量會(huì)提升一個(gè)數(shù)量級(jí))。

Demo項(xiàng)目倉(cāng)庫(kù):

  • framework-mesh/ulid4jhttps://github.com/zjcscut/framework-mesh/tree/master/ulid4j

參考資料:

crockford-base32

ulid-creator

以上就是java語(yǔ)言自行實(shí)現(xiàn)ULID過(guò)程底層原理詳解的詳細(xì)內(nèi)容,更多關(guān)于java ULID底層原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • RocketMQ消息隊(duì)列實(shí)現(xiàn)隨機(jī)消息發(fā)送當(dāng)做七夕禮物

    RocketMQ消息隊(duì)列實(shí)現(xiàn)隨機(jī)消息發(fā)送當(dāng)做七夕禮物

    這篇文章主要為大家介紹了RocketMQ消息隊(duì)列實(shí)現(xiàn)隨機(jī)消息發(fā)送當(dāng)做七夕禮物,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • redisson 實(shí)現(xiàn)分布式鎖的源碼解析

    redisson 實(shí)現(xiàn)分布式鎖的源碼解析

    這篇文章主要介紹了redisson 實(shí)現(xiàn)分布式鎖的源碼解析,通過(guò)模擬一個(gè)商品秒殺的場(chǎng)景結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • Java BufferedWriter BufferedReader 源碼分析

    Java BufferedWriter BufferedReader 源碼分析

    本文是關(guān)于Java BufferedWriter ,BufferedReader 簡(jiǎn)介、分析源碼 對(duì)Java IO 流深入了解,希望看到的同學(xué)對(duì)你有所幫助
    2016-07-07
  • Mybatis映射文件規(guī)則實(shí)例詳解

    Mybatis映射文件規(guī)則實(shí)例詳解

    在映射文件當(dāng)中,mapper元素是映射文件的根元素,其他的標(biāo)簽都是它的子元素,下面這篇文章主要給大家介紹了關(guān)于Mybatis映射文件規(guī)則的相關(guān)資料,需要的朋友可以參考下
    2022-04-04
  • SpringBoot使用OpenCV示例總結(jié)

    SpringBoot使用OpenCV示例總結(jié)

    這篇文章主要介紹了SpringBoot使用OpenCV示例總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Springboot的自動(dòng)配置是什么及注意事項(xiàng)

    Springboot的自動(dòng)配置是什么及注意事項(xiàng)

    SpringBoot的自動(dòng)配置(Auto-configuration)是指框架根據(jù)項(xiàng)目的依賴(lài)和應(yīng)用程序的環(huán)境自動(dòng)配置Spring應(yīng)用上下文中的Bean和組件,目的是簡(jiǎn)化開(kāi)發(fā)者的配置工作,本文介紹Springboot的自動(dòng)配置是什么及注意事項(xiàng),感興趣的朋友一起看看吧
    2025-03-03
  • Java實(shí)現(xiàn)插入排序

    Java實(shí)現(xiàn)插入排序

    這篇文章主要介紹了Java實(shí)現(xiàn)插入排序,把一列數(shù)組按從小到大或從大到小排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Java類(lèi)的訪問(wèn)權(quán)限關(guān)鍵字用法說(shuō)明

    Java類(lèi)的訪問(wèn)權(quán)限關(guān)鍵字用法說(shuō)明

    這篇文章主要介紹了Java類(lèi)的訪問(wèn)權(quán)限關(guān)鍵字用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • java分割日期時(shí)間段代碼

    java分割日期時(shí)間段代碼

    這篇文章主要為大家詳細(xì)介紹了java分割日期時(shí)間段代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Java基于Dijkstra算法實(shí)現(xiàn)校園導(dǎo)游程序

    Java基于Dijkstra算法實(shí)現(xiàn)校園導(dǎo)游程序

    這篇文章主要為大家詳細(xì)介紹了Java基于Dijkstra算法實(shí)現(xiàn)校園導(dǎo)游程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論

昌乐县| 龙陵县| 奉化市| 双城市| 勐海县| 江门市| 大邑县| 龙川县| 上杭县| 江城| 库尔勒市| 北川| 南阳市| 宜春市| 东城区| 华阴市| 罗平县| 漳州市| 红原县| 滦南县| 古丈县| 行唐县| 卓资县| 孝感市| 井陉县| 元氏县| 庆安县| 苏州市| 桐梓县| 始兴县| 乃东县| 马鞍山市| 田阳县| 怀集县| 富锦市| 保靖县| 柘荣县| 大余县| 江源县| 正定县| 石棉县|