成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

swoole_table 實(shí)現(xiàn)原理剖析

smartlion / 792人閱讀

摘要:受限于的實(shí)現(xiàn),程序無(wú)法使用多線程進(jìn)行編程開(kāi)發(fā)。比如實(shí)現(xiàn)一個(gè)聊天室程序,用戶在進(jìn)程中處理,用戶在進(jìn)程中處理,和如果在同一個(gè),這個(gè)在多線程環(huán)境中直接用表示,和加到對(duì)應(yīng)的中即可。想要解決這個(gè)問(wèn)題,必須實(shí)現(xiàn)一個(gè)基于共享內(nèi)存的數(shù)據(jù)結(jié)構(gòu)。

Swoole項(xiàng)目從 2012 年推出到現(xiàn)在已經(jīng)有 5 年的歷史,現(xiàn)在越來(lái)越多的互聯(lián)網(wǎng)企業(yè)使用Swoole來(lái)開(kāi)發(fā)各類后臺(tái)應(yīng)用。受限于 PHP 的ZendVM實(shí)現(xiàn),PHP 程序無(wú)法使用多線程進(jìn)行編程開(kāi)發(fā)。應(yīng)用程序中實(shí)現(xiàn)并行處理只能使用多進(jìn)程模式。

做過(guò)多進(jìn)程開(kāi)發(fā)的 PHPer 都知道進(jìn)程的內(nèi)存隔離性。在程序中聲明的global全局?jǐn)?shù)組,實(shí)際上并不是數(shù)據(jù)共享的,在一個(gè)進(jìn)程內(nèi)修改數(shù)組的值,在另外一個(gè)進(jìn)程中是無(wú)效的。

$array = array();

function process1() {
    global $array;
    $array["test"] = "hello world";
}

function process2() {
    global $array;
    //這里讀取不到test的值
    var_dump($array["test"]);
}

這個(gè)進(jìn)程隔離性給程序的開(kāi)發(fā)帶來(lái)的很多煩惱。比如實(shí)現(xiàn)一個(gè)聊天室程序,用戶A在進(jìn)程1中處理,用戶B在進(jìn)程2中處理,AB如果在同一個(gè)group,這個(gè)group在多線程環(huán)境中直接用set表示,AB加到對(duì)應(yīng)groupset中即可。但多進(jìn)程環(huán)境中,用 PHP 的array無(wú)法實(shí)現(xiàn)。一般可以有2個(gè)思路解決問(wèn)題:

進(jìn)程間通信,可以使用管道,向另外一個(gè)進(jìn)程發(fā)送請(qǐng)求,獲取數(shù)據(jù)的值

借助存儲(chǔ)實(shí)現(xiàn),如Redis、MySQL文件

這2個(gè)方案雖然可以實(shí)現(xiàn),但都存在明顯的缺點(diǎn)。方案一實(shí)現(xiàn)較為復(fù)雜,開(kāi)發(fā)困難。方案二實(shí)現(xiàn)簡(jiǎn)單,但存在額外的IO消耗,不是純內(nèi)存操作,有性能瓶頸?;?b>/dev/shm實(shí)現(xiàn)內(nèi)存文件讀寫(xiě)的方案,是一個(gè)不錯(cuò)的方案,但需要注意鎖的操作,讀寫(xiě)時(shí)需要額外的系統(tǒng)調(diào)用開(kāi)銷。

想要解決這個(gè)問(wèn)題,必須實(shí)現(xiàn)一個(gè)基于共享內(nèi)存的數(shù)據(jù)結(jié)構(gòu)。在 PHP 中也有一些擴(kuò)展模塊可以使用。如APCuYac、shm_put_var/shm_get_var

Yac:性能高,但由于底層實(shí)現(xiàn)的限制,無(wú)法保證一致性。只能作為Cache來(lái)使用

