[转]豆瓣beansdb源码浅析 .

http://blog.csdn.net/heiyeshuwu/article/details/7836671

作者:kafka0102
来源:http://www.kafka0102.com/2010/02/54.html


1、Beansdb是什么?

Beansdb是豆瓣荣誉出品的分布式key-value存储系统,该系统是对经典的Dynamo的简化。项目地址:http://code.google.com/p/beansdb/,其上的Inside BeansDB.pdf文档是对beansdb的很好的介绍。

Beansdb的CAP特点表现为:

1)分布式的,伸缩性比较好(P)

2)最终一致的(C),可能出现短时间内的数据不一致。

3)高可用的(A),部分节点出现故障不影响服务。

Beansdb在豆瓣内部有着广泛的使用,比如图片文件、小媒体文件、profile、properties等等。Beansdb不像GFS等分布式存储系统,一般不用于存储百兆以上单位的大数据。

2、Beansdb功能组成

Beansdb代码由两部分组成:1)c代码实现的服务器端程序,2)一些python脚本实现的程序。

我当前分析的版本是0.3,下面分别介绍其功能。

2.1服务器端程序

服务器端程序实现的功能如下:

1、基于tokyo cabinet实现key-value存储功能。为了提高性能,在存储数据时,Beansdb先将数据快速的存储到tcmdb(内存db)中,由独立线程异步的将tcmdb中的数据更新到tchdb中。可见,在非正常情况下,Beansdb是有可能丢失数据的,这种情况就需要对等节点同步数据来修复。

2、为了解决对等系统中的提交的一致性问题,Beansdb采用版本号+修改的时间戳来标识数据。提交的策略是:高版本号覆盖低版本号,新数据覆盖旧数据。

3、Beansdb使用hashtree来diff两个数据节点的数据一致性。Hashtree中保存的hash数据会固化到磁盘上,同时为了提高性能,在内存中会完整的构造hashtree结构,hashtree的更新也是由独立线程异步更新(在新的查询或者提交前会再次尝试更新hashtree,保证hashtree的实时性,如果hashtree已是valid,这个操作相当于空操作)。

4、Beansdb对外提供兼容memcached协议的接口。Beansdb额外提供了两个命令:get @xxx用来返回hastree的状态,get ?xxxx用来返回对应key的meta信息,这两个命令辅助不同节点间hashtree的diff及数据同步。

小结:Beansdb的服务器端程序并没有像cassandra等提供一套集数据存储、数据复制、数据校验、对等节点管理等分布式功能,它只是实现一个单机key-value存储server和hashtree。从选择方面来说,如果使用如tokyo tyrant等现成的key-value存储server,beansdb也不必自己再使用memcached+tc造遍轮子。而服务器端的hashtree只提供了存储,对等节点的diff需要由外部脚本完成。

2.2 Python脚本程序

相比于c的服务器端程序,Beansdb的几个python脚本程序看起来有些不够工业化。下面简要的说明一下核心的python脚本提供的功能。

2.2.1 proxy.py

Proxy.py这个代理程序使得beansdb具有了分布式特点,不过这个脚本还是太朴素了些,它提供的功能如下:

1、客户端程序的请求都走到Proxy.py,由Proxy.py将请求分发到相应的存储服务器程序。不像Dynamo中的复杂的分布式一致性hash策略,proxy.py按照trunk手动分配数据的分布。比如,在桶数16时,DB = {“A:7900″:range(8), “B:7900”:range(8,16), “B:7900”:range(16)….}的配置使得,节点A的数据分布在桶0-7,节点B的数据分布在桶8-15,节点C的数据分布在桶0-15。和分布式一致性hash对比,这相当于虚拟节点个数是16,而每个虚拟节点对应的物理存储节点是已知不变的。Proxy.py将请求的key取hash后模桶数,以得到该桶中对应的节点列表。这样做的好处是:不像分布式一致性hash那样,数据的分布情况不是透明的,不能够手动迁移数据。缺点是:这种手动策略不适合大型的分布式集群,添加删除机器需要人工干预。

