[原] 利用Radix树作为Key-Value 键值对的数据路由
引言:总所周知,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 键值对的数据路由相关推荐
- html键值对与名称值对的区别,使用网络存储存储键值对的数据-HTML5教程
本节课的内容是介绍网络存储,使用它在浏览器里存储键值对的数据,功能上像以前的cookie一样,不过他更好,存储的数据可以大小.有两种类型的网络存储:本地存储和会话存储,他们使用相同的实现机制,只是可见 ...
- Android本地存储键值对,flutter本地存储键值对简单数据(相当于web的localstorage) 代码实现...
flutter中存储键值对简单数据(相当于前端localstorage概念) 首先需要安装一个官方推荐包: 1 dependencies: 2 flutter: 3 sdk: flutter 4 sh ...
- python方向键键值_Python实现的字典排序操作示例【按键名key与键值value排序】
本文实例讲述了Python实现的字典排序操作.分享给大家供大家参考,具体如下: 对字典进行排序?这其实是一个伪命题,搞清楚python字典的定义---字典本身默认以key的字符顺序输出显示---就像我 ...
- map,存储多个键值对的数据集合
在ES5中,我们使用的是对象的方式来存储键值对,键是属性名,值是属性值,这种方法有以下问题 1)键名只能是字符串 2)获取数据的数量不方便 3)键名容易跟原型上的名称冲突 ES6中新增了map集合专门 ...
- 将 键值对的数据 拼成 json 格式的 串
https://developer.mozilla.org/zh-cn/Using_native_JSON json键值对逆置 在某些特殊场景需要将JSON的键值对倒置(Inverting),以达到业 ...
- [原] 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 ...
- Redis键-值数据库 nosql 数据建模(3)------ 如何存储主从表数据 一对多关系
作者:QQ 14588019 WonderfulLife customers表 (主表) cust_id cust_name mobile address 3892045 ...
- Redis键-值数据库 nosql 数据建模(4)------ 如何存储主从表数据 一对超级多关系
Create GUIDs online 全球唯一主键 作者:QQ 14588019 WonderfulLife 本范例中不能使用guid编号作为明细的id,如果这么干,明细将无法查询,分页就更别谈啦 ...
- Redis键-值数据库 nosql 数据建模(5)------ 如何存储树形结构的数据
作者:QQ 14588019 WonderfulLife 关系型数据库电商产品分类表 catgories id name parentId idPath 101 level1 1st category ...
最新文章
- Android Wear开发 - 数据通讯 - 第二节 : 数据的发送与接收
- oracle form 滚动条,jQuery实现的自定义滚动条实例详解
- ajax mysql项目 react_Github MIT开源银行电子支付系统(ReactJS+Nodejs+Mysql)
- 留言条.html .js来完成
- 台安变频器n2按键说明_台安N2变频器说明书.pdf
- Web安全-伪静态网页
- Python正则表达式,看完这篇文章就够了...#华为云·寻找黑马程序员#
- python Raw I/O
- scikit-learn学习资源
- CAUSALITY FOR MACHINE LEARNING
- 安全secuerity证券
- 关于股票的经典书籍有哪些推荐?
- 如何做好线上活动策划及执行的思路与框架
- Datakit.CrossManager.2023(2D/3D数据格式转换器)
- 使用Python输出正金字塔
- 啥是单点登录及单点登录原理
- jQuery动画操作
- Mac装双系统的那些优缺点详解
- 别怕,遇到Map源码面试题这样答就对了
- Oracle表空间不足ORA-01654
热门文章
- python爬虫从入门到精通-Python爬虫从入门到精通视频(2018新版)
- python使用教程pandas-Python 数据处理库 pandas 入门教程基本操作
- ipad编程软件python-iPad可用的软件编程软件有吗?
- python实现文件下载-Python 实现文件下载
- 鱼c论坛python课后题-【零基础】Python3学习课后练习题(十九)
- 如何自学python数据分析-『』python数据分析该怎么入门呢?
- python使用del保留字定义一个函数-Python使用什么保留字定义一个函数。
- python爬虫实例解析-Python(爬虫)- 动态加载案例分析
- python写错了怎么更改-Python中如何修改文件?Python文件修改方法
- python入门教程2word-python操作word入门