APCu:支持Key-Value式數(shù)據(jù)的讀寫(xiě),缺點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單粗暴,鎖的粒度太粗。高并發(fā)時(shí)存在大量鎖的爭(zhēng)搶,性能較差

shm 系列函數(shù):這個(gè)方案雖然能實(shí)現(xiàn)共享內(nèi)存操作,但實(shí)際上底層實(shí)現(xiàn)非常簡(jiǎn)陋。一方面底層根本沒(méi)有加鎖,如果你要在并發(fā)環(huán)境中使用,需要自行實(shí)現(xiàn)鎖的操作。另外,底層實(shí)際上是一個(gè)鏈表結(jié)構(gòu),數(shù)據(jù)較多時(shí),查詢性能非常差

swoole_table 介紹

為了解決多進(jìn)程程序中數(shù)據(jù)共享的難題,Swoole擴(kuò)展提供了swoole_table數(shù)據(jù)結(jié)構(gòu)。Table的實(shí)現(xiàn)非常精巧,使用最方便,同時(shí)性能也是最好的。

$table = new swoole_table(1024);
$table->column("id", swoole_table::TYPE_INT, 4);
$table->column("name", swoole_table::TYPE_STRING, 64);
$table->column("num", swoole_table::TYPE_FLOAT);
$table->create();

$table->set("[email protected]", array("id" => 145, "name" => "rango", "num" => 3.1415));
$table->set("[email protected]", array("id" => 358, "name" => "Rango1234", "num" => 3.1415));
$table->set("[email protected]", array("id" => 189, "name" => "rango3", "num" => 3.1415));

$ret1 = $table->get("[email protected]");
$ret2 = $table->get("[email protected]");

$table->del("[email protected]");

Table實(shí)現(xiàn)了一個(gè)二維Map結(jié)構(gòu),有點(diǎn)像 PHP 的二維數(shù)組,簡(jiǎn)單易用。在最新的1.9.19中還可以使用ArrayAccess接口以array的方式操作Table

$table = new swoole_table(1024);
$table->column("id", swoole_table::TYPE_INT);
$table->column("name", swoole_table::TYPE_STRING, 64);
$table->column("num", swoole_table::TYPE_FLOAT);
$table->create();

$table["apple"] = array("id" => 145, "name" => "iPhone", "num" => 3.1415);
$table["google"] = array("id" => 358, "name" => "AlphaGo", "num" => 3.1415);

$table["microsoft"]["name"] = "Windows";
$table["microsoft"]["num"] = "1997.03";

var_dump($table["apple"]);
var_dump($table["microsoft"]);

$table["google"]["num"] = 500.90;
var_dump($table["google"]);
Table的優(yōu)勢(shì)

性能極高,全部是純內(nèi)存操作,沒(méi)有任何系統(tǒng)調(diào)用和IO的開(kāi)銷。在酷睿I5機(jī)器上測(cè)試,Table單進(jìn)程單線程每秒可完成寫(xiě)操作300萬(wàn)次,讀操作每秒可完成150萬(wàn)次。在24核服務(wù)器上,理論上每秒可實(shí)現(xiàn)數(shù)千萬(wàn)次讀寫(xiě)操作。

使用數(shù)據(jù)行鎖,底層使用了數(shù)據(jù)行鎖自旋鎖。多進(jìn)程并發(fā)執(zhí)行時(shí),讀寫(xiě)不同的key不存在鎖的爭(zhēng)搶問(wèn)題。只有同一CPU時(shí)間讀寫(xiě)同一個(gè)Key才需要進(jìn)行加鎖操作。而且Table本身鎖的粒度非常小,get、set操作內(nèi)部只有少量?jī)?nèi)存讀寫(xiě)的指令,可以在數(shù)百納秒內(nèi)完成操作。

Table的局限性

Key最大長(zhǎng)度不得超過(guò)64字節(jié)

