<?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/%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90/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>[Solr源码分析]LRUCache和FastLRUCache实现分析</title>
		<link>http://www.kafka0102.com/2010/08/293.html</link>
		<comments>http://www.kafka0102.com/2010/08/293.html#comments</comments>
		<pubDate>Sun, 08 Aug 2010 16:01:42 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[cache]]></category>
		<category><![CDATA[solr]]></category>
		<category><![CDATA[HashMap]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[源码分析]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=293</guid>
		<description><![CDATA[	在 [Solr 实践]Solr Cache使用介绍及分析 一文我有对Solr的LRUCache和FastLRUCache做了一些介绍，本文在此基础对其实现做些补充。
1、LRUCache的实现分析
	在分析LRUCache前先对LinkedHashMap做些介绍。LinkedHashMap继承于HashMap，它使用了一个双向链表来存储Map中的Entry顺序关系，这种顺序有两种，一种是LRU顺序，一种是插入顺序，这可以由其构造函数public LinkedHashMap(int initialCapacity,float loadFactor,                   boolean accessOrder)指定。所以，对于get、put、remove等操作，LinkedHashMap除了要做HashMap做的事情，还做些调整Entry顺序链表的工作。
	以get操作为例，如果是LRU顺序（accessOrder为true），Entry的recordAccess方法就调整get到的Entry到链表的头部去：

   public V get&#40;Object key&#41; &#123;
        Entry&#60;K,V&#62; e = &#40;Entry&#60;K,V&#62;&#41;getEntry&#40;key&#41;;
        if &#40;e [...]]]></description>
			<content:encoded><![CDATA[<p>	在 <a href="http://www.kafka0102.com/2010/08/267.html" target="blank">[Solr 实践]Solr Cache使用介绍及分析</a> 一文我有对Solr的LRUCache和FastLRUCache做了一些介绍，本文在此基础对其实现做些补充。</p>
<h2>1、LRUCache的实现分析</h2>
<p>	在分析LRUCache前先对LinkedHashMap做些介绍。LinkedHashMap继承于HashMap，它使用了一个双向链表来存储Map中的Entry顺序关系，这种顺序有两种，一种是LRU顺序，一种是插入顺序，这可以由其构造函数public LinkedHashMap(int initialCapacity,float loadFactor,                   boolean accessOrder)指定。所以，对于get、put、remove等操作，LinkedHashMap除了要做HashMap做的事情，还做些调整Entry顺序链表的工作。<br />
	以get操作为例，如果是LRU顺序（accessOrder为true），Entry的recordAccess方法就调整get到的Entry到链表的头部去：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">   <span style="color: #000000; font-weight: bold;">public</span> V get<span style="color: #009900;">&#40;</span><span style="color: #003399;">Object</span> key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        Entry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span> e <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>Entry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#41;</span>getEntry<span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>e <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span>
            <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
        e.<span style="color: #006633;">recordAccess</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">this</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">return</span> e.<span style="color: #006633;">value</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span></pre></div></div>

<p>    对于put来说，LinkedHashMap重写了addEntry方法：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">   <span style="color: #000066; font-weight: bold;">void</span> addEntry<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> hash, K key, V value, <span style="color: #000066; font-weight: bold;">int</span> bucketIndex<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        createEntry<span style="color: #009900;">&#40;</span>hash, key, value, bucketIndex<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #666666; font-style: italic;">// Remove eldest entry if instructed, else grow capacity if appropriate</span>
        Entry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span> eldest <span style="color: #339933;">=</span> header.<span style="color: #006633;">after</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>removeEldestEntry<span style="color: #009900;">&#40;</span>eldest<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            removeEntryForKey<span style="color: #009900;">&#40;</span>eldest.<span style="color: #006633;">key</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>size <span style="color: #339933;">&gt;=</span> threshold<span style="color: #009900;">&#41;</span>
                resize<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">2</span> <span style="color: #339933;">*</span> table.<span style="color: #006633;">length</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span></pre></div></div>

<p>    addEntry中调用了boolean removeEldestEntry(Map.Entry<K,V> eldest)方法，默认实现一直返回false，也就是默认的Map是没有容量限制的。LinkedHashMap的子类可以复写该方法，当当前的size大于阈值时返回true，这样LinkedHashMap就可以从Entry顺序链表中删除最旧的Entry。这使得LinkedHashMap具有了Cache的功能，可以存储限量的元素，并具有两种可选的元素淘汰策略（LRU和FIFO），其中的LRU是最常用的。<br />
	Solr的LRUCache是基于LinkedHashMap实现的，所以LRUCache的实现真的很简单，这里列出其中核心的代码片断：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> init<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Map</span> args, <span style="color: #003399;">Object</span> persistence, <span style="color: #000000; font-weight: bold;">final</span> CacheRegenerator regenerator<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #666666; font-style: italic;">//一堆解析参数参数初始化的代码</span>
	<span style="color: #666666; font-style: italic;">//map map    </span>
    map <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> LinkedHashMap<span style="color: #009900;">&#40;</span>initialSize, 0.75f, <span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      @Override
      <span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000066; font-weight: bold;">boolean</span> removeEldestEntry<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Map.<span style="color: #006633;">Entry</span></span> eldest<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>size<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> limit<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          <span style="color: #666666; font-style: italic;">// increment evictions regardless of state.</span>
          <span style="color: #666666; font-style: italic;">// this doesn't need to be synchronized because it will</span>
          <span style="color: #666666; font-style: italic;">// only be called in the context of a higher level synchronized block.</span>
          evictions<span style="color: #339933;">++;</span>
          stats.<span style="color: #006633;">evictions</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">true</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">false</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>persistence<span style="color: #339933;">==</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// must be the first time a cache of this type is being created</span>
      persistence <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CumulativeStats<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    stats <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>CumulativeStats<span style="color: #009900;">&#41;</span>persistence<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">return</span> persistence<span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> put<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> key, <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> value<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">synchronized</span> <span style="color: #009900;">&#40;</span>map<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>state <span style="color: #339933;">==</span> State.<span style="color: #006633;">LIVE</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        stats.<span style="color: #006633;">inserts</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
      <span style="color: #666666; font-style: italic;">// increment local inserts regardless of state???</span>
      <span style="color: #666666; font-style: italic;">// it does make it more consistent with the current size...</span>
      inserts<span style="color: #339933;">++;</span>
      <span style="color: #000000; font-weight: bold;">return</span> map.<span style="color: #006633;">put</span><span style="color: #009900;">&#40;</span>key,value<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> get<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">synchronized</span> <span style="color: #009900;">&#40;</span>map<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #003399;">Object</span> val <span style="color: #339933;">=</span> map.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>state <span style="color: #339933;">==</span> State.<span style="color: #006633;">LIVE</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">// only increment lookups and hits if we are live.</span>
        lookups<span style="color: #339933;">++;</span>
        stats.<span style="color: #006633;">lookups</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>val<span style="color: #339933;">!=</span><span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          hits<span style="color: #339933;">++;</span>
          stats.<span style="color: #006633;">hits</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
      <span style="color: #009900;">&#125;</span>
      <span style="color: #000000; font-weight: bold;">return</span> val<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>  可以看到，LRUCache对读写操作直接加的互斥锁，多线程并发读写时会有锁的竞争问题。通常来说，Cache系统的读要远多于写，不能并发读是有些不够友好。不过，相比于Solr中其它耗时的操作来说，LRUCache的串行化读往往不会成为系统的瓶颈。LRUCache的优点是，直接套用LinkedHashMap，实现简单，缺点是，因为LinkedHashMap的get操作需要操作Entry顺序链表，所以必须对整个操作加锁。</p>
<h2>2、FastLRUCache的实现分析</h2>
<p>	Solr1.4引入FastLRUCache作为另一种可选的实现。FastLRUCache放弃了LinkedHashMap，而是使用现在很多Java Cache实现中使用的ConcurrentHashMap。但ConcurrentHashMap只提供了高性能的并发存取支持，并没有提供对淘汰数据的支持，所以FastLRUCache主要需要做的就是这件事情。FastLRUCache的存取操作都在ConcurrentLRUCache中实现，所以我们直接过渡到ConcurrentLRUCache的实现。<br />
	ConcurrentLRUCache的存取操作代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">  <span style="color: #000000; font-weight: bold;">public</span> V get<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> K key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">final</span> CacheEntry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span> e <span style="color: #339933;">=</span> map.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>e <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>islive<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        stats.<span style="color: #006633;">missCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
      <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>islive<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      e.<span style="color: #006633;">lastAccessed</span> <span style="color: #339933;">=</span> stats.<span style="color: #006633;">accessCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> e.<span style="color: #006633;">value</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> V remove<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> K key<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">final</span> CacheEntry<span style="color: #339933;">&lt;</span>K,V<span style="color: #339933;">&gt;</span> cacheEntry <span style="color: #339933;">=</span> map.<span style="color: #006633;">remove</span><span style="color: #009900;">&#40;</span>key<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>cacheEntry <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      stats.<span style="color: #006633;">size</span>.<span style="color: #006633;">decrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000000; font-weight: bold;">return</span> cacheEntry.<span style="color: #006633;">value</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #003399;">Object</span> put<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> K key, <span style="color: #000000; font-weight: bold;">final</span> V val<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>val <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">final</span> CacheEntry e <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> CacheEntry<span style="color: #009900;">&#40;</span>key, val, stats.<span style="color: #006633;">accessCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">final</span> CacheEntry oldCacheEntry <span style="color: #339933;">=</span> map.<span style="color: #006633;">put</span><span style="color: #009900;">&#40;</span>key, e<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000066; font-weight: bold;">int</span> currentSize<span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>oldCacheEntry <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      currentSize <span style="color: #339933;">=</span> stats.<span style="color: #006633;">size</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
      currentSize <span style="color: #339933;">=</span> stats.<span style="color: #006633;">size</span>.<span style="color: #006633;">get</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>islive<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      stats.<span style="color: #006633;">putCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
      stats.<span style="color: #006633;">nonLivePutCounter</span>.<span style="color: #006633;">incrementAndGet</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// Check if we need to clear out old entries from the cache.</span>
    <span style="color: #666666; font-style: italic;">// isCleaning variable is checked instead of markAndSweepLock.isLocked()</span>
    <span style="color: #666666; font-style: italic;">// for performance because every put invokation will check until</span>
    <span style="color: #666666; font-style: italic;">// the size is back to an acceptable level.</span>
    <span style="color: #666666; font-style: italic;">// There is a race between the check and the call to markAndSweep, but</span>
    <span style="color: #666666; font-style: italic;">// it's unimportant because markAndSweep actually aquires the lock or returns if it can't.</span>
    <span style="color: #666666; font-style: italic;">// Thread safety note: isCleaning read is piggybacked (comes after) other volatile reads</span>
    <span style="color: #666666; font-style: italic;">// in this method.</span>
    <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>currentSize <span style="color: #339933;">&gt;</span> upperWaterMark <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span>isCleaning<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
      <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>newThreadForCleanup<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #003399;">Thread</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          @Override
          <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">void</span> run<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            markAndSweep<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
          <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>.<span style="color: #006633;">start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>cleanupThread <span style="color: #339933;">!=</span> <span style="color: #000066; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        cleanupThread.<span style="color: #006633;">wakeThread</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span> <span style="color: #000000; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span>
        markAndSweep<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">return</span> oldCacheEntry <span style="color: #339933;">==</span> <span style="color: #000066; font-weight: bold;">null</span> <span style="color: #339933;">?</span> <span style="color: #000066; font-weight: bold;">null</span> <span style="color: #339933;">:</span> oldCacheEntry.<span style="color: #006633;">value</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>	所有的操作都是直接调用map（ConcurrentHashMap）的。看下put中的代码，当map容量达到上限并且没有其他线程在清理数据（currentSize > upperWaterMark &#038;&#038; !isCleaning），就调用markAndSweep方法清理数据，可以有3种方式做清理工作：1）在该线程同步执行，2）即时启动新线程异步执行，3）提供单独的清理线程，即时唤醒它异步执行。</p>
<p>	markAndSweep方法那是相当的冗长，这里就不罗列出来。下面叙述下它的思路。</p>
<p>	对于ConcurrentLRUCache中的每一个元素CacheEntry，它有个属性lastAccessed，表示最后访问的数值大小。ConcurrentLRUCache中的stats.accessCounter是全局的自增整数，当put或get Entry时，Entry的lastAccessed会被更新成新自增得到的accessCounter。		ConcurrentLRUCache淘汰数据就是淘汰那些lastAccessed较小的Entry。因为ConcurrentLRUCache没有维护以lastAccessed排序的Entry链表（否则就是LRUCache了），所以淘汰数据时就需要遍历整个Map中的元素来淘汰合适的Entry。这是不是要扯上排序呢？其实不用那么大动干戈。</p>
<p>	这里定义几个变量，wantToKeep表示Map中需要保留的Entry个数，wantToRemove表示需要删除的个数（wantToRemove=map.size-wantToKeep),newestEntry是最大的lastAccessed值（初始是stats.accessCounter），这三个变量初始都是已知的，oldestEntry表示最小的lastAccessed，这个是未知的，可以在遍历Entry时通过比较递进到最小。Map中的Entry有3种:(a)是可以立刻判断出可以被淘汰的，也就是lastAccessed<(oldestEntry+wantToRemove)的，（b）是可以立刻判断出可以被保留的，也就是lastAccessed>(newestEntry-1000)的，（c）除上述两者之外的就是不能准确判断是否需要被淘汰的。对于遍历一趟Map中的Entry来说，极好的情况是如果淘汰掉满足（a）的Entry后Map大小降到了wantToKeep，这种情况的典型代表是对Cache只有get和put操作，使得lastAccessed在Map中能保持连续；极坏的情况是，可能满足（a）的Entry不够多甚至没有。但遍历一趟Map至少有一个效果是，会把需要处理的Entry范围缩小到满足（c）的。如此反复迭代，一定使得Map容量调到wantToKeep。而对这个淘汰，也要考虑一个现实情况是，wantToKeep往往是接近于map.size（比如等于0.9*map.size）的，如果remove操作不是很多，那么并不需要很多次遍历就可以完成清理工作。</p>
<p>	ConcurrentLRUCache淘汰数据的基本思想如上所述。它的执行过程可以分为3个阶段。第一个阶段就是遍历Map中的每个Entry，如果满足（a）就remove，满足（b）则跳过，满足（c）则放到新map中。一遍下来后，如果map.size还大于wantToKeep，第二个阶段就再重复上述过程（实现上，Solr用了个变量numPasses，似乎想做个开关控制遍历几次，当前就固定成一次）。完了如果map.size还大于wantToKeep，第三阶段再遍历一遍Map，但这次使用PriorityQueue来提取出还需要再淘汰的N个最old的Entry，这样一次下来就收工了。需要补充一点，上面提到的wantToKeep在代码中是acceptableWaterMark和lowerWaterMark，也就是如果遍历后达到acceptableWaterMark就算完成，但操作是按lowerWaterMark的要求来。</p>
<p>	这个算法的时间复杂度是2n+kln(k)（k值在实际大多数情况下会很小），相比于直接的堆排，通常会更快些。</p>
<h2>3、总结</h2>
<p>	LRUCache和FastLRUCache两种Cache实现是两种很不同的思路。两者的相同点是，都使用了现成的Map来维护数据。不同点是如何来淘汰数据。LRUCache（也就是LinkedHashMap）格外维护了一个结构，在做存取操作时同时更新该结构，优点在于淘汰操作是O(1)的，缺点是需要对存取操作加互斥锁。FastLRUCache正相反，它没有额外维护新的结构，可以由ConcurrentHashMap支持并发读，但put操作中如果需要淘汰数据，淘汰过程是O(n)的，因为整个过程不加锁，这也只会影响该次put的性能，而FastLRUCache也可选成起独立线程异步执行来降低影响。而另一个Cache实现Ehcache，它在淘汰数据就是同步的，不过它限定了每次淘汰数据的大小（通常都少于5个），所以同步情况下性能不会太受影响。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/08/293.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>[Solr源码分析]Solr复制类ReplicationHandler实现简要分析</title>
		<link>http://www.kafka0102.com/2010/07/249.html</link>
		<comments>http://www.kafka0102.com/2010/07/249.html#comments</comments>
		<pubDate>Sat, 24 Jul 2010 16:42:27 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[solr]]></category>
		<category><![CDATA[lucene]]></category>
		<category><![CDATA[源码分析]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=249</guid>
		<description><![CDATA[在上一文《solr ReplicationHandler使用介绍》的基础上，本文接着对solr的ReplicationHandler实现细节做些分析，这个分析原则上没有摘取大段代码，窃以为摘了代码后未见得有很好的阐述效果，但不摘取后窃又发现，阐述的效果依旧不好。归结起来，还是窃的表达不够深入浅出所致。闲言少叙，直接上内容。]]></description>
			<content:encoded><![CDATA[<p>在上一文《<a title="Permanent Link to solr ReplicationHandler使用介绍" rel="bookmark" href="http://www.kafka0102.com/2010/07/244.html">solr  ReplicationHandler使用介绍</a>》的基础上，本文接着对solr的ReplicationHandler实现细节做些分析，这个分析原则上没有摘取大段代码，窃以为摘了代码后未见得有很好的阐述效果，但不摘取后窃又发现，阐述的效果依旧不好。归结起来，还是窃的表达不够深入浅出所致。闲言少叙，直接上内容。</p>
<h2>1、master的工作</h2>
<p>对于ReplicationHandler的复制功能来说，核心的问题确定是在一个时间点要复制哪些文件，这就用上了lucene的IndexDeletionPolicy的特性。lucene在初始化时，会调用IndexDeletionPolicy.onInit(List&lt;? extends IndexCommit&gt; commits)方法；lucene在commit（触发的时机也可以是optimize、close，solr在commit时实际上就是close了indexwriter）时，会调用IndexDeletionPolicy.onCommit(List&lt;? extends IndexCommit&gt; commits)。IndexCommit对象中保存了该次提交关联的文件列表等信息，这使得solr中的复制过程中，slave可以从master得到文件列表后跟本地文件做比较，跳过不变的文件，下载新文件，并删除无用的文件。IndexDeletionPolicy的两个针对commits的函数，会对当前存在的commits列表做些处理，比如lucene默认的KeepOnlyLastCommitDeletionPolicy会只保留最新的IndexCommit，对那些过时的IndexCommit执行delete操作以将无用的文件删掉。solr中，SolrDeletionPolicy默认也是保留最新一个IndexCommit，但可以设置maxCommitAge、maxCommitsToKeep、maxOptimizedCommitsToKeep来保留更多的IndexCommit。但solr真正使用的IndexDeletionPolicy实现是IndexDeletionPolicyWrapper，它是SolrDeletionPolicy的wrap。在slave从master复制文件的过程中，要保证当前正在复制的IndexCommit点不能被删除，这就用到了IndexDeletionPolicyWrapper中的void setReserveDuration(Long indexVersion, long reserveTime)方法，该方法会在master向slave响应indexversion、filelist命令前、以及每向slave传送5M的索引文件内容时调用，而默认的reserveTime时间是10s，如果慢速网络传输5M数据需要10秒以上，就需要调整该值了。</p>
<p>ReplicationHandler复制文件没有采用rsync，而是使用http，它在读一个文件内容传输到slave时，默认是按照1M大小分段输出内容到slave（http chunked？），并且默认是对每段内容做了checksum，保证传输的内容的正确性。上面提到的setReserveDuration点，主要就是它在packetsWritten % 5 == 0次数后触发一次修改。</p>
<p>ReplicationHandler还可以备份索引文件。由于lucene的索引文件只是追加新文件而不会修改已有文件，所以只要针对一个IndexCommit点做备份，其过程还是很简单的。</p>
<h2>2、slave的工作</h2>
<p>slave启动时会创建SnapPuller对象，SnapPuller会启动一个线程定时的（pollInterval间隔）从master复制数据（fetchLatestIndex方法）。对于一次复制过程，slave和master交互处理细节如下：<br />
1、slave首先向master询问最新的索引版本号（indexversion命令），slave检查得到的latestVersion、latestGeneration有效后，和本地的IndexCommit的getVersion()、getGeneration()比较，如果不相等，则需要往下进行，否则等待下一次调度。</p>
<p>2、slave向master请求之前得到的indexversion下的文件列表（filelist命令，包括索引文件和可选的配置文件）。如果文件列表为空，则返回等待下一次调度。否则，就需要检查哪些文件需要被下载过来。这里做的判断有：1）如果本地的commit.getGeneration() &gt;= latestGeneration，说明本地索引文件被破坏（比如对slave不小心提交了修改索引的命令），需要完全将master的文件复制过来。2）逐个检查文件列表中的文件是否在本地存在，不存在就下载下来。</p>
<p>3、对于下载文件内容，对应命令是filecontent。下载的文件显然需要放到临时目录中，这个临时目录和已有的索引目录（默认名字index）在同一数据目录下，只是命名为index.&lt;时间戳&gt;。下载完毕后，copy数据有两种情况：1）如果是完全下载，则不需要将临时目录中的文件copy到已有目录中，而是修改数据目录中的index.properties，标识索引目录为新生成的临时目录，而旧索引目录并不会被删除，可以手工删掉，当然，通常是不应该出现slave的Generation大于master的异常情况。2）通常就是把临时索引目录的文件copy到旧索引目录，copy时要把segments_N放到最后copy，避免copy中途出现异常造成数据被毁。</p>
<p>4、当新索引和可选的配置文件copy完毕之后，slave会对solrcore的UpdateHandler做commit操作，这会close掉indexwriter并强制重启新的indexsearcher提供服务。同时，如果solrcore的UpdateHandler是DirectUpdateHandler2（不应该不是），会强制调用handler.forceOpenWriter()来删除旧的无用的索引文件，并调用replicationHandler.refreshCommitpoint()来更新slave的indexCommitPoint。</p>
<p>5、如果索引复制失败，slave会向数据目录下的replication.properties输出复制失败的信息。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/07/249.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>zoie DocIDMapperImpl类实现分析</title>
		<link>http://www.kafka0102.com/2010/07/238.html</link>
		<comments>http://www.kafka0102.com/2010/07/238.html#comments</comments>
		<pubDate>Fri, 16 Jul 2010 20:55:36 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[java]]></category>
		<category><![CDATA[search]]></category>
		<category><![CDATA[bloom filter]]></category>
		<category><![CDATA[zoie]]></category>
		<category><![CDATA[源码分析]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=238</guid>
		<description><![CDATA[有网友留言询问我对zoie的DocIDMapperImpl实现是否有了解。说实话，之前看zoie也只是大面上的，知道DocIDMapperImpl的用处，但没有仔细分析它的算法。就趁着夜深人静，把这个类好好琢磨了下。但我得承认，看这种伤脑筋的算法让我有些吃不消，下面就列出我对它的大致分析，如果有不恰当的地方，也望指正。

还是说下DocIDMapperImpl的作用吧。在zoie中，uid和lucene的docid有一一对应关系。从docid到uid的映射很简单，就是分配个maxdoc大小的数组，索引位置是docid，值是uid。这样做也是因为docid是从小到大自增的，大小总有限。但uid是long型的，使用数组反映射是不行了，一个直接的选择是使用hashmap。不过zoie为了节约空间，使用了更有效的算法，也就是下面的类。]]></description>
			<content:encoded><![CDATA[<p>有网友留言询问我对zoie的DocIDMapperImpl实现是否有了解。说实话，之前看zoie也只是大面上的，知道DocIDMapperImpl的用处，但没有仔细分析它的算法。就趁着夜深人静，把这个类好好琢磨了下。但我得承认，看这种伤脑筋的算法让我有些吃不消，下面就列出我对它的大致分析，如果有不恰当的地方，也望指正。</p>
<p>还是说下DocIDMapperImpl的作用吧。在zoie中，uid和lucene的docid有一一对应关系。从docid到uid的映射很简单，就是分配个maxdoc大小的数组，索引位置是docid，值是uid。这样做也是因为docid是从小到大自增的，大小总有限。但uid是long型的，使用数组反映射是不行了，一个直接的选择是使用hashmap。不过zoie为了节约空间，使用了更有效的算法，也就是下面的类，这个算法有些像是bloom filter算法的变种应用。</p>

<div class="wp_syntax"><div class="code"><pre class="java" style="font-family:monospace;">&nbsp;
<span style="color: #000000; font-weight: bold;">package</span> <span style="color: #006699;">proj.zoie.api.impl</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">java.util.Arrays</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">java.util.HashMap</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">proj.zoie.api.DocIDMapper</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">import</span> <span style="color: #006699;">proj.zoie.api.ZoieIndexReader</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #008000; font-style: italic; font-weight: bold;">/**
 * @author ymatsuda
 * 
 */</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">class</span> DocIDMapperImpl <span style="color: #000000; font-weight: bold;">implements</span> DocIDMapper <span style="color: #009900;">&#123;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> _docArray<span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">long</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> _uidArray<span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> _start<span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">long</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> _filter<span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> _mask<span style="color: #339933;">;</span>
	<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> MIXER <span style="color: #339933;">=</span> <span style="color: #cc66cc;">2147482951</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// a prime number</span>
&nbsp;
	<span style="color: #008000; font-style: italic; font-weight: bold;">/**
	 * 
	 * @param uidArray uidArray的大小是索引的maxdoc，所以数组的每个索引位置
	 * 表示docid，值表示uid，如果docid被删除，
	 * 其索引位置的值为ZoieIndexReader.DELETED_UID
	 */</span>
	<span style="color: #000000; font-weight: bold;">public</span> DocIDMapperImpl<span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">long</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> uidArray<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #000066; font-weight: bold;">int</span> len <span style="color: #339933;">=</span> uidArray.<span style="color: #006633;">length</span><span style="color: #339933;">;</span>
		<span style="color: #000066; font-weight: bold;">int</span> mask <span style="color: #339933;">=</span> len <span style="color: #339933;">/</span> <span style="color: #cc66cc;">4</span><span style="color: #339933;">;</span>
		mask <span style="color: #339933;">|=</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&gt;&gt;</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		mask <span style="color: #339933;">|=</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&gt;&gt;</span> <span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		mask <span style="color: #339933;">|=</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&gt;&gt;</span> <span style="color: #cc66cc;">4</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		mask <span style="color: #339933;">|=</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&gt;&gt;</span> <span style="color: #cc66cc;">8</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		mask <span style="color: #339933;">|=</span> <span style="color: #009900;">&#40;</span>mask <span style="color: #339933;">&gt;&gt;</span> <span style="color: #cc66cc;">16</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		_mask <span style="color: #339933;">=</span> mask<span style="color: #339933;">;</span>
	<span style="color: #666666; font-style: italic;">//上面的操作，首先是取mask为len的1/4,之后做了联合的右移及或操作，</span>
	<span style="color: #666666; font-style: italic;">//使得mask最高有效位右边的位值都变为1,也就是说，假如mask开始等于0x10110000，</span>
	<span style="color: #666666; font-style: italic;">//操作后变成0x11111111，这才能mask可以和下面的h做与操作定位到_filter数组中</span>
	<span style="color: #666666; font-style: italic;">//的某个索引位置。也可以看到，mask的大小介于len的1/4到1/2。</span>
		_filter <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #000066; font-weight: bold;">long</span><span style="color: #009900;">&#91;</span>mask <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">long</span> uid <span style="color: #339933;">:</span> uidArray<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">!=</span> ZoieIndexReader.<span style="color: #006633;">DELETED_UID</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #000066; font-weight: bold;">int</span> h <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">32</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">^</span> uid<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> MIXER<span style="color: #339933;">;</span>
	<span style="color: #666666; font-style: italic;">//这个hash值算法目的是将uid能散到int的整个数值范围内，并降低h之间的冲突，</span>
	<span style="color: #666666; font-style: italic;">//所以计算后得到的h会比较大。</span>
				<span style="color: #000066; font-weight: bold;">long</span> bits <span style="color: #339933;">=</span> _filter<span style="color: #009900;">&#91;</span>h <span style="color: #339933;">&amp;</span> _mask<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
				bits <span style="color: #339933;">|=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>1L <span style="color: #339933;">&lt;&lt;</span> <span style="color: #009900;">&#40;</span>h <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">26</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				bits <span style="color: #339933;">|=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>1L <span style="color: #339933;">&lt;&lt;</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>h <span style="color: #339933;">&gt;&gt;</span> <span style="color: #cc66cc;">20</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> 0x3F<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				_filter<span style="color: #009900;">&#91;</span>h <span style="color: #339933;">&amp;</span> _mask<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> bits<span style="color: #339933;">;</span>
	<span style="color: #666666; font-style: italic;">//(h &gt;&gt;&gt; 26)得到的是h高位的前5位，再经过1L &lt;&lt; 后，其取值范围就是0-31;</span>
	<span style="color: #666666; font-style: italic;">//(1L &lt;&lt; ((h &gt;&gt; 20) &amp; 0x3F))取值范围是0-63。</span>
	<span style="color: #666666; font-style: italic;">//这两个或操作正好取了bits的位数范围中的两位。</span>
	<span style="color: #666666; font-style: italic;">//bits的两个或操作，相当于bloom filter中两次hash取位。</span>
	<span style="color: #666666; font-style: italic;">//对于bloom filter算法，判定key是否存在是有误判的可能性，这里也不意外。</span>
	<span style="color: #666666; font-style: italic;">//因为bits有64位，而每个uid取两位，mask最坏是len的1/2，在hash散均的情况下，</span>
	<span style="color: #666666; font-style: italic;">//这个_filter每个桶(索引位置）冲突率不会很大。</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		_start <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#91;</span>_mask <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span> <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		len <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">long</span> uid <span style="color: #339933;">:</span> uidArray<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">!=</span> ZoieIndexReader.<span style="color: #006633;">DELETED_UID</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				_start<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">32</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">^</span> uid<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> MIXER<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> _mask<span style="color: #009900;">&#93;</span><span style="color: #339933;">++;</span>
				len<span style="color: #339933;">++;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000066; font-weight: bold;">int</span> val <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> i <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> _start.<span style="color: #006633;">length</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			val <span style="color: #339933;">+=</span> _start<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
			_start<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> val<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		_start<span style="color: #009900;">&#91;</span>_mask<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> len<span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">//_start经过了两个循环处理，第一个循环计算出_start每个桶中保存了多少个uid，</span>
<span style="color: #666666; font-style: italic;">//并计算出有效的uid个数len。第二个循环是为下面的操作做准备，它使得_start中每个桶</span>
<span style="color: #666666; font-style: italic;">//保存的是从第0个桶到当前桶有多少有效的uid。</span>
&nbsp;
		<span style="color: #000066; font-weight: bold;">long</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> partitionedUidArray <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #000066; font-weight: bold;">long</span><span style="color: #009900;">&#91;</span>len<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		<span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> docArray <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#91;</span>len<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">long</span> uid <span style="color: #339933;">:</span> uidArray<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">!=</span> ZoieIndexReader.<span style="color: #006633;">DELETED_UID</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #000066; font-weight: bold;">int</span> i <span style="color: #339933;">=</span> <span style="color: #339933;">--</span><span style="color: #009900;">&#40;</span>_start<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">32</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">^</span> uid<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> MIXER<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> _mask<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				partitionedUidArray<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> uid<span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #000066; font-weight: bold;">int</span> s <span style="color: #339933;">=</span> _start<span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> i <span style="color: #339933;">=</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> _start.<span style="color: #006633;">length</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000066; font-weight: bold;">int</span> e <span style="color: #339933;">=</span> _start<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>s <span style="color: #339933;">&lt;</span> e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #003399;">Arrays</span>.<span style="color: #006633;">sort</span><span style="color: #009900;">&#40;</span>partitionedUidArray, s, e<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
			s <span style="color: #339933;">=</span> e<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #666666; font-style: italic;">//这两个循环来填充partitionedUidArray数组，并调整_start保存的计数，</span>
	<span style="color: #666666; font-style: italic;">//这个计数就是partitionedUidArray数组的索引位置的偏小临近值。</span>
	<span style="color: #666666; font-style: italic;">//注意对_start的--操作和s &lt; e的判断，这是处理一个桶里存在多个uid的情况，</span>
	<span style="color: #666666; font-style: italic;">//以保证partitionedUidArray中uid的顺序，也使得_start相邻两个桶的计数会有差值。</span>
	<span style="color: #666666; font-style: italic;">//所以当可以利用二分查找来搜索_uidArray和_docArray。</span>
		<span style="color: #000000; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span> docid <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> docid <span style="color: #339933;">&lt;</span> uidArray.<span style="color: #006633;">length</span><span style="color: #339933;">;</span> docid<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000066; font-weight: bold;">long</span> uid <span style="color: #339933;">=</span> uidArray<span style="color: #009900;">&#91;</span>docid<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">!=</span> ZoieIndexReader.<span style="color: #006633;">DELETED_UID</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				<span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> p <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">32</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">^</span> uid<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> MIXER<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> _mask<span style="color: #339933;">;</span>
				<span style="color: #000066; font-weight: bold;">int</span> idx <span style="color: #339933;">=</span> findIndex<span style="color: #009900;">&#40;</span>partitionedUidArray, uid, _start<span style="color: #009900;">&#91;</span>p<span style="color: #009900;">&#93;</span>,
						_start<span style="color: #009900;">&#91;</span>p <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
				<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>idx <span style="color: #339933;">&gt;=</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
					docArray<span style="color: #009900;">&#91;</span>idx<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> docid<span style="color: #339933;">;</span>
				<span style="color: #009900;">&#125;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #666666; font-style: italic;">//填充docArray</span>
		_uidArray <span style="color: #339933;">=</span> partitionedUidArray<span style="color: #339933;">;</span>
		_docArray <span style="color: #339933;">=</span> docArray<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #008000; font-style: italic; font-weight: bold;">/**
	 * @see 分析出构造函数后，这个函数就比较好理解了。这里就不细说了。
	 */</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000066; font-weight: bold;">int</span> getDocID<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">long</span> uid<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> h <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>uid <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">32</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">^</span> uid<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> MIXER<span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> p <span style="color: #339933;">=</span> h <span style="color: #339933;">&amp;</span> _mask<span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #666666; font-style: italic;">// check the filter</span>
		<span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">long</span> bits <span style="color: #339933;">=</span> _filter<span style="color: #009900;">&#91;</span>p<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>bits <span style="color: #339933;">&amp;</span> <span style="color: #009900;">&#40;</span>1L <span style="color: #339933;">&lt;&lt;</span> <span style="color: #009900;">&#40;</span>h <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">26</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">0</span>
				<span style="color: #339933;">||</span> <span style="color: #009900;">&#40;</span>bits <span style="color: #339933;">&amp;</span> <span style="color: #009900;">&#40;</span>1L <span style="color: #339933;">&lt;&lt;</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>h <span style="color: #339933;">&gt;&gt;</span> <span style="color: #cc66cc;">20</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> 0x3F<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span>
			<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #339933;">-</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #666666; font-style: italic;">// do binary search in the partition</span>
		<span style="color: #000066; font-weight: bold;">int</span> begin <span style="color: #339933;">=</span> _start<span style="color: #009900;">&#91;</span>p<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		<span style="color: #000066; font-weight: bold;">int</span> end <span style="color: #339933;">=</span> _start<span style="color: #009900;">&#91;</span>p <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">-</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		<span style="color: #666666; font-style: italic;">// we have some uids in this partition, so we assume (begin &lt;= end)</span>
		<span style="color: #000000; font-weight: bold;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000066; font-weight: bold;">int</span> mid <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>begin <span style="color: #339933;">+</span> end<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
			<span style="color: #000066; font-weight: bold;">long</span> midval <span style="color: #339933;">=</span> _uidArray<span style="color: #009900;">&#91;</span>mid<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>midval <span style="color: #339933;">==</span> uid<span style="color: #009900;">&#41;</span>
				<span style="color: #000000; font-weight: bold;">return</span> _docArray<span style="color: #009900;">&#91;</span>mid<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>mid <span style="color: #339933;">==</span> end<span style="color: #009900;">&#41;</span>
				<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #339933;">-</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>midval <span style="color: #339933;">&lt;</span> uid<span style="color: #009900;">&#41;</span>
				begin <span style="color: #339933;">=</span> mid <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">else</span>
				end <span style="color: #339933;">=</span> mid<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #008000; font-style: italic; font-weight: bold;">/**
	 * @see 在arr的一个区间内二分查找uid所在的索引位置。
	 * @param arr
	 * @param uid
	 * @param begin
	 * @param end
	 * @return
	 */</span>
	<span style="color: #000000; font-weight: bold;">private</span> <span style="color: #000000; font-weight: bold;">static</span> <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">int</span> findIndex<span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">long</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> arr, <span style="color: #000000; font-weight: bold;">final</span> <span style="color: #000066; font-weight: bold;">long</span> uid,
			<span style="color: #000066; font-weight: bold;">int</span> begin, <span style="color: #000066; font-weight: bold;">int</span> end<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>begin <span style="color: #339933;">&gt;=</span> end<span style="color: #009900;">&#41;</span>
			<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #339933;">-</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
		end<span style="color: #339933;">--;</span>
&nbsp;
		<span style="color: #000000; font-weight: bold;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000066; font-weight: bold;">int</span> mid <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>begin <span style="color: #339933;">+</span> end<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;&gt;&gt;</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
			<span style="color: #000066; font-weight: bold;">long</span> midval <span style="color: #339933;">=</span> arr<span style="color: #009900;">&#91;</span>mid<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>midval <span style="color: #339933;">==</span> uid<span style="color: #009900;">&#41;</span>
				<span style="color: #000000; font-weight: bold;">return</span> mid<span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>mid <span style="color: #339933;">==</span> end<span style="color: #009900;">&#41;</span>
				<span style="color: #000000; font-weight: bold;">return</span> <span style="color: #339933;">-</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>midval <span style="color: #339933;">&lt;</span> uid<span style="color: #009900;">&#41;</span>
				begin <span style="color: #339933;">=</span> mid <span style="color: #339933;">+</span> <span style="color: #cc66cc;">1</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">else</span>
				end <span style="color: #339933;">=</span> mid<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>   就时间复杂度来说，DocIDMapperImpl和hashmap相当（在hash均匀情况下，那个二分查找次数通常不会很多）。就空间复杂度来说，DocIDMapperImpl中的_uidArray和_docArray相当于hashmap中项的kye和value集合。DocIDMapperImpl中还有的是_start和_filter，而hashmap中每个项还有hash值和项冲突时的next引用以及需要大于负载因子的额外空间。在mask等于1/2 len的最坏情况下，DocIDMapperImpl也是要优于hashmap的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/07/238.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++智能指针之auto_ptr实现分析</title>
		<link>http://www.kafka0102.com/2010/02/62.html</link>
		<comments>http://www.kafka0102.com/2010/02/62.html#comments</comments>
		<pubDate>Wed, 24 Feb 2010 18:36:34 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[c++]]></category>
		<category><![CDATA[auto_ptr]]></category>
		<category><![CDATA[智能指针]]></category>
		<category><![CDATA[源码分析]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=62</guid>
		<description><![CDATA[C++的动态内存的分配与释放是个挺折磨人的事情，尤其当业务逻辑变得复杂时（比如一堆try catch中，各catch里需要做delete 掉相关的堆上分配的内存），极有可能产生内存泄露的情况，尤其是一些很难触发的分支条件。C++中提供了智能指针作为可选的解决方案， C++标准库中自带的智能指针是auto_ptr，它在大多数场景下是满足需求的。针对auto_ptr的缺点，boost和loki两套库都扩展出一些智能指针，并且boost中有两位幸运儿入选了tr1中（std::tr1::shared_ptr,std::tr1::weak_ptr）。本文就gcc中auto_ptr的实现做些分析，以飨自己。笔记采用注释源码的方式。]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: medium;">C++的动态内存的分配与释放是个挺折磨人的事情，尤其异常分支复杂时（比如一堆try catch中，各catch里需要做delete 掉相关的堆上分配的内存），极有可能产生内存泄露的情况。C++中提供了智能指针作为可选的解决方案， C++标准库中自带的智能指针是auto_ptr，它在大多数场景下是满足需求的。针对auto_ptr的缺点，boost和loki两套库都扩展出一些智能指针，并且boost中有两位幸运儿入选了tr1中（std::tr1::shared_ptr,std::tr1::weak_ptr）。本文就gcc中auto_ptr的实现做些分析，以飨自己。笔记采用注释源码的方式。</span></p>
<p>/**<br />
*  这个wrapper类提供auto_ptr以引用语义，在下面的操作中有介绍。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>
<span style="color: #993333;">struct</span> auto_ptr_ref <span style="color: #009900;">&#123;</span>
	_Tp1<span style="color: #339933;">*</span> _M_ptr<span style="color: #339933;">;</span>
	explicit auto_ptr_ref<span style="color: #009900;">&#40;</span>_Tp1<span style="color: #339933;">*</span> __p<span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<div id="_mcePaste">/**</div>
<div id="_mcePaste">* auto_ptr的实现还是很简单的，使用上也简单。在创建auto_ptr对象后，</div>
<div id="_mcePaste">* 通常的使用也就是调用它的*和-&gt;操作符，如下面的sample片段：</div>
<div id="_mcePaste">* AutoPtr&lt;Admin&gt; ptr1(new Admin());</div>
<div id="_mcePaste">* cout&lt;&lt;ptr1-&gt;getAge()&lt;&lt;endl;</div>
<div id="_mcePaste">* cout&lt;&lt;&#8221;obj:&#8221;&lt;&lt;*ptr1&lt;&lt;endl;</div>
<div id="_mcePaste">* 可以看到，auto_ptr中的成员函数都是throw()不抛异常的。</div>
<div id="_mcePaste">*/</div>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">template<span style="color: #339933;">&lt;</span>typename _Tp<span style="color: #339933;">&gt;</span>
class auto_ptr <span style="color: #009900;">&#123;</span>
private<span style="color: #339933;">:</span>
	_Tp<span style="color: #339933;">*</span> _M_ptr<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//</span>
&nbsp;
public<span style="color: #339933;">:</span>
	<span style="color: #666666; font-style: italic;">/// The pointed-to type.</span>
	<span style="color: #993333;">typedef</span> _Tp element_type<span style="color: #339933;">;</span></pre></div></div>

<p>/**<br />
*  构造函数，将auto_ptr绑定到指针__p。<br />
*  __p是一个指向new出来的对象的指针，默认为0（NULL）是说auto_ptr的构造函数可以不传参构造，<br />
*  这时成员_M_ptr=0，如果接着解引用auto_ptr对象，将Segmentation fault。当然，通常应用auto_ptr的构造<br />
*  函数会传参的。auto_ptr提供了get函数来判断_M_ptr是否为空、reset函数重置_M_ptr指针。<br />
*  在继承情况下，_M_ptr可以是__p的基类型。<br />
*  构造函数声明为explicit表示禁止参数的自动类型转换（因为它们总是邪恶的）。<br />
*<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	explicit auto_ptr<span style="color: #009900;">&#40;</span>element_type<span style="color: #339933;">*</span> __p <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 辅助函数<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	element_type<span style="color: #339933;">*</span>	release<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		element_type<span style="color: #339933;">*</span> __tmp <span style="color: #339933;">=</span> _M_ptr<span style="color: #339933;">;</span>
		_M_ptr <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> __tmp<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* auto_ptr的复制构造函数是很邪恶的，它的逻辑是将参数__a中的指针挪给新对象的，<br />
* 原来的__a的内置指针被置空，接下来就不能继续操作__a引用的对象，否则就掉进出错的陷阱。<br />
* 另外，复制构造函数的参数不是个const，因为它需要修改参数内容的。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">			auto_ptr<span style="color: #009900;">&#40;</span>auto_ptr<span style="color: #339933;">&amp;</span> __a<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__a.<span style="color: #202020;">release</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 成员函数模板。好吧，我承认，我对模板也是半知半解（注意，不是一知半解）。这个函数<br />
* 用于将继承体系中的子类型上溯成基类型。举个例子：<br />
* 假如User是基类，Admin是派生类，那么下面的操作是ok的。<br />
* auto_ptr&lt;Admin&gt; ptr2(new Admin());<br />
* auto_ptr&lt;User&gt; ptr3(ptr2);<br />
* 编译器的原理类型识别和转换的大致过程是：当模板参数类型不匹配时（_Tp1转成_Tp），<br />
* 编译首先检查是否存在合适的类型转换操作符（auto_ptr是没有的），如果没有则检查是否<br />
* 存在合适的成员函数模板完成类型转换。在_Tp1是_Tp的派生类的情况下，这种转换就会成功。<br />
* 这也是说，编译器检查的是模板参数类型而不是对象的实际类型，所以，下面的例子编译就会失败：<br />
* auto_ptr&lt;User&gt; ptr2(new Admin());<br />
* auto_ptr&lt;Admin&gt; ptr3(ptr2);<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>	auto_ptr<span style="color: #009900;">&#40;</span>auto_ptr<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;&amp;</span> __a<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__a.<span style="color: #202020;">release</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
*  @brief 重置管理的对象指针，如果_M_ptr不为空，会delete掉。如果重置的指针就是本身<br />
*  的_M_ptr，就是个空操作。<br />
*  @param  __p 对象指针.<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	<span style="color: #993333;">void</span> reset<span style="color: #009900;">&#40;</span>element_type<span style="color: #339933;">*</span> __p <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>__p <span style="color: #339933;">!=</span> _M_ptr<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			delete _M_ptr<span style="color: #339933;">;</span>
			_M_ptr <span style="color: #339933;">=</span> __p<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 赋值操作符，和复制构造函数一样是邪恶的，赋值操作会delete掉右值管理的对象指针。<br />
* 如果auto_ptr对象作为函数参数传递，并且是传值，那么这个调用过程会涉及到赋值操作符<br />
* 的调用，产生并不期待的delete外部对象的结果。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">		auto_ptr<span style="color: #339933;">&amp;</span> operator<span style="color: #339933;">=</span><span style="color: #009900;">&#40;</span>auto_ptr<span style="color: #339933;">&amp;</span> __a<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		reset<span style="color: #009900;">&#40;</span>__a.<span style="color: #202020;">release</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>this<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 成员函数模板赋值操作符<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>	auto_ptr<span style="color: #339933;">&amp;</span> operator<span style="color: #339933;">=</span><span style="color: #009900;">&#40;</span>auto_ptr<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;&amp;</span> __a<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		reset<span style="color: #009900;">&#40;</span>__a.<span style="color: #202020;">release</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>this<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 析构函数，操作很明显，因为是delete，所以auto_ptr是不支持数组指针的，否则会有<br />
* 内存泄露。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	~auto_ptr<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		delete _M_ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 解引用操作符<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">		element_type<span style="color: #339933;">&amp;</span> operator<span style="color: #339933;">*</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #993333;">const</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		_GLIBCXX_DEBUG_ASSERT<span style="color: #009900;">&#40;</span>_M_ptr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>_M_ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* -&gt;操作符，是auto_ptr被用得最多的调用吧。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">				element_type<span style="color: #339933;">*</span> operator<span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #993333;">const</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		_GLIBCXX_DEBUG_ASSERT<span style="color: #009900;">&#40;</span>_M_ptr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> _M_ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 返回auto_ptr管理的指针，这通常用于判断指针是否为空的情况，所以，如果要判断<br />
* auto_ptr管理的指针是否为空，不要使用if(auto_ptr_obj){}而是使用get函数(实际上，<br />
* 因为auto_ptr并没用定义指向element_type的dumb指针的隐式类型转换操作符，所以根本<br />
* 编译不过if(auto_ptr_obj))。<br />
* 但是，auto_ptr并没有禁止你进一步操作你得到的指针，甚至delete它使<br />
* auto_ptr对象内置的指针悬空。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	element_type<span style="color: #339933;">*</span> get<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #993333;">const</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> _M_ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 下面的三个函数连带auto_ptr_ref是auto_ptr中的神奇之笔，因为我拍了好多次脑袋才想明白<br />
* 是怎么的应用原理。考虑两种使用情况：<br />
* 1）void foo(auto_ptr&lt; User&gt; ptr);<br />
* 2）auto_ptr&lt; User&gt;  func_returning_auto_ptr(&#8230;..);<br />
*    auto_ptr ptr&lt; User&gt; = func_returning_auto_ptr(&#8230;..);<br />
*  对于第一种情况，当调用方式是：foo(auto_ptr&lt; User&gt;(new User));时，因为是传值调用，<br />
*  而实参是个临时对象，所以需要做赋值构造对象，但auto_ptr的赋值构造函数参数并不是const的<br />
*  所以不匹配其复制构造函数。auto_ptr采用了曲线策略，编译器接着检查类型转换操作符，<br />
*  发现operator auto_ptr_ref&lt;_Tp1&gt;()是匹配的，所以将临时对象转成auto_ptr_ref，再调用<br />
*  auto_ptr(auto_ptr_ref __ref)把auto_ptr_ref转成auto_ptr。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	auto_ptr<span style="color: #009900;">&#40;</span>auto_ptr_ref<span style="color: #339933;">&lt;</span>element_type<span style="color: #339933;">&gt;</span> __ref<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__ref._M_ptr<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>	operator auto_ptr_ref<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> auto_ptr_ref<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#40;</span>this<span style="color: #339933;">-&gt;</span>release<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 和auto_ptr(auto_ptr_ref __ref)相似<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">		auto_ptr<span style="color: #339933;">&amp;</span> operator<span style="color: #339933;">=</span><span style="color: #009900;">&#40;</span>auto_ptr_ref<span style="color: #339933;">&lt;</span>element_type<span style="color: #339933;">&gt;</span> __ref<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>__ref._M_ptr <span style="color: #339933;">!=</span> this<span style="color: #339933;">-&gt;</span>get<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			delete _M_ptr<span style="color: #339933;">;</span>
			_M_ptr <span style="color: #339933;">=</span> __ref._M_ptr<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>this<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 怎么说这个函数呢？我还不晓得这个转换操作符在什么时候调用呢？<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">			template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>	operator auto_ptr<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> auto_ptr<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#40;</span>this<span style="color: #339933;">-&gt;</span>release<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>//auto_ptr是不支持void类型的模板特化。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">template<span style="color: #339933;">&lt;&gt;</span> class auto_ptr<span style="color: #339933;">&lt;</span>void<span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#123;</span>
public<span style="color: #339933;">:</span>
	<span style="color: #993333;">typedef</span> <span style="color: #993333;">void</span> element_type<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/02/62.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>

