PHP 毫秒級時間戳生成

做消息隊列時發現在 Redis 的 ZSet 中,Score 數字只能設置到 17 位,我想讓 Score 唯一,就嘗試了幾種時間戳 + 隨機數的組合,於是得出一些答案

代碼如下:

/**
 * 取毫秒級時間戳,默認返回普通秒級時間戳 time() 及 3 位長度毫秒字符串
 *
 * @param int  $msec_length 毫秒長度,默認 3
 * @param int  $random_length 添加隨機數長度,默認 0
 * @param bool $dot 隨機是否存入小數點,默認 false
 * @param int  $delay 是否延遲,傳入延遲秒數,默認 0
 * @return string
 */
function msectime($msec_length = 3, $random_length = 0, $dot = false, $delay = 0) {
    list($msec, $sec) = explode(' ', microtime());
    $rand = $random_length > 0 ?
        number_format(
            mt_rand(1, (int)str_repeat('9', $random_length))
            * (float)('0.' . str_repeat('0', $random_length - 1) . '1'),
            $random_length,
            '.',
            '') : '';
    $msectime = sprintf('%.0f', (floatval($msec) + floatval($sec) + $delay) * pow(10, $msec_length));
    return $dot ? $msectime . '.' . substr($rand, 2) : $msectime . substr($rand, 2);
}

假設此刻調用上面的方法 msectime(7) 得到 15283761526669518

然後把這個數字扔進 Redis ZSet 的 Score 裏,如下圖,是正常的:

Redis Score

但是我們加一位,例如,變成了 152837615266695181

我去這...

哦豁,變成了科學計數?Emmm,顯然不合情理,這玩意兒似乎會丟掉精度呢。

怎麼辦?既然只能 17 位,我們就考慮減少時間戳的位數,當然毫秒級時間戳是非常精確的,先來看幾個效果:


保留 3 位毫秒,即 10 位秒級時間戳 + 3 位毫秒。

for ($i = 0; $i < 100; $i++) {
    echo msectime() . '<br>';
}

結果如下:(長圖慎入)

3 位毫秒

看起來有點意思,但是注意屁股 3 位數,這循環 100 次的結果,大部分數據都是一樣的,當然這種情況,我們可以考慮在後面直接 mt_rand() 生成 5 4 位隨機數拼接上

我考慮了一下這個,還是覺得不太保險,繼續嘗試加長毫秒位數,直接加兩位看看


5 位毫秒

for ($i = 0; $i < 100; $i++) {
    echo msectime(5) . '<br>';
}

結果如下:

5 位毫秒

結果很好了!屁股兩位的重複率很低!此時的數字長度爲 15 位,似乎再生成兩位隨機數就可以了?

當然常規的隨機數生成會考慮 mt_rand(10, 99) 這種形式

我建議 substr(mt_rand(1, 99) * 0.01, 2) 這樣生成:

在線運行

這樣能得到更廣的隨機結果值,相比起普通的 mt_rand() 更可靠一些。

回到正題!既然也存在小部分重複值的情況,在我看來依靠隨機數總是有風險的(當然沒有位數限制,完全可以考慮擴大隨機範圍)

於是我直接把毫秒時間戳擴大到 17 位再看


7 位毫秒

for ($i = 0; $i < 100; $i++) {
    echo msectime(7) . '<br>';
}

7 位毫秒

驚喜的發現!完全沒有重複值了,當然 Redis 裏的 Score 長度被用光了,無法再隨機哪怕 1 位數字

不過這個我的需求已經滿足了!


以上方法是本人根據實際需求所寫的一個,通過這個思路我們可以利用毫秒級時間戳生成訂單號等需求。

再寫一個例子:

msectime(3, 5, true);

// 得到結果如下
1528377789213.17053
1528377789213.19110
1528377789213.80717
1528377789213.12004
1528377789214.91335
1528377789214.08298
1528377789214.92701
1528377789214.85589
1528377789214.62383

歡迎大神提供更好的思路在秒殺等情況下生成不重複的數值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章