必須在創(chuàng)建前規(guī)劃好容量,一旦寫(xiě)滿后,再set新的數(shù)據(jù)會(huì)出現(xiàn)內(nèi)存分配導(dǎo)致失敗,無(wú)法實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)容

因此使用Table時(shí)盡可能地設(shè)置較大的內(nèi)存尺寸,這樣雖然會(huì)帶來(lái)一定的內(nèi)存浪費(fèi),但實(shí)際上現(xiàn)代服務(wù)器內(nèi)存非常廉價(jià),這個(gè)局限性在實(shí)際項(xiàng)目中的問(wèn)題并不大。

swoole_table 實(shí)現(xiàn)原理

Table底層基于共享內(nèi)存實(shí)現(xiàn),所占內(nèi)存取決于表格的尺寸size、沖突率(默認(rèn)20%)、column的設(shè)置(如上面的示例中每行需要8 + 64 + 8字節(jié))、64字節(jié)KEY的存儲(chǔ)空間、管理結(jié)構(gòu)的內(nèi)存消耗。

Table 的內(nèi)存申請(qǐng)
size_t row_num = table->size * (1 + table->conflict_proportion);
size_t row_memory_size = sizeof(swTableRow) + table->item_size;
size_t memory_size = row_num * row_memory_size;

memory_size += sizeof(swMemoryPool) + sizeof(swFixedPool) + ((row_num - table->size) * sizeof(swFixedPool_slice));

memory_size += table->size * sizeof(swTableRow *);
void *memory = sw_shm_malloc(memory_size);

swoole_table本身是一個(gè)HashTable結(jié)構(gòu),Key會(huì)計(jì)算為hash值,來(lái)散列到每一行。HashTable結(jié)構(gòu)會(huì)遇到Hash沖突問(wèn)題,兩個(gè)完全不同的Key可能計(jì)算的hash值是同一個(gè),這時(shí)需要使用鏈表來(lái)解決Hash沖突。Swoole底層會(huì)創(chuàng)建一個(gè)浮動(dòng)的內(nèi)存池swFixedPool結(jié)構(gòu)來(lái)管理這些沖突Key的內(nèi)存。默認(rèn)會(huì)創(chuàng)建size * 20%數(shù)量的浮動(dòng)內(nèi)存池。在1.9.19中可以自行定義沖突率。

$table = new swoole_table(65536, 0.9);

假如你的場(chǎng)景中Hash沖突較多,可以調(diào)高沖突率,以申請(qǐng)一塊較大的浮動(dòng)內(nèi)存池。

static swTableRow* swTable_hash(swTable *table, char *key, int keylen)
{
#ifdef SW_TABLE_USE_PHP_HASH
    uint64_t hashv = swoole_hash_php(key, keylen);
#else
    uint64_t hashv = swoole_hash_austin(key, keylen);
#endif
    uint64_t index = hashv & table->mask;
    assert(index < table->size);
    return table->rows[index];
}

swTableRow* swTableRow_set(swTable *table, char *key, int keylen, swTableRow **rowlock)
{
    if (keylen > SW_TABLE_KEY_SIZE)
    {
        keylen = SW_TABLE_KEY_SIZE;
    }

    swTableRow *row = swTable_hash(table, key, keylen);
    *rowlock = row;
    swTableRow_lock(row);

#ifdef SW_TABLE_DEBUG
    int _conflict_level = 0;
#endif

    if (row->active)
    {
        for (;;)
        {
            if (strncmp(row->key, key, keylen) == 0)
            {
                break;
            }
            else if (row->next == NULL)
            {
                table->lock.lock(&table->lock);
                swTableRow *new_row = table->pool->alloc(table->pool, 0);

#ifdef SW_TABLE_DEBUG
                conflict_count ++;
                if (_conflict_level > conflict_max_level)
                {
                    conflict_max_level = _conflict_level;
                }

#endif
                table->lock.unlock(&table->lock);

                if (!new_row)
                {
                    return NULL;
                }
                //add row_num
                bzero(new_row, sizeof(swTableRow));
                sw_atomic_fetch_add(&(table->row_num), 1);
                row->next = new_row;
                row = new_row;
                break;
            }
            else
            {
                row = row->next;
#ifdef SW_TABLE_DEBUG
                _conflict_level++;
#endif
            }
        }
    }
    else
    {
#ifdef SW_TABLE_DEBUG
        insert_count ++;
#endif
        sw_atomic_fetch_add(&(table->row_num), 1);
    }

    memcpy(row->key, key, keylen);
    row->active = 1;
    return row;
}