2、Proxy.py当前提供的NWR是N=3, R=1, W=1。读时只要从一个节点读到数据就返回,写时只要写到一个节点就表示写成功。这种策略虽然有些粗暴,不过也是最实时高效的,对于准确性要求不高的应用场景来说,也是可以接受的。

2.2.1 sync.py

sync.py脚本定时比较位于一个桶中的各存储节点的hashtree的hash是否相同。这个比较是从hashtree的根节点开始的,如果根节点相同就不需要继续比较,否则递归向下比较到hash值不同的叶子节点,diff出value不同时,由高版本节点数据覆盖低版本的节点数据。

3、服务器端程序分析

3.1 key-value存储分析

Beansdb基于tokyo cabinet的tcmdb和tchdb实现存储功能。实现代码位于hstore.h/hstroe.c文件。实现策略及特点如下:

1、存储的value数据除了提交的真实数据,还在数据后附加了Meta信息,Meta信息用于提交时的冲突解决。Meta信息结构如下:

 

  1. <SPAN style="COLOR: rgb(153,51,51)">typedef</SPAN> <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_meta <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>  
  2.    
  3. int32_t  version<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//版本号</EM></SPAN>  
  4.    
  5. uint32_t hash<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//value的hash值</EM></SPAN>  
  6.    
  7. uint32_t flag<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//memcached协议中的flag字段</EM></SPAN>  
  8.    
  9. time_t   modified<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//最后修改时间</EM></SPAN>  
  10.    
  11. <SPAN style="COLOR: rgb(0,153,0)">}</SPAN> Meta<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  

 

2、存储的核心结构式hstore,定义如下:

 

  1. <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_hstore <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>  
  2.     <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> height<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//</EM></SPAN>  
  3.     <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> start<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  4.     <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> end<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  5.     bool stop_scan<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  6.     bool <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>scanning<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  7.     TCHDB <SPAN style="COLOR: rgb(51,153,51)">**</SPAN>db<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  8.     HTree <SPAN style="COLOR: rgb(51,153,51)">**</SPAN>tree<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  9.     pthread_mutex_t <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>mutex<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  10.     TCMDB <SPAN style="COLOR: rgb(51,153,51)">**</SPAN>cache<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  11. <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  

 

涉及的操作函数如下:

 

  1. HStore<SPAN style="COLOR: rgb(51,153,51)">*</SPAN> hs_open<SPAN style="COLOR: rgb(0,153,0)">(</SPAN><SPAN style="COLOR: rgb(153,51,51)">char</SPAN> <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>path<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> height<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> start<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> end<SPAN style="COLOR: rgb(0,153,0)">)</SPAN>  

 

默认的情况下,参数height=1,start=0,end=-1,这使得创建的store->height=1, store->start =0, store->end =16;也就是说,默认会创建16个hdb文件及对应的db、htree和cache结构,这样做可以减少读写的锁冲突,也方便htree比较等。

 

  1. <SPAN style="COLOR: rgb(153,51,51)">char</SPAN> <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>hs_get<SPAN style="COLOR: rgb(0,153,0)">(</SPAN>HStore <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>store<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">char</SPAN> <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>key<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>vlen<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> uint32_t <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>flag<SPAN style="COLOR: rgb(0,153,0)">)</SPAN>  

 

读数据。读请求有3种:

1)@请求读hashtree的状态信息。@请求有两种,一种是读16个桶文件各自总的状态信息,一种是读单个桶的所有的key的状态信息。图示如下:

这个图是命令“get @”,图中的0-f列表示16个db桶,每行的第二列表示总的hash,第三列表示item个数。第一行key @对应的是flag(0)和value的长度(132)。

再看下张图:

命令是“get @e”,表示查看桶e的信息,每行的列含义分别是key、hash、version。

2)?请求读key对应的value的Meta信息。

