引言:总所周知,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 的初始化和申请太过于浪费资源,可以采用节点池的方式设计。

转载于:https://www.cnblogs.com/Bozh/archive/2012/04/15/Radix.html

[原] 利用Radix树作为Key-Value 键值对的数据路由相关推荐

  1. html键值对与名称值对的区别,使用网络存储存储键值对的数据-HTML5教程

    本节课的内容是介绍网络存储,使用它在浏览器里存储键值对的数据,功能上像以前的cookie一样,不过他更好,存储的数据可以大小.有两种类型的网络存储:本地存储和会话存储,他们使用相同的实现机制,只是可见 ...

  2. Android本地存储键值对,flutter本地存储键值对简单数据(相当于web的localstorage) 代码实现...

    flutter中存储键值对简单数据(相当于前端localstorage概念) 首先需要安装一个官方推荐包: 1 dependencies: 2 flutter: 3 sdk: flutter 4 sh ...

  3. python方向键键值_Python实现的字典排序操作示例【按键名key与键值value排序】

    本文实例讲述了Python实现的字典排序操作.分享给大家供大家参考,具体如下: 对字典进行排序?这其实是一个伪命题,搞清楚python字典的定义---字典本身默认以key的字符顺序输出显示---就像我 ...

  4. map,存储多个键值对的数据集合

    在ES5中,我们使用的是对象的方式来存储键值对,键是属性名,值是属性值,这种方法有以下问题 1)键名只能是字符串 2)获取数据的数量不方便 3)键名容易跟原型上的名称冲突 ES6中新增了map集合专门 ...

  5. 将 键值对的数据 拼成 json 格式的 串

    https://developer.mozilla.org/zh-cn/Using_native_JSON json键值对逆置 在某些特殊场景需要将JSON的键值对倒置(Inverting),以达到业 ...

  6. [原] insert into … on duplicate key update / replace into 多行数据

    场景是这样的,我有KV型的表,建表语句如下: CREATE TABLE `dkv` (`k1` int(11) NOT NULL DEFAULT '0',`k2` int(11) NOT NULL D ...

  7. Redis键-值数据库 nosql 数据建模(3)------ 如何存储主从表数据 一对多关系

    作者:QQ 14588019 WonderfulLife customers表 (主表) cust_id     cust_name   mobile       address 3892045    ...

  8. Redis键-值数据库 nosql 数据建模(4)------ 如何存储主从表数据 一对超级多关系

    Create GUIDs online 全球唯一主键 作者:QQ 14588019 WonderfulLife 本范例中不能使用guid编号作为明细的id,如果这么干,明细将无法查询,分页就更别谈啦 ...

  9. Redis键-值数据库 nosql 数据建模(5)------ 如何存储树形结构的数据

    作者:QQ 14588019 WonderfulLife 关系型数据库电商产品分类表 catgories id name parentId idPath 101 level1 1st category ...

最新文章

  1. Android Wear开发 - 数据通讯 - 第二节 : 数据的发送与接收
  2. oracle form 滚动条,jQuery实现的自定义滚动条实例详解
  3. ajax mysql项目 react_Github MIT开源银行电子支付系统(ReactJS+Nodejs+Mysql)
  4. 留言条.html .js来完成
  5. 台安变频器n2按键说明_台安N2变频器说明书.pdf
  6. Web安全-伪静态网页
  7. Python正则表达式,看完这篇文章就够了...#华为云·寻找黑马程序员#
  8. python Raw I/O
  9. scikit-learn学习资源
  10. CAUSALITY FOR MACHINE LEARNING
  11. 安全secuerity证券
  12. 关于股票的经典书籍有哪些推荐?
  13. 如何做好线上活动策划及执行的思路与框架
  14. Datakit.CrossManager.2023(2D/3D数据格式转换器)
  15. 使用Python输出正金字塔
  16. 啥是单点登录及单点登录原理
  17. jQuery动画操作
  18. Mac装双系统的那些优缺点详解
  19. 别怕,遇到Map源码面试题这样答就对了
  20. Oracle表空间不足ORA-01654

热门文章

  1. python爬虫从入门到精通-Python爬虫从入门到精通视频(2018新版)
  2. python使用教程pandas-Python 数据处理库 pandas 入门教程基本操作
  3. ipad编程软件python-iPad可用的软件编程软件有吗?
  4. python实现文件下载-Python 实现文件下载
  5. 鱼c论坛python课后题-【零基础】Python3学习课后练习题(十九)
  6. 如何自学python数据分析-『』python数据分析该怎么入门呢?
  7. python使用del保留字定义一个函数-Python使用什么保留字定义一个函数。
  8. python爬虫实例解析-Python(爬虫)- 动态加载案例分析
  9. python写错了怎么更改-Python中如何修改文件?Python文件修改方法
  10. python入门教程2word-python操作word入门