使用swTable_hash計(jì)算hash值,散列到對(duì)應(yīng)的行

Key發(fā)生沖突時(shí),需要調(diào)用table->pool->alloc從浮動(dòng)內(nèi)存池中分配內(nèi)存

浮動(dòng)內(nèi)存池內(nèi)存不足時(shí),alloc失敗,這時(shí)無(wú)法寫(xiě)入數(shù)據(jù)到Table

數(shù)據(jù)自旋鎖

當(dāng)同一CPU時(shí)間,多個(gè)進(jìn)程同時(shí)讀取某一行時(shí),需要鎖的爭(zhēng)搶。

swTableRow_lock(row);
//內(nèi)存操作
swTableRow_unlock(_rowlock);

swTableRow_lock 本身是一個(gè)自選鎖,這里使用了gcc編譯器提供的__sync_bool_compare_and_swap函數(shù)進(jìn)行CPU原子操作。多個(gè)進(jìn)程同時(shí)讀寫(xiě)某一行數(shù)據(jù)時(shí),先得到鎖的進(jìn)程會(huì)執(zhí)行內(nèi)存讀寫(xiě)操作,未得到鎖的進(jìn)程會(huì)進(jìn)行CPU自旋等待進(jìn)程釋放鎖。

static sw_inline void sw_spinlock(sw_atomic_t *lock)
{
    uint32_t i, n;
    while (1)
    {
        if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1))
        {
            return;
        }
        if (SW_CPU_NUM > 1)
        {
            for (n = 1; n < SW_SPINLOCK_LOOP_N; n <<= 1)
            {
                for (i = 0; i < n; i++)
                {
                    sw_atomic_cpu_pause();
                }

                if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1))
                {
                    return;
                }
            }
        }
        swYield();
    }
}
返回結(jié)果

使用table::get方法時(shí),從Table共享內(nèi)存中,讀取數(shù)據(jù)寫(xiě)入到PHP本地內(nèi)存數(shù)組中。底層會(huì)根據(jù)列信息table->columns,計(jì)算內(nèi)存指針的偏移量,得到對(duì)應(yīng)字段的值。

