Nginx源碼初探之數據結構 - 基數樹數據結構

基數樹介紹

       基數樹也叫做壓縮前綴樹,是一種多叉搜索樹,對比其他結構跟節省空間。基數樹常見於IP路由檢索,文本文檔的的倒排索引等場景中。同時基數樹也是按照字典順序來組織葉節點的,這種特點使之適合持久化改造,加上他的多道特點,靈活性較強,適合作爲區塊鏈的基礎數據結構,構建持久性區塊時較好的映射各類數據集合。

Nginx基數樹的實現
        Nginx中基數樹的實現是一種二叉查找樹,具備二叉查找樹的所有優點,同時避免了紅黑樹增刪數據是需要通過自身旋轉來維持平衡,因此他具有更快的插入、刪除速度和更高的內存空間利用率。基數樹的key兼顧唯一標識和樹平衡維護的功能。的每個節點的key關鍵字先轉換爲32爲二進制數,然後從左至右開始,0進入左子樹,1進入右子樹,節點插入的同時自動完成二叉樹平衡的維護。
1.數據結構

struct ngx_radix_node_s {
    ngx_radix_node_t  *right;/*右孩子節點*/
    ngx_radix_node_t  *left;/*左孩子節點*/
    ngx_radix_node_t  *parent;/*父節點*/
    uintptr_t          value;/*用戶自定義結構的數據指針*/
};

        ngx_radix_tree_t是Nginx基數樹的管理和操作類,實現了內存的自己管理。已經分配但是未使用的節點交給free變量,當需要使用節點的時候優先在free變量中重用已有的節點。

typedef struct {
    ngx_radix_node_t  *root;/*根節點*/
    ngx_pool_t        *pool;/*內存池*/
    ngx_radix_node_t  *free;/*空閒節點*/
    char              *start;/*已分配內存未使用的首地址,也就是下個節點待分配內存的起始地址*/
    size_t             size;/*空閒內存的大小*/
} ngx_radix_tree_t;

2.基數樹的創建
          基數樹的創建和二叉樹的創建沒有太大的不同,按照基數樹的規則一一實現就好。。Nginx基數樹中爲了減少二叉樹的高度(壓縮前綴樹,壓縮就體現在消除不需要的節點帶來的分支)使用了掩碼。通過掩碼來決定樹的高度,具體邏輯常見代碼註釋。

ngx_radix_tree_t *
ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)
{
    uint32_t           key, mask, inc;
    ngx_radix_tree_t  *tree;

    tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t));
    if (tree == NULL) {
        return NULL;
    }

    tree->pool = pool;
    tree->free = NULL;
    tree->start = NULL;
    tree->size = 0;

    tree->root = ngx_radix_alloc(tree);
    if (tree->root == NULL) {
        return NULL;
    }

    tree->root->right = NULL;
    tree->root->left = NULL;
    tree->root->parent = NULL;
    tree->root->value = NGX_RADIX_NO_VALUE;

    if (preallocate == 0) {
        return tree;
    }

    /*
     * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
     * increases TLB hits even if for first lookup iterations.
     * On 32-bit platforms the 7 preallocated bits takes continuous 4K,
     * 8 - 8K, 9 - 16K, etc.  On 64-bit platforms the 6 preallocated bits
     * takes continuous 4K, 7 - 8K, 8 - 16K, etc.  There is no sense to
     * to preallocate more than one page, because further preallocation
     * distributes the only bit per page.  Instead, a random insertion
     * may distribute several bits per page.
     *
     * Thus, by default we preallocate maximum
     *     6 bits on amd64 (64-bit platform and 4K pages)
     *     7 bits on i386 (32-bit platform and 4K pages)
     *     7 bits on sparc64 in 64-bit mode (8K pages)
     *     8 bits on sparc64 in 32-bit mode (8K pages)
     */
    /**/
	/*1.根據系統電腦處理器架構決定基數樹的最高層數*/
    if (preallocate == -1) {
        switch (ngx_pagesize / sizeof(ngx_radix_node_t)) {

        /* amd64 */
        case 128:
            preallocate = 6;
            break;

        /* i386, sparc64 */
        case 256:
            preallocate = 7;
            break;

        /* sparc64 in 32-bit mode */
        default:
            preallocate = 8;
        }
    }

    mask = 0;
    inc = 0x80000000;
    /*2.掩碼初始0層,然後循環處理器架構層次,掩碼依次右移逐層增加*/
    while (preallocate--) {

        key = 0;
        mask >>= 1;
		/*2.1異或0x80000000保證掩碼最高位是1*/
        mask |= 0x80000000;/*轉換二進制1000 0000 0000 0000 0000 0000 0000 0000 */
       /*2.2基數樹的首先遍歷樹的深度,如果爲1,向右子樹搜索,否則向左子樹搜索,如果找到位置有結點,則直接覆蓋。否則,則依次創建沿途結點(0或1)並插入在樹中。*/
        do {
            if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE)
                != NGX_OK)
            {
                return NULL;
            }

            key += inc;

        } while (key);

        inc >>= 1;
    }
    /*返回構建好的樹*/
    return tree;
}

3.基數樹的查找
      根據基數樹的規則,從根節點開始遍歷,遇到0遍歷左子樹,遇到1遍歷右子樹,最後返回查詢結果。

uintptr_t
ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)
{
    uint32_t           bit;
    uintptr_t          value;
    ngx_radix_node_t  *node;

    bit = 0x80000000;
    value = NGX_RADIX_NO_VALUE;
    node = tree->root;

    while (node) {
        if (node->value != NGX_RADIX_NO_VALUE) {
            value = node->value;
        }

        if (key & bit) {
            node = node->right;

        } else {
            node = node->left;
        }

        bit >>= 1;
    }

    return value;
}

        Nginx的基數樹要求每個節點key必須可以轉換爲32位整型,並且因爲需要自己管理內存(無形中提高了使用的難度),所以總的來說,即使在Nginx中應用也不廣泛。當然這個也沒什麼奇怪的,基數樹解決的主要問題還是字典問題,而字典問題其實關聯數組,Hash散列表都可以很好的解決這個問題,這樣基數樹不常用也就不難理解了。

發佈了27 篇原創文章 · 獲贊 1 · 訪問量 2238
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章