3)普通的读。普通的读的过程是:先计算出key对应到哪个桶中,然后读tcmdb,不存在时再读tchdb,得到的结果包含Meta信息。

 

  1. bool hs_set<SPAN style="COLOR: rgb(0,153,0)">(</SPAN>HStore <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>store<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">char</SPAN> <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>key<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">char</SPAN><SPAN style="COLOR: rgb(51,153,51)">*</SPAN> value<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> vlen<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> ver<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> uint32_t flag<SPAN style="COLOR: rgb(0,153,0)">)</SPAN>  

 

set数据的过程是:

1)计算出key在store中的桶index。

2)从index所在的hashtree中查找是否已有该key的item数据old_ver、old_hash。

3)计算value的hash,如果hash值等于old_hash,从tcmdb或者tchdb中查找该key对应的oldv,如果和value相同,表示数据没有变化。

4)如果数据有变化,存储数据到tcmdb中,格式是:value+ Meta。

5)调用ht_add来更新hashtree。

 

  1. bool hs_delete<SPAN style="COLOR: rgb(0,153,0)">(</SPAN>HStore <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>store<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">char</SPAN> <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>key<SPAN style="COLOR: rgb(0,153,0)">)</SPAN>  

 

删除数据的过程是:

1)计算出key在store中的桶index。

2)调用ht_remove删除hashtree中所在的data。

3)删除tcmdb和tchdb中的数据。

3.2 hashtree实现分析

Beansdb使用hashtree来校验不同数据,分析如下:

1、实现上,hashtree类似于heap结构,用到了两个大数组,一个存储hashtree的节点结构,一个存储hashtree中叶子节点的data数据。叶子节点的数据序列化后存储在tchdb中(文件名是.[number].index)。

2、Hashtree的每个非叶子节点有16个子节点,每个叶子节点最多存储128个data数据项。当叶子节点存储的data数据项超过128时,就会将叶子节点分化成非叶子节点并调整树结构。