static inline void php_swoole_table_row2array(swTable *table, swTableRow *row, zval *return_value)
{
    array_init(return_value);

    swTableColumn *col = NULL;
    swTable_string_length_t vlen = 0;
    double dval = 0;
    int64_t lval = 0;
    char *k;

    while(1)
    {
        col = swHashMap_each(table->columns, &k);
        if (col == NULL)
        {
            break;
        }
        if (col->type == SW_TABLE_STRING)
        {
            memcpy(&vlen, row->data + col->index, sizeof(swTable_string_length_t));
            sw_add_assoc_stringl_ex(return_value, col->name->str, col->name->length + 1, row->data + col->index + sizeof(swTable_string_length_t), vlen, 1);
        }
        else if (col->type == SW_TABLE_FLOAT)
        {
            memcpy(&dval, row->data + col->index, sizeof(dval));
            sw_add_assoc_double_ex(return_value, col->name->str, col->name->length + 1, dval);
        }
        else
        {
            switch (col->type)
            {
            case SW_TABLE_INT8:
                memcpy(&lval, row->data + col->index, 1);
                sw_add_assoc_long_ex(return_value, col->name->str, col->name->length + 1, (int8_t) lval);
                break;
            case SW_TABLE_INT16:
                memcpy(&lval, row->data + col->index, 2);
                sw_add_assoc_long_ex(return_value, col->name->str, col->name->length + 1, (int16_t) lval);
                break;
            case SW_TABLE_INT32:
                memcpy(&lval, row->data + col->index, 4);
                sw_add_assoc_long_ex(return_value, col->name->str, col->name->length + 1, (int32_t) lval);
                break;
            default:
                memcpy(&lval, row->data + col->index, 8);
                sw_add_assoc_long_ex(return_value, col->name->str, col->name->length + 1, lval);
                break;
            }
        }
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/25728.html

相關(guān)文章

  • swoole——從入門(mén)到放棄(三)

    摘要:從入門(mén)到放棄三一進(jìn)程子進(jìn)程創(chuàng)建成功后要執(zhí)行的函數(shù)重定向子進(jìn)程的標(biāo)準(zhǔn)輸入和輸出。默認(rèn)為阻塞讀取。是否創(chuàng)建管道,啟用后,此選項(xiàng)將忽略用戶參數(shù),強(qiáng)制為。 swoole——從入門(mén)到放棄(三) 一、進(jìn)程 swoole_process SwooleProcess swoole_process::__construct(callable $function, $redirect_stdin...

    王笑朝 評(píng)論0 收藏0
  • swoole——從入門(mén)到放棄(三)

    摘要:從入門(mén)到放棄三一進(jìn)程子進(jìn)程創(chuàng)建成功后要執(zhí)行的函數(shù)重定向子進(jìn)程的標(biāo)準(zhǔn)輸入和輸出。默認(rèn)為阻塞讀取。是否創(chuàng)建管道,啟用后,此選項(xiàng)將忽略用戶參數(shù),強(qiáng)制為。 swoole——從入門(mén)到放棄(三) 一、進(jìn)程 swoole_process SwooleProcess swoole_process::__construct(callable $function, $redirect_stdin...

    rottengeek 評(píng)論0 收藏0
  • Swoole 源碼分析——內(nèi)存模塊之共享內(nèi)存表swoole_table

    摘要:如果互斥鎖的持有者死亡了,或者持有這樣的互斥鎖的進(jìn)程了互斥鎖所在的共享內(nèi)存或者持有這樣的互斥鎖的進(jìn)程執(zhí)行了調(diào)用,則會(huì)解除鎖定該互斥鎖?;コ怄i的下一個(gè)持有者將獲取該互斥鎖并返回錯(cuò)誤。 前言 swoole_table 一個(gè)基于共享內(nèi)存和鎖實(shí)現(xiàn)的超高性能,并發(fā)數(shù)據(jù)結(jié)構(gòu)。用于解決多進(jìn)程/多線程數(shù)據(jù)共享和同步加鎖問(wèn)題。 swoole_table 的數(shù)據(jù)結(jié)構(gòu) swoole_table 實(shí)際上...

    Invoker 評(píng)論0 收藏0
  • Swoole 在 Swoft 中的應(yīng)用

    摘要:在中的應(yīng)用官網(wǎng)源碼解讀號(hào)外號(hào)外歡迎大家我們開(kāi)發(fā)組定了一個(gè)就線下聚一次的小目標(biāo)上一篇源碼解讀反響還不錯(cuò)不少同學(xué)推薦再加一篇講解一下中使用到的功能幫助大家開(kāi)啟的實(shí)戰(zhàn)之旅服務(wù)器開(kāi)發(fā)涉及到的相關(guān)技術(shù)領(lǐng)域的知識(shí)非常多不日積月累打好基礎(chǔ)是很難真正 date: 2017-12-14 21:34:51title: swoole 在 swoft 中的應(yīng)用 swoft 官網(wǎng): https://www.sw...

    EscapedDog 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<