溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PHP函數(shù)uniqid()能不能生成唯一ID

發(fā)布時間:2021-09-03 22:28:34 來源:億速云 閱讀:179 作者:chen 欄目:云計算

這篇文章主要講解了“PHP函數(shù)uniqid()能不能生成唯一ID”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“PHP函數(shù)uniqid()能不能生成唯一ID”吧!


雖然PHP提供了一個生成唯一ID的函數(shù)uniqid(),但這個函數(shù)真的可以生成唯一ID嗎?我們來看看uniqid()的具體實現(xiàn):

PHP_FUNCTION(uniqid)
{
    ...
    gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);

    sec = (int) tv.tv_sec;
    usec = (int) (tv.tv_usec % 0x100000);

    spprintf(&uniqid, 0, "%s%08x%05x", prefix, sec, usec);

    RETURN_STRING(uniqid, 0);
}

 從代碼可以看出,uniqid()是通過微妙級時間戳來實現(xiàn)的,在分布式高并發(fā)的情況下,ID的重復(fù)率是很高的,所以我們不能使用uniqid()來生成唯一ID。

snowflake算法

既然不能單純靠時間戳來保證唯一性,那么是不是可以增加以下特征值來保證呢?為此,Twitter公司發(fā)明了snowflake算法。snowflake算法的核心原理是把一個64位的整數(shù)分為3個部分,如下圖:

PHP函數(shù)uniqid()能不能生成唯一ID

如上圖所示,高端的第一位不使用,接著的41位字節(jié)用于存儲毫秒級的時間戳,緊跟著時間戳的10位作為機器ID,而最后12位為序列號。

  1. 對于不同的機器來說,可以為每一臺機器分配一個唯一的機器ID,這樣就可以保證每臺機器鎖生成的ID不會重復(fù)。

  2. 對于同一臺機器,如果同一時刻多個客戶端并發(fā)請求,那么可以通過增加序列號來保證ID唯一性。

默認情況下41位的時間戳可以支持該算法使用到2082年(需要通過減去一個起始時間戳),10位的工作機器ID可以支持1023臺機器,12位的序列號支持1毫秒產(chǎn)生4095個自增序列ID。
也就是說1臺機器1秒可以承受4095000個并發(fā),可以勝任任何場景。snowflake算法的代碼實現(xiàn)大概如下:

static uint64_t last_ts = 0;
static uint64_t sequence = 0;
static uint64_t datacenter_id = 0;

// 獲取毫秒時間戳
uint64_t current_timestamp()
{
    struct timeval tv;
    uint64_t retval;

    if (gettimeofday(&tv, NULL) == -1) {
        return 0ULL;
    }

    retval = (u64_t)tv.tv_sec * 1000ULL +
             (u64_t)tv.tv_usec / 1000ULL;

    return retval;
}

// 如果在同一毫秒內(nèi)超過了并發(fā)現(xiàn)在, 那么等待下一毫秒
uint64_t skip_next_millis()
{
    uint64_t ts;

    while (1) {
        ts = current_timestamp();
        if (ts > last_ts) {
            break;
        }
    }

    return ts;
}

// 獲取下一個ID
uint64_t get_next_id()
{
    uint64_t retval, ts;

    ts = current_timestamp();

    if (ts == last_ts) { // 同一毫秒內(nèi)多個并發(fā)
        sequence = (sequence + 1) & 0xFFF; // 增加序列號計數(shù)器
        if (sequence == 0) {  // 計數(shù)器用完
            ts = skip_next_millis(); // 等待下一毫秒
        }
    } else {
        sequence = 0; // 清空序列號計數(shù)器
    }

    last_ts = ts;

    retval = (ts << 22) | (datacenter_id << 12) | sequence;

    return retval;
}

PHP實現(xiàn)唯一ID生成函數(shù)

嚴格來說使用PHP是不能實現(xiàn)snowflake算法的,這是因為PHP的運行機制導(dǎo)致的。一般一臺機器會啟動多個PHP進程,而且進程之間是不能共享內(nèi)存的,就是說多個PHP進程之間不能使用同一個序列號,這樣就會導(dǎo)致不同進程生成的ID可能會重復(fù)。而且每次請求完,PHP都會釋放本次請求的所有資源,那么就不能記錄最后一次時間戳和序列號計數(shù)器的值(雖然可以使用文件或者memcached之類實現(xiàn),當(dāng)這樣性能就會降低很多)。所以說使用PHP是不能實現(xiàn)snowflake算法的。

不能使用PHP代碼實現(xiàn)snowflake算法,但是可以通過PHP擴展來實現(xiàn),下圖是PHP-FPM的運行機制:

PHP函數(shù)uniqid()能不能生成唯一ID

從上圖可以看出,在創(chuàng)建worker進程之前先會調(diào)用每個擴展的init()函數(shù)(PHP_MINIT_FUNCTION函數(shù)),所以我們可以在init()函數(shù)創(chuàng)建一塊共享內(nèi)存,然后每個worker進程就可以共用這塊內(nèi)存(因為fork之前創(chuàng)建的共享內(nèi)存可以在子進程中共用)。

因為不同的進程并發(fā)訪問共享內(nèi)存可能會導(dǎo)致數(shù)據(jù)不一致的問題,所以必須解決資源競爭的問題,解決資源競爭最常用的方法就是使用鎖。

進程間一般使用的鎖有:信號量和自旋鎖。信號量與自旋鎖的不同之處是,信號量會發(fā)生進程上下文切換,而自旋鎖不會。

當(dāng)然這兩種鎖都可以解決資源競爭問題,但是相對于生成唯一ID這種場景,使用自旋鎖會有更好的性能,這是因為生成ID這個過程非常短,而自旋鎖鎖不需要切換上下文。


自旋鎖

自旋鎖的原理是不斷測試鎖是否能夠被上鎖,如果能夠上鎖就進行上鎖操作,否則就不斷重復(fù)上面的操作。下面代碼是一個簡單的實現(xiàn):

void spin_lock(atomic_t *lock, int id)
{
    int i, n;

    for ( ;; ) {

        if (*lock == 0 &&
            __sync_bool_compare_and_swap(lock, 0, id)) {
            return;
        }

        if (ncpu > 1) {

            for (n = 1; n < 129; n << 1) {

                for (i = 0; i < n; i++) {
                    __asm("pause");
                }

                if (*lock == 0 &&
                    __sync_bool_compare_and_swap(lock, 0, id)) {
                    return;
                }
            }
        }

        sched_yield();
    }
}

__sync_bool_compare_and_swap(var, old, new)函數(shù)是一個原子性操作,作用就是比較var與old的值,如果相等就把var的值改為new,如果不相等就繼續(xù)進行這個操作直到成功為止。這里有個小技巧,就是當(dāng)長時間獲取不到鎖的情況下,我們會調(diào)用sched_yield()系統(tǒng)調(diào)用讓出CPU,從而避免過度使用CPU資源。


感謝各位的閱讀,以上就是“PHP函數(shù)uniqid()能不能生成唯一ID”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對PHP函數(shù)uniqid()能不能生成唯一ID這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

php
AI