3、下面给出核心的几个数据结构:

 

  1. <SPAN style="COLOR: rgb(153,51,51)">typedef</SPAN> <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_item Item<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  2.    
  3. <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_item <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>  
  4.    
  5. uint32_t keyhash<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  6.    
  7. uint32_t hash<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//value hash</EM></SPAN>  
  8.    
  9. <SPAN style="COLOR: rgb(153,51,51)">short</SPAN>    ver<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//版本号</EM></SPAN>  
  10.    
  11. <SPAN style="COLOR: rgb(153,51,51)">unsigned</SPAN> <SPAN style="COLOR: rgb(153,51,51)">char</SPAN> length<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//item总长度</EM></SPAN>  
  12.    
  13. <SPAN style="COLOR: rgb(153,51,51)">char</SPAN>     name<SPAN style="COLOR: rgb(0,153,0)">[</SPAN><SPAN style="COLOR: rgb(0,0,221)">1</SPAN><SPAN style="COLOR: rgb(0,153,0)">]</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//key,name是变长的,= sizeof(Item) + n。</EM></SPAN>  
  14.    
  15. <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  16.    
  17. <SPAN style="COLOR: rgb(153,51,51)">typedef</SPAN> <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_data Data<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  18.    
  19. <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_data <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>  
  20.    
  21. <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> size<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//整个结构的总大小</EM></SPAN>  
  22.    
  23. <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> count<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//item个数</EM></SPAN>  
  24.    
  25. Item head<SPAN style="COLOR: rgb(0,153,0)">[</SPAN><SPAN style="COLOR: rgb(0,0,221)">0</SPAN><SPAN style="COLOR: rgb(0,153,0)">]</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//item列表,由count及item结构中length可遍历得到列表数据</EM></SPAN>  
  26.    
  27. <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  28.    
  29. <SPAN style="COLOR: rgb(153,51,51)">typedef</SPAN> <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_node Node<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  30.    
  31. <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_node <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>  
  32.    
  33. uint16_t is_node<SPAN style="COLOR: rgb(51,153,51)">:</SPAN><SPAN style="COLOR: rgb(0,0,221)">1</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  34.    
  35. uint16_t valid<SPAN style="COLOR: rgb(51,153,51)">:</SPAN><SPAN style="COLOR: rgb(0,0,221)">1</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  36.    
  37. uint16_t modified<SPAN style="COLOR: rgb(51,153,51)">:</SPAN><SPAN style="COLOR: rgb(0,0,221)">1</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  38.    
  39. uint16_t depth<SPAN style="COLOR: rgb(51,153,51)">:</SPAN><SPAN style="COLOR: rgb(0,0,221)">4</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//节点所在的深度,根是0,叶子是tree-&gt;height-1</EM></SPAN>  
  40.    
  41. uint16_t flag<SPAN style="COLOR: rgb(51,153,51)">:</SPAN><SPAN style="COLOR: rgb(0,0,221)">9</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  42.    
  43. uint16_t hash<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//所有子节点的hash总和</EM></SPAN>  
  44.    
  45. uint32_t count<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//所有子节点的count和,如果是叶子节点,则为data-&gt;count</EM></SPAN>  
  46.    
  47. <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  48.    
  49. <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_hash_tree <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>  
  50.    
  51. <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> depth<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//就是hstore的height,默认是1</EM></SPAN>  
  52.    
  53. <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> height<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>//高度,由节点数可得到,随着节点数的增加而增加</EM></SPAN>  
  54.    
  55. TCHDB <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>db<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  56.    
  57. Node <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>root<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  58.    
  59. Data <SPAN style="COLOR: rgb(51,153,51)">**</SPAN>data<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  60.    
  61. <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> pool_size<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  62.    
  63. pthread_mutex_t lock<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  64.    
  65. <SPAN style="COLOR: rgb(153,51,51)">char</SPAN> keybuf<SPAN style="COLOR: rgb(0,153,0)">[</SPAN><SPAN style="COLOR: rgb(0,0,221)">30</SPAN><SPAN style="COLOR: rgb(0,153,0)">]</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  66.    
  67. <SPAN style="COLOR: rgb(153,51,51)">char</SPAN> buf<SPAN style="COLOR: rgb(0,153,0)">[</SPAN><SPAN style="COLOR: rgb(0,0,221)">512</SPAN><SPAN style="COLOR: rgb(0,153,0)">]</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  
  68.    
  69. <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  

 

4、对于hashtree在tchdb中存储的data,key是节点在Node大数组中的位置,value是序列化的Data结构数据。

5、hashtree中存在全局数组:static const int g_index[] = {0, 1, 17, 289, 4913, 83521, 1419857, 24137569, 410338673};。可以看到,g_index中的每个值表示hashtree中相应depth(起始是0,也就是数组的索引)下的最小节点位置。也可以看到,beansdb可以存储的项数是在整数范围内。在hashtree中,经常会用到下面3个定位函数:

 

  1. <SPAN style="COLOR: rgb(0,0,0)"><STRONG>inline</STRONG></SPAN> uint32_t get_pos<SPAN style="COLOR: rgb(0,153,0)">(</SPAN>HTree <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>tree<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> Node <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>node<SPAN style="COLOR: rgb(0,153,0)">)</SPAN>  

 

得到node相对于同一depth下的第一个节点的偏移位置。

 

  1. <SPAN style="COLOR: rgb(0,0,0)"><STRONG>inline</STRONG></SPAN> Node <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>get_child<SPAN style="COLOR: rgb(0,153,0)">(</SPAN>HTree <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>tree<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> Node <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>node<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> b<SPAN style="COLOR: rgb(0,153,0)">)</SPAN>  

 

得到node节点的第i个子节点。

 

  1. <SPAN style="COLOR: rgb(51,153,51)">#define INDEX(it) (0x0f & ((it)->keyhash >> ((7 - node->depth - tree->depth) * 4)))</SPAN>  

 

