zoukankan      html  css  js  c++  java
  • [原] 利用Radix树作为KeyValue 键值对的数据路由

    引言:总所周知,NoSQL,Memcached等作为Key—Value 存储的模型的数据路由都采用Hash表来达到目的。如何解决Hash冲突和Hash表大小的设计是一个很头疼的问题。

    借助于Radix树,我们同样可以达到对于uint32_t 的数据类型的路由。这个灵感就来自于Linux内核的IP路由表的设计。

    作为传统的Hash表,我们把接口简化一下,可以抽象为这么几个接口。

    void Hash_create( size_t Max );
    
    int Hash_insert( uint32_t hash_value , value_type value ) ;
    
    value_type *Hash_get( uint32_t hashvalue );

    int Hash_delete( uint32_t hash_value );

    接口的含义如其名,创建一个Hash表,插入,取得,删除。

    同样,把这个接口的功能抽象后,利用radix同样可以实现相同的接口方式。

     1 int mc_radix_hash_ini(mc_radix_t *t ,int nodenum );
     2 
     3 int mc_radix_hash_insert( mc_radix_t *t , unsigned int hashvalue , void *data ,size_t size );
     4 
     5 int mc_radix_hash_del( mc_radix_t *t , unsigned int hashvalue ) ;
     6 
     7 void *mc_radix_hash_get( mc_radix_t *t , unsigned int hashvalue ) ;
    

    那我们简单介绍一下Radix树:

    Radix Tree(基树) 其实就差不多是传统的二叉树,只是在寻找方式上,利用比如一个unsigned int  的类型的每一个比特位作为树节点的判断。

    可以这样说,比如一个数  1000101010101010010101010010101010 (随便写的)那么按照Radix 树的插入就是在根节点,如果遇到 0 ,就指向左节点,如果遇到1就指向右节点,在插入过程中构造树节点,在删除过程中删除树节点。如果觉得太多的调用Malloc的话,可以采用池化技术,预先分配多个节点,本博文就采用这种方式。

     1 typedef struct _node_t
     2 {
     3     char     zo                ;         // zero or one
     4     int        used_num       ;
     5     struct _node_t *parent ;
     6     struct _node_t *left   ;
     7     struct _node_t *right  ;
     8     void            *data   ;//for nodes array list finding next empty node
     9     int        index           ;
    10 }mc_radix_node_t ;

    节点的结构定义如上。

    zo 可以忽略,父节点,坐指针,右指针顾名思义,data 用于保存数据的指针,index 是作为 node 池的数组的下标。

    树的结构定义如下:

     1 ypedef struct _radix_t
     2 {
     3     mc_radix_nodes_array_t * nodes    ;
     4     mc_radix_node_t    *         root      ;
     5 
     6     mc_slab_t        *         slab      ;
     7     
     8     
     9     /*
    10     pthread_mutex_t             lock        ;
    11     */
    12     int                         magic       ;
    13     int                         totalnum ;
    14     size_t                     pool_nodenum ;
    15     
    16     mc_item_queue             queue ;
    17 }mc_radix_t ;

     暂且不用看 nodes 的结构,这里只是作为一个node池的指针

     root 指针顾名思义是指向根结构,slab 是作为存放数据时候的内存分配器,如果要使用内存管理来减少开销的话(参见slab内存分配器一章)

     magic用来判断是否初始化,totalnum 是叶节点个数,poll_nodenum 是节点池内节点的个数。

     queue是作为数据项中数据的队列。

    我们采用8421编码的宏来作为每一个二进制位的判断:

    #define U01_MASK  	0x80000000
    #define U02_MASK  	0x40000000
    #define U03_MASK  	0x20000000
    #define U04_MASK 	0x10000000
    .
    .
    .
    .

      #define U31_MASK 0x00000002
      #define U32_MASK 0x00000001

     类似这样的方式来对每一位二进制位做判断,还有其他更好的办法,这里只是作为简化和快速。

    unsigned int MASKARRAY[32] = { 
    	U01_MASK,U02_MASK,U03_MASK,U04_MASK,U05_MASK,U06_MASK,U07_MASK,U08_MASK,
    	U09_MASK,U10_MASK,U11_MASK,U12_MASK,U13_MASK,U14_MASK,U15_MASK,U16_MASK,
    	U17_MASK,U18_MASK,U19_MASK,U20_MASK,U21_MASK,U22_MASK,U23_MASK,U24_MASK,
    	U25_MASK,U26_MASK,U27_MASK,U28_MASK,U29_MASK,U30_MASK,U31_MASK,U32_MASK
    };
    

      

    我们为Radix 提供了一些静态函数,不对外声明:

    初始化节点池

    static int mc_radix_nodes_ini(mc_radix_nodes_array_t *par_nodearray ,size_t par_maxnum )
    

    取得一个节点:

    static mc_radix_node_t *mc_get_radix_node(mc_radix_nodes_array_t *par_nodearray )
    

    归还一个节点:

    static void mc_free_radix_node( mc_radix_nodes_array_t *par_nodearray , mc_radix_node_t * par_free_node )
    

     这里是初始化radix 树:

     1 int mc_radix_hash_ini(mc_radix_t *t ,size_t nodenum )
     2 {
     3     /* init the node pool */
     4     t->nodes = (mc_radix_nodes_array_t *)malloc( sizeof(mc_radix_nodes_array_t) ); //为节点池分配空间
     5     t->slab = mc_slab_create();                                //使用slab分配器
     6     mc_radix_nodes_ini( t->nodes , nodenum );                      //初始化节点
     7     t->magic = MC_MAGIC ;
     8     t->totalnum = 0 ;
     9     t->pool_nodenum = nodenum ;
    10     t->root = NULL ;
    11     
    12     
    13     t->queue.head = NULL ;
    14     t->queue.pear = NULL ;
    15     t->queue.max_num = nodenum ;
    16     t->queue.cur_num = 0 ;
    17 }
     1 int mc_radix_hash_insert( mc_radix_t *t , unsigned int hashvalue , void *data ,size_t size )
     2 {
     3     unsigned int i = 0 ;
     4     mc_radix_node_t * root = t->root ;
     5 
     6     if( t->root == NULL )
     7     {
     8         t->root = mc_get_radix_node( t->nodes ) ;
     9     }
    10     
    11     /* LRU */
    12     /*其中涉及到LRU算法,原理是将所有的叶子节点链接为双向队列,然后更新和插入放入队列头,按照一定的比例从队列尾删除数据*/
    13     if( t->queue.cur_num >= (t->queue.max_num)*PERCENT )
    14     {
    15         for( i = 0 ; i < (t->queue.max_num)*(1-PERCENT) ; i++ )
    16         {
    17             mc_del_item( t , t->queue.pear );
    18         }
    19     }
    20     mc_radix_node_t * cur = t->root ;
    21     for(i = 0  ; i < 32 ; i++ )
    22     {
    23         /* 1 ---> right */
    24      /*按位来探测树节点*/
    25         if( hashvalue & MASKARRAY[i] )
    26         {
    27             
    28             if( cur -> right != NULL )
    29             {
    30                 cur->used_num++     ;
    31                 cur->right->parent = cur ;
    32                 cur = cur->right ;                
    33             }
    34             else
    35             {
    36                 cur->right = mc_get_radix_node( t->nodes ) ;
    37                 if( cur->right == NULL )
    38                 {
    39                     fprintf(stderr,"mc_get_radix_node error\n");
    40                     return -1;
    41                 }
    42                 cur->used_num++     ;
    43                 cur->right->parent = cur ;
    44                 cur = cur->right ;
    45             }
    46         }
    47         /* 0 ---> left */
    48         else
    49         {
    50             
    51             if( cur->left != NULL )
    52             {
    53                 cur->used_num++;
    54                 cur->left->parent = cur  ;
    55                 cur = cur->left ;
    56             }
    57             else
    58             {
    59                 cur->left = mc_get_radix_node( t->nodes ) ;
    60                 if( cur->left == NULL )
    61                 {
    62                     fprintf(stderr,"mc_get_radix_node error\n");
    63                     return -1;
    64                 }
    65     
    66                 cur->used_num++;
    67                 cur->left->parent = cur  ;
    68                 cur = cur->left ;
    69             }
    70         }        
    71     }
    72     
    73     t->totalnum ++ ;
    74     mc_slot_t * l_slot = mc_slot_alloc( t->slab, size ) ;
    75     cur->data = ( mc_slot_t *)(cur->data);
    76     memcpy( l_slot->star , data , size );
    77     cur->data = l_slot ;
    78     
    79     /*add to t->queue */
    80     if( t->queue.head == NULL )
    81     {
    82         t->queue.head = cur ;
    83         t->queue.pear = cur ;
    84         cur->left = NULL  ;
    85         cur->right = NULL ;
    86         
    87         t->queue.cur_num++ ;
    88     }
    89     else
    90     {
    91         cur->left = NULL ;
    92         cur->right = t->queue.head ;
    93         t->queue.head->left = cur ;
    94         t->queue.head = cur ;
    95         
    96         t->queue.cur_num++ ;
    97     }
    98     return 1;
    99 }

    删除一个节点,通过hashvalue作为其value,顾名思义

     1 int mc_radix_hash_del( mc_radix_t *t , unsigned int hashvalue )
     2 {
     3     if( t == NULL || t->root == NULL )
     4     {        
     5         return -1;
     6     }
     7     /* non  initialized */
     8     if( t->magic != MC_MAGIC )
     9     {        
    10         return -1;
    11     }
    12     mc_radix_node_t * cur = t->root ;    
    13     mc_radix_node_t * cur_par ;
    14     int    i = 0 ;
    15     for( ; i < 32 ; i++ )
    16     {
    17         if( hashvalue & MASKARRAY[i] )
    18         {
    19             
    20             if( cur->right != NULL )
    21             {
    22                 cur->used_num--  ;
    23                 cur = cur->right ;
    24             }
    25             else
    26                 return -1;
    27         }
    28         else
    29         {
    30         
    31             if( cur->left != NULL )
    32             {
    33                 cur->used_num-- ;
    34                 cur = cur->left ;
    35             }
    36             else
    37                 return -1;
    38         }
    39     }
    40     
    41     if( cur->used_num >= 0 )
    42         mc_slot_free(cur->data);
    43     
    44     /*remove from t->queue */
    45     if( cur == t->queue.pear && cur == t->queue.head )
    46     {
    47         t->queue.pear = NULL ;
    48         t->queue.head = NULL ;
    49         t->queue.cur_num -- ;
    50     }
    51     /* the last item */
    52     else if( cur == t->queue.pear && cur != t->queue.head)
    53     {
    54         cur->left->right = NULL  ;
    55         cur->left = NULL  ;
    56         t->queue.cur_num -- ;
    57     }
    58     else if( cur != t->queue.pear )
    59     {
    60         cur->left->right = cur->right ;
    61         cur->right->left = cur->left ;
    62         t->queue.cur_num -- ;
    63     }
    64     else
    65     {
    66         cur->left->right = cur->right ;
    67         cur->right->left = cur->left ;
    68         t->queue.cur_num -- ;
    69     }
    70         
    71     for(;;)
    72     {
    73         
    74         if( cur->used_num == 0 )
    75         {
    76             cur_par = cur->parent ;
    77             mc_free_radix_node( t->nodes , cur );
    78             cur = cur_par ;
    79         }
    80         if( cur == NULL )
    81             break ;
    82         if( cur->used_num > 0  )
    83             break ;
    84             
    85     }
    86     
    87     return 1;
    88     
    89 }

    取得值:通过void * 指向

     1 void *mc_radix_hash_get( mc_radix_t *t , unsigned int hashvalue )
     2 {
     3     if( t == NULL || t->root == NULL )
     4     {        
     5         fprintf(stderr,"t == NULL || t->root == NULL\n");
     6         return (void *)(0);
     7     }
     8     /* non  initialized */
     9     if( t->magic != MC_MAGIC )
    10     {        
    11         fprintf(stderr,"t->magic != MC_MAGIC\n");
    12         return (void *)(0);
    13     }
    14     mc_radix_node_t * cur = t->root ;    
    15     mc_slot_t *ret_slot ;
    16     int i = 0 ; 
    17     for( ; i < 32 ; i++ )
    18     {
    19         if( hashvalue & MASKARRAY[i] )
    20         {
    21             if( cur->right == NULL )
    22                 break;
    23             else
    24                 cur = cur->right ;
    25         }
    26         else
    27         {
    28             if( cur->left == NULL )
    29                 break;
    30             else
    31                 cur = cur->left ;
    32         }
    33     }
    34     if( i == 32 )
    35     {
    36         ret_slot = cur->data;
    37         
    38         /* update LRU queue*/
    39         if( cur->left != NULL )
    40         {
    41             if( cur->right != NULL )
    42             {
    43                     cur->left->right = cur->right ;
    44                     cur->right->left = cur->left ;
    45                     cur->left = t->queue.head ;
    46                     t->queue.head->left = cur ;
    47                     t->queue.head = cur ;
    48             }
    49             else
    50             {
    51                 /* cur->right == NULL  last element of LRU queue */
    52                     cur->left->right = NULL ;
    53                     cur->left = t->queue.head ;
    54                     t->queue.head->left = cur ;
    55                     t->queue.head = cur ;
    56                     
    57             }
    58         }
    59         return (void *)(ret_slot->star) ;
    60     }
    61     else
    62     {
    63         fprintf(stderr,"i = %d \n",i);
    64         return (void *)(0);
    65     }
    66 }
     1 int mc_free_radix( mc_radix_t *t )
     2 {
     3     mc_free_all_radix_node(t->nodes);
     4     mc_slab_free(t->slab);
     5     free(t->nodes);
     6 }
     7 
     8 static void mc_del_item( mc_radix_t *t ,  mc_radix_node_t * cur )
     9 {
    10     if( cur->left == NULL )
    11     {
    12         fprintf(stderr,"item number in LRU queue is too small \n");
    13         return ;
    14     }
    15     if( cur->right != NULL )
    16     {
    17         fprintf(stderr,"cur should be the last of LRU queue \n");
    18     }
    19     /* remove from LRU queue */
    20     mc_radix_node_t * pcur = cur->left ;
    21     cur->left = NULL   ;
    22     pcur->right = NULL ;
    23     
    24     pcur = cur->parent ;
    25     /* remove from radix tree */
    26     while( pcur != NULL )
    27     {
    28         cur->used_num -- ;
    29         if( cur->used_num <=0 )
    30         {
    31             mc_free_radix_node( t->nodes , cur );
    32         }
    33         cur = pcur ;
    34         pcur = pcur->parent ;
    35     } 
    36     
    37 }

    总结:radix 树作为key-value 路由最大的好处就是在于减少了hash表的动态和一部分碰撞问题等。还可以在此结构上方便的扩展 LRU算法,淘汰数据等。

    如果担心node 的初始化和申请太过于浪费资源,可以采用节点池的方式设计。

    文章属原创,转载请注明出处 联系作者: Email:zhangbolinux@sina.com QQ:513364476
  • 相关阅读:
    洛谷 P1032 字串变换
    洛谷 P1027 Car的旅行路线
    洛谷 P1024 一元三次方程求解
    洛谷 P1018 乘积最大
    洛谷 P1023 税收与补贴问题
    洛谷 P3456 [POI2007]GRZ-Ridges and Valleys
    洛谷 P1183 多边形的面积
    codeforces 407C Curious Array
    codeforces 12D Ball
    codeforces 388D Fox and Perfect Sets(线性基+数位dp)
  • 原文地址:https://www.cnblogs.com/Bozh/p/Radix.html
Copyright © 2011-2022 走看看