<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>kafka0102的边城客栈 &#187; 豆瓣</title>
	<atom:link href="http://www.kafka0102.com/tag/%e8%b1%86%e7%93%a3/feed" rel="self" type="application/rss+xml" />
	<link>http://www.kafka0102.com</link>
	<description>要有最朴素的生活与最遥远的梦想，即使明日天寒地冻、路远马亡。</description>
	<lastBuildDate>Sat, 18 Jun 2011 04:20:42 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>豆瓣beansdb源码浅析</title>
		<link>http://www.kafka0102.com/2010/02/54.html</link>
		<comments>http://www.kafka0102.com/2010/02/54.html#comments</comments>
		<pubDate>Sat, 20 Feb 2010 10:04:03 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[nosql]]></category>
		<category><![CDATA[beansdb]]></category>
		<category><![CDATA[douban]]></category>
		<category><![CDATA[douban db]]></category>
		<category><![CDATA[源码分析]]></category>
		<category><![CDATA[豆瓣]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=54</guid>
		<description><![CDATA[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等分布式存储系统，一般不用于存储百兆以上单位的大数据。]]></description>
			<content:encoded><![CDATA[<h1>1、Beansdb是什么？</h1>
<p>Beansdb是豆瓣荣誉出品的分布式key-value存储系统，该系统是对经典的Dynamo的简化。项目地址：<a href="http://code.google.com/p/beansdb/">http://code.google.com/p/beansdb/</a>，其上的<a href="http://beansdb.googlecode.com/files/Inside%20BeansDB.pdf">Inside BeansDB.pdf</a>文档是对beansdb的很好的介绍。</p>
<p>Beansdb的CAP特点表现为：</p>
<p>1）分布式的，伸缩性比较好（P）</p>
<p>2）最终一致的(C)，可能出现短时间内的数据不一致。</p>
<p>3）高可用的(A)，部分节点出现故障不影响服务。</p>
<p>Beansdb在豆瓣内部有着广泛的使用，比如图片文件、小媒体文件、profile、properties等等。Beansdb不像GFS等分布式存储系统，一般不用于存储百兆以上单位的大数据。</p>
<h1>2、Beansdb功能组成</h1>
<p>Beansdb代码由两部分组成：1）c代码实现的服务器端程序，2）一些python脚本实现的程序。</p>
<p>我当前分析的版本是0.3，下面分别介绍其功能。</p>
<h2>2.1服务器端程序</h2>
<p>服务器端程序实现的功能如下：</p>
<p>1、基于tokyo cabinet实现key-value存储功能。为了提高性能，在存储数据时，Beansdb先将数据快速的存储到tcmdb（内存db）中，由独立线程异步的将tcmdb中的数据更新到tchdb中。可见，在非正常情况下，Beansdb是有可能丢失数据的，这种情况就需要对等节点同步数据来修复。</p>
<p>2、为了解决对等系统中的提交的一致性问题，Beansdb采用版本号+修改的时间戳来标识数据。提交的策略是：高版本号覆盖低版本号，新数据覆盖旧数据。</p>
<p>3、Beansdb使用hashtree来diff两个数据节点的数据一致性。Hashtree中保存的hash数据会固化到磁盘上，同时为了提高性能，在内存中会完整的构造hashtree结构，hashtree的更新也是由独立线程异步更新（在新的查询或者提交前会再次尝试更新hashtree，保证hashtree的实时性，如果hashtree已是valid，这个操作相当于空操作）。</p>
<p>4、Beansdb对外提供兼容memcached协议的接口。Beansdb额外提供了两个命令：get @xxx用来返回hastree的状态，get ?xxxx用来返回对应key的meta信息，这两个命令辅助不同节点间hashtree的diff及数据同步。</p>
<p>小结：Beansdb的服务器端程序并没有像cassandra等提供一套集数据存储、数据复制、数据校验、对等节点管理等分布式功能，它只是实现一个单机key-value存储server和hashtree。从选择方面来说，如果使用如tokyo tyrant等现成的key-value存储server，beansdb也不必自己再使用memcached+tc造遍轮子。而服务器端的hashtree只提供了存储，对等节点的diff需要由外部脚本完成。</p>
<h2>2.2 Python脚本程序</h2>
<p>相比于c的服务器端程序，Beansdb的几个python脚本程序看起来有些不够工业化。下面简要的说明一下核心的python脚本提供的功能。</p>
<h3>2.2.1 proxy.py</h3>
<p>Proxy.py这个代理程序使得beansdb具有了分布式特点，不过这个脚本还是太朴素了些，它提供的功能如下：</p>
<p>1、客户端程序的请求都走到Proxy.py，由Proxy.py将请求分发到相应的存储服务器程序。不像Dynamo中的复杂的分布式一致性hash策略，proxy.py按照trunk手动分配数据的分布。比如，在桶数16时，DB = {“A:7900&#8243;:range(8), “B:7900”:range(8,16), “B:7900”:range(16)&#8230;.}的配置使得，节点A的数据分布在桶0-7，节点B的数据分布在桶8-15，节点C的数据分布在桶0-15。和分布式一致性hash对比，这相当于虚拟节点个数是16，而每个虚拟节点对应的物理存储节点是已知不变的。Proxy.py将请求的key取hash后模桶数，以得到该桶中对应的节点列表。这样做的好处是：不像分布式一致性hash那样，数据的分布情况不是透明的，不能够手动迁移数据。缺点是：这种手动策略不适合大型的分布式集群，添加删除机器需要人工干预。</p>
<p>2、Proxy.py当前提供的NWR是N=3, R=1, W=1。读时只要从一个节点读到数据就返回，写时只要写到一个节点就表示写成功。这种策略虽然有些粗暴，不过也是最实时高效的，对于准确性要求不高的应用场景来说，也是可以接受的。</p>
<h3>2.2.1 sync.py</h3>
<p>sync.py脚本定时比较位于一个桶中的各存储节点的hashtree的hash是否相同。这个比较是从hashtree的根节点开始的，如果根节点相同就不需要继续比较，否则递归向下比较到hash值不同的叶子节点，diff出value不同时，由高版本节点数据覆盖低版本的节点数据。</p>
<h1>3、服务器端程序分析</h1>
<h2>3.1 key-value存储分析</h2>
<p>Beansdb基于tokyo cabinet的tcmdb和tchdb实现存储功能。实现代码位于hstore.h/hstroe.c文件。实现策略及特点如下：</p>
<p>1、存储的value数据除了提交的真实数据，还在数据后附加了Meta信息，Meta信息用于提交时的冲突解决。Meta信息结构如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> t_meta <span style="color: #009900;">&#123;</span>
&nbsp;
int32_t  version<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//版本号</span>
&nbsp;
uint32_t hash<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//value的hash值</span>
&nbsp;
uint32_t flag<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//memcached协议中的flag字段</span>
&nbsp;
time_t   modified<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//最后修改时间</span>
&nbsp;
<span style="color: #009900;">&#125;</span> Meta<span style="color: #339933;">;</span></pre></div></div>

<p>2、存储的核心结构式hstore，定义如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">struct</span> t_hstore <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">int</span> height<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//</span>
    <span style="color: #993333;">int</span> start<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> end<span style="color: #339933;">;</span>
    bool stop_scan<span style="color: #339933;">;</span>
    bool <span style="color: #339933;">*</span>scanning<span style="color: #339933;">;</span>
    TCHDB <span style="color: #339933;">**</span>db<span style="color: #339933;">;</span>
    HTree <span style="color: #339933;">**</span>tree<span style="color: #339933;">;</span>
    pthread_mutex_t <span style="color: #339933;">*</span>mutex<span style="color: #339933;">;</span>
    TCMDB <span style="color: #339933;">**</span>cache<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>涉及的操作函数如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">HStore<span style="color: #339933;">*</span> hs_open<span style="color: #009900;">&#40;</span><span style="color: #993333;">char</span> <span style="color: #339933;">*</span>path<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> height<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> start<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> end<span style="color: #009900;">&#41;</span></pre></div></div>

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

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">char</span> <span style="color: #339933;">*</span>hs_get<span style="color: #009900;">&#40;</span>HStore <span style="color: #339933;">*</span>store<span style="color: #339933;">,</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>key<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>vlen<span style="color: #339933;">,</span> uint32_t <span style="color: #339933;">*</span>flag<span style="color: #009900;">&#41;</span></pre></div></div>

<p>读数据。读请求有3种：</p>
<p>1）@请求读hashtree的状态信息。@请求有两种，一种是读16个桶文件各自总的状态信息，一种是读单个桶的所有的key的状态信息。图示如下：</p>
<p><a href="http://www.kafka0102.com/wp-content/uploads/2010/02/beansdb-get@.jpg"><img class="aligncenter size-full wp-image-57" title="beansdb get@" src="http://www.kafka0102.com/wp-content/uploads/2010/02/beansdb-get@.jpg" alt="" width="122" height="248" /></a>这个图是命令“get @”，图中的0-f列表示16个db桶，每行的第二列表示总的hash，第三列表示item个数。第一行key @对应的是flag（0）和value的长度（132）。</p>
<p>再看下张图：</p>
<p><a href="http://www.kafka0102.com/wp-content/uploads/2010/02/beansdb-get@e.jpg"><img class="aligncenter size-full wp-image-59" title="beansdb get@e" src="http://www.kafka0102.com/wp-content/uploads/2010/02/beansdb-get@e.jpg" alt="" width="152" height="96" /></a>命令是“get @e”，表示查看桶e的信息，每行的列含义分别是key、hash、version。</p>
<p>2）?请求读key对应的value的Meta信息。</p>
<p>3）普通的读。普通的读的过程是：先计算出key对应到哪个桶中，然后读tcmdb，不存在时再读tchdb，得到的结果包含Meta信息。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">bool hs_set<span style="color: #009900;">&#40;</span>HStore <span style="color: #339933;">*</span>store<span style="color: #339933;">,</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>key<span style="color: #339933;">,</span> <span style="color: #993333;">char</span><span style="color: #339933;">*</span> value<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> vlen<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> ver<span style="color: #339933;">,</span> uint32_t flag<span style="color: #009900;">&#41;</span></pre></div></div>

<p>set数据的过程是：</p>
<p>1）计算出key在store中的桶index。</p>
<p>2）从index所在的hashtree中查找是否已有该key的item数据old_ver、old_hash。</p>
<p>3）计算value的hash，如果hash值等于old_hash，从tcmdb或者tchdb中查找该key对应的oldv，如果和value相同，表示数据没有变化。</p>
<p>4）如果数据有变化，存储数据到tcmdb中，格式是：value+ Meta。</p>
<p>5）调用ht_add来更新hashtree。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">bool hs_delete<span style="color: #009900;">&#40;</span>HStore <span style="color: #339933;">*</span>store<span style="color: #339933;">,</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>key<span style="color: #009900;">&#41;</span></pre></div></div>

<p>删除数据的过程是：</p>
<p>1）计算出key在store中的桶index。</p>
<p>2）调用ht_remove删除hashtree中所在的data。</p>
<p>3）删除tcmdb和tchdb中的数据。</p>
<h2>3.2 hashtree实现分析</h2>
<p>Beansdb使用hashtree来校验不同数据，分析如下：</p>
<p>1、实现上，hashtree类似于heap结构，用到了两个大数组，一个存储hashtree的节点结构，一个存储hashtree中叶子节点的data数据。叶子节点的数据序列化后存储在tchdb中（文件名是.[number].index）。</p>
<p>2、Hashtree的每个非叶子节点有16个子节点，每个叶子节点最多存储128个data数据项。当叶子节点存储的data数据项超过128时，就会将叶子节点分化成非叶子节点并调整树结构。</p>
<p>3、下面给出核心的几个数据结构：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> t_item Item<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">struct</span> t_item <span style="color: #009900;">&#123;</span>
&nbsp;
uint32_t keyhash<span style="color: #339933;">;</span>
&nbsp;
uint32_t hash<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//value hash</span>
&nbsp;
<span style="color: #993333;">short</span>    ver<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//版本号</span>
&nbsp;
<span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span> length<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//item总长度</span>
&nbsp;
<span style="color: #993333;">char</span>     name<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//key，name是变长的，= sizeof(Item) + n。</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> t_data Data<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">struct</span> t_data <span style="color: #009900;">&#123;</span>
&nbsp;
<span style="color: #993333;">int</span> size<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//整个结构的总大小</span>
&nbsp;
<span style="color: #993333;">int</span> count<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//item个数</span>
&nbsp;
Item head<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//item列表，由count及item结构中length可遍历得到列表数据</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> t_node Node<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">struct</span> t_node <span style="color: #009900;">&#123;</span>
&nbsp;
uint16_t is_node<span style="color: #339933;">:</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
&nbsp;
uint16_t valid<span style="color: #339933;">:</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
&nbsp;
uint16_t modified<span style="color: #339933;">:</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
&nbsp;
uint16_t depth<span style="color: #339933;">:</span><span style="color: #0000dd;">4</span><span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//节点所在的深度，根是0，叶子是tree-&amp;gt;height-1</span>
&nbsp;
uint16_t flag<span style="color: #339933;">:</span><span style="color: #0000dd;">9</span><span style="color: #339933;">;</span>
&nbsp;
uint16_t hash<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//所有子节点的hash总和</span>
&nbsp;
uint32_t count<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//所有子节点的count和，如果是叶子节点，则为data-&amp;gt;count</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">struct</span> t_hash_tree <span style="color: #009900;">&#123;</span>
&nbsp;
<span style="color: #993333;">int</span> depth<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//就是hstore的height，默认是1</span>
&nbsp;
<span style="color: #993333;">int</span> height<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//高度，由节点数可得到，随着节点数的增加而增加</span>
&nbsp;
TCHDB <span style="color: #339933;">*</span>db<span style="color: #339933;">;</span>
&nbsp;
Node <span style="color: #339933;">*</span>root<span style="color: #339933;">;</span>
&nbsp;
Data <span style="color: #339933;">**</span>data<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">int</span> pool_size<span style="color: #339933;">;</span>
&nbsp;
pthread_mutex_t lock<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">char</span> keybuf<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">30</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">char</span> buf<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">512</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>4、对于hashtree在tchdb中存储的data，key是节点在Node大数组中的位置，value是序列化的Data结构数据。</p>
<p>5、hashtree中存在全局数组：static const int g_index[] = {0, 1, 17, 289, 4913, 83521, 1419857, 24137569, 410338673};。可以看到，g_index中的每个值表示hashtree中相应depth（起始是0，也就是数组的索引）下的最小节点位置。也可以看到，beansdb可以存储的项数是在整数范围内。在hashtree中，经常会用到下面3个定位函数：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">inline</span> uint32_t get_pos<span style="color: #009900;">&#40;</span>HTree <span style="color: #339933;">*</span>tree<span style="color: #339933;">,</span> Node <span style="color: #339933;">*</span>node<span style="color: #009900;">&#41;</span></pre></div></div>

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

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">inline</span> Node <span style="color: #339933;">*</span>get_child<span style="color: #009900;">&#40;</span>HTree <span style="color: #339933;">*</span>tree<span style="color: #339933;">,</span> Node <span style="color: #339933;">*</span>node<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> b<span style="color: #009900;">&#41;</span></pre></div></div>

<p>得到node节点的第i个子节点。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define INDEX(it) (0x0f &amp; ((it)-&gt;keyhash &gt;&gt; ((7 - node-&gt;depth - tree-&gt;depth) * 4)))</span></pre></div></div>

<p>得到item的index位置（0-15），在node的不同层次，使用keyhash的不同的4位值做与结果。这个函数使得，当向hashtree添加新的data时，自根节点向下，通过node-&gt;depth的不同计算出其子节点index，并最终将数据保存在叶子节点上。</p>
<p>6、下面介绍几个核心函数的功能：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">HTree<span style="color: #339933;">*</span>   ht_open<span style="color: #009900;">&#40;</span><span style="color: #993333;">char</span> <span style="color: #339933;">*</span>path<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> depth<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

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

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>load_node<span style="color: #009900;">&#40;</span>HTree <span style="color: #339933;">*</span>tree<span style="color: #339933;">,</span> Node <span style="color: #339933;">*</span>node<span style="color: #009900;">&#41;</span></pre></div></div>

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

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> update_node<span style="color: #009900;">&#40;</span>HTree <span style="color: #339933;">*</span>tree<span style="color: #339933;">,</span> Node <span style="color: #339933;">*</span>node<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>递归更新node的数据（hash、count），它是自底向上重新计算node的count和hash，叶子节点需要调用load_node加载数据。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> save_node<span style="color: #009900;">&#40;</span>HTree <span style="color: #339933;">*</span>tree<span style="color: #339933;">,</span> Node <span style="color: #339933;">*</span>node<span style="color: #009900;">&#41;</span></pre></div></div>

<p>这同样是个递归操作，当node-&gt;modified为真，将数据更新到tchdb中，并调用update_node重新计算hash值，并释放掉data内存资源。</p>
<p>对于hashtree的add、delete、get操作，都会联合使用update_node和save_node保证内存数据及db数据的准确性。</p>
<h2>3.3 服务端处理模型分析</h2>
<p>服务端处理模型分为3部分，说明如下：</p>
<p>1、处理请求部分是由一个主线程+N个work线程构成。处理客户端请求代码是从memcached照搬过来的。主线程负责监听客户端请求，当accept后通过轮询方式将客户端fd扔到某个work线程的连接队列里，并通过pipe向work线程发送通知。Work线程处理的事件有两种：1是主线程发来的新的客户端连接请求，2是长连接情况下保持的客户端连接请求，其处理过程就是个状态机。这部分的代码不做分析，倘有时间，可以对memcached源码做更细致的分析。</p>
<p>2、flush线程，它定期遍历tcmdb，将tcmdb中的数据保存到tchdb中，并更新tcmdb和htree。</p>
<p>2、Check线程，它除了做flush线程的工作，还检查db数据和htree数据的一致性，当数据项个数差别很大时，将db数据信息更新到htree。窃猜测，这可能是htree损坏时会产生这种情况。</p>
<h1>4、总结</h1>
<p>写了这么多，其实并没有将源码整个托盘出来。像beansdb，代码算不上很多，如果是粗略的分析也用不上多少时间，不过要把整个代码的细节都照顾到还是需要一些耐心和时间的。纵览beansdb的代码，还是很清晰质量不错的。而就功能来说，它的proxy是可以用C实现得更完整些。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/02/54.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