得到item的index位置(0-15),在node的不同层次,使用keyhash的不同的4位值做与结果。这个函数使得,当向hashtree添加新的data时,自根节点向下,通过node->depth的不同计算出其子节点index,并最终将数据保存在叶子节点上。

6、下面介绍几个核心函数的功能:

 

  1. HTree<SPAN style="COLOR: rgb(51,153,51)">*</SPAN>   ht_open<SPAN style="COLOR: rgb(0,153,0)">(</SPAN><SPAN style="COLOR: rgb(153,51,51)">char</SPAN> <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>path<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> depth<SPAN style="COLOR: rgb(0,153,0)">)</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  

 

打开htree索引文件,这在程序初始化阶段调用。在该函数中,通过调用get_max_pos函数遍历索引文件,得到max_pos,并以此确定内存需要存储的Node节点和Data的pool_size。因为Node节点和Data都要存储在内存中,所以当数据量很大时还是会消耗很多内存的。

 

  1. <SPAN style="COLOR: rgb(153,51,51)">static</SPAN> <SPAN style="COLOR: rgb(153,51,51)">void</SPAN> <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>load_node<SPAN style="COLOR: rgb(0,153,0)">(</SPAN>HTree <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>tree<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> Node <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>node<SPAN style="COLOR: rgb(0,153,0)">)</SPAN>  

 

加载node对应的data,如果node不是叶子节点返回NULL,如果tree->data不为空直接返回data,否则从db加载数据并校验有效性,有效时set数据到tree->data中相应的index,并返回数据。

 

  1. <SPAN style="COLOR: rgb(153,51,51)">static</SPAN> <SPAN style="COLOR: rgb(153,51,51)">void</SPAN> update_node<SPAN style="COLOR: rgb(0,153,0)">(</SPAN>HTree <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>tree<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> Node <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>node<SPAN style="COLOR: rgb(0,153,0)">)</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>  

 

递归更新node的数据(hash、count),它是自底向上重新计算node的count和hash,叶子节点需要调用load_node加载数据。

 

  1. <SPAN style="COLOR: rgb(153,51,51)">static</SPAN> <SPAN style="COLOR: rgb(153,51,51)">void</SPAN> save_node<SPAN style="COLOR: rgb(0,153,0)">(</SPAN>HTree <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>tree<SPAN style="COLOR: rgb(51,153,51)">,</SPAN> Node <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>node<SPAN style="COLOR: rgb(0,153,0)">)</SPAN>  

 

这同样是个递归操作,当node->modified为真,将数据更新到tchdb中,并调用update_node重新计算hash值,并释放掉data内存资源。

对于hashtree的add、delete、get操作,都会联合使用update_node和save_node保证内存数据及db数据的准确性。

3.3 服务端处理模型分析

服务端处理模型分为3部分,说明如下:

1、处理请求部分是由一个主线程+N个work线程构成。处理客户端请求代码是从memcached照搬过来的。主线程负责监听客户端请求,当accept后通过轮询方式将客户端fd扔到某个work线程的连接队列里,并通过pipe向work线程发送通知。Work线程处理的事件有两种:1是主线程发来的新的客户端连接请求,2是长连接情况下保持的客户端连接请求,其处理过程就是个状态机。这部分的代码不做分析,倘有时间,可以对memcached源码做更细致的分析。

2、flush线程,它定期遍历tcmdb,将tcmdb中的数据保存到tchdb中,并更新tcmdb和htree。

2、Check线程,它除了做flush线程的工作,还检查db数据和htree数据的一致性,当数据项个数差别很大时,将db数据信息更新到htree。窃猜测,这可能是htree损坏时会产生这种情况。

4、总结

写了这么多,其实并没有将源码整个托盘出来。像beansdb,代码算不上很多,如果是粗略的分析也用不上多少时间,不过要把整个代码的细节都照顾到还是需要一些耐心和时间的。纵览beansdb的代码,还是很清晰质量不错的。而就功能来说,它的proxy是可以用C实现得更完整些。


==================== 华丽的终止符 ===================

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值