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信息结构如下:
- <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>
-
- int32_t version<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>
-
- uint32_t hash<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>
-
- uint32_t flag<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>
-
- time_t modified<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>
-
- <SPAN style="COLOR: rgb(0,153,0)">}</SPAN> Meta<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
typedef struct t_meta {
int32_t version;//版本号
uint32_t hash;//value的hash值
uint32_t flag;//memcached协议中的flag字段
time_t modified;//最后修改时间
} Meta;
2、存储的核心结构式hstore,定义如下:
- <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_hstore <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>
- <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>
- <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(51,153,51)">;</SPAN>
- bool stop_scan<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
- bool <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>scanning<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
- TCHDB <SPAN style="COLOR: rgb(51,153,51)">**</SPAN>db<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
- HTree <SPAN style="COLOR: rgb(51,153,51)">**</SPAN>tree<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
- pthread_mutex_t <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>mutex<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
- TCMDB <SPAN style="COLOR: rgb(51,153,51)">**</SPAN>cache<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
- <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
struct t_hstore {
int height;//
int start;
int end;
bool stop_scan;
bool *scanning;
TCHDB **db;
HTree **tree;
pthread_mutex_t *mutex;
TCMDB **cache;
};
涉及的操作函数如下:
- 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>
HStore* hs_open(char *path, int height, int start, int end)
默认的情况下,参数height=1,start=0,end=-1,这使得创建的store->height=1, store->start =0, store->end =16;也就是说,默认会创建16个hdb文件及对应的db、htree和cache结构,这样做可以减少读写的锁冲突,也方便htree比较等。
- <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>
char *hs_get(HStore *store, char *key, int *vlen, uint32_t *flag)
读数据。读请求有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信息。
- 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>
bool hs_set(HStore *store, char *key, char* value, int vlen, int ver, uint32_t flag)
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。
- 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>
bool hs_delete(HStore *store, char *key)
删除数据的过程是:
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、下面给出核心的几个数据结构:
- <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>
-
- <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_item <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>
-
- uint32_t keyhash<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- uint32_t hash<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>
-
- <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>
-
- <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>
-
- <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>
-
- <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- <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>
-
- <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_data <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>
-
- <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>
-
- <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 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>
-
- <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- <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>
-
- <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_node <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>
-
- 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>
-
- 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>
-
- 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>
-
- 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>
-
- 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>
-
- uint16_t hash<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>
-
- uint32_t count<SPAN style="COLOR: rgb(51,153,51)">;</SPAN><SPAN style="COLOR: rgb(102,102,102)"><EM>
-
- <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- <SPAN style="COLOR: rgb(153,51,51)">struct</SPAN> t_hash_tree <SPAN style="COLOR: rgb(0,153,0)">{</SPAN>
-
- <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>
-
- <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>
-
- TCHDB <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>db<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- Node <SPAN style="COLOR: rgb(51,153,51)">*</SPAN>root<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- Data <SPAN style="COLOR: rgb(51,153,51)">**</SPAN>data<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- <SPAN style="COLOR: rgb(153,51,51)">int</SPAN> pool_size<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- pthread_mutex_t lock<SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
-
- <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>
-
- <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>
-
- <SPAN style="COLOR: rgb(0,153,0)">}</SPAN><SPAN style="COLOR: rgb(51,153,51)">;</SPAN>
typedef struct t_item Item;
struct t_item {
uint32_t keyhash;
uint32_t hash;//value hash
short ver;//版本号
unsigned char length;//item总长度
char name[1];//key,name是变长的,= sizeof(Item) + n。
};
typedef struct t_data Data;
struct t_data {
int size;//整个结构的总大小
int count;//item个数
Item head[0];//item列表,由count及item结构中length可遍历得到列表数据
};
typedef struct t_node Node;
struct t_node {
uint16_t is_node:1;
uint16_t valid:1;
uint16_t modified:1;
uint16_t depth:4;//节点所在的深度,根是0,叶子是tree->height-1
uint16_t flag:9;
uint16_t hash;//所有子节点的hash总和
uint32_t count;//所有子节点的count和,如果是叶子节点,则为data->count
};
struct t_hash_tree {
int depth;//就是hstore的height,默认是1
int height;//高度,由节点数可得到,随着节点数的增加而增加
TCHDB *db;
Node *root;
Data **data;
int pool_size;
pthread_mutex_t lock;
char keybuf[30];
char buf[512];
};
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个定位函数:
- <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>
inline uint32_t get_pos(HTree *tree, Node *node)
得到node相对于同一depth下的第一个节点的偏移位置。
- <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>
inline Node *get_child(HTree *tree, Node *node, int b)
得到node节点的第i个子节点。
- <SPAN style="COLOR: rgb(51,153,51)">#define INDEX(it) (0x0f & ((it)->keyhash >> ((7 - node->depth - tree->depth) * 4)))</SPAN>
#define INDEX(it) (0x0f & ((it)->keyhash >> ((7 - node->depth - tree->depth) * 4)))
得到item的index位置(0-15),在node的不同层次,使用keyhash的不同的4位值做与结果。这个函数使得,当向hashtree添加新的data时,自根节点向下,通过node->depth的不同计算出其子节点index,并最终将数据保存在叶子节点上。
6、下面介绍几个核心函数的功能:
- 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* ht_open(char *path, int depth);
打开htree索引文件,这在程序初始化阶段调用。在该函数中,通过调用get_max_pos函数遍历索引文件,得到max_pos,并以此确定内存需要存储的Node节点和Data的pool_size。因为Node节点和Data都要存储在内存中,所以当数据量很大时还是会消耗很多内存的。
- <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>
static void *load_node(HTree *tree, Node *node)
加载node对应的data,如果node不是叶子节点返回NULL,如果tree->data不为空直接返回data,否则从db加载数据并校验有效性,有效时set数据到tree->data中相应的index,并返回数据。
- <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>
static void update_node(HTree *tree, Node *node);
递归更新node的数据(hash、count),它是自底向上重新计算node的count和hash,叶子节点需要调用load_node加载数据。
- <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>
static void save_node(HTree *tree, Node *node)
这同样是个递归操作,当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实现得更完整些。
==================== 华丽的终止符 ===================