<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts | Academic</title><link>https://loloxwg.top/posts/</link><atom:link href="https://loloxwg.top/posts/index.xml" rel="self" type="application/rss+xml"/><description>Posts</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><image><url>https://loloxwg.top/media/icon_hu0b7a4cb9992c9ac0e91bd28ffd38dd00_9727_512x512_fill_lanczos_center_3.png</url><title>Posts</title><link>https://loloxwg.top/posts/</link></image><item><title>ExtendibleHashing</title><link>https://loloxwg.top/posts/cmu-15445/extendible-hashing/</link><pubDate>Wed, 12 Apr 2023 16:16:26 +0800</pubDate><guid>https://loloxwg.top/posts/cmu-15445/extendible-hashing/</guid><description>&lt;h1 id="extendible-hashing">Extendible Hashing&lt;/h1>
&lt;p>&lt;a href="https://www.geeksforgeeks.org/extendible-hashing-dynamic-approach-to-dbms/" target="_blank" rel="noopener">Extendible Hashing (Dynamic approach to DBMS) - GeeksforGeeks&lt;/a>&lt;/p>
&lt;p>Extendible hashing is a dynamic hashing technique in which the hash function is modified as the size of the hash table increases. It is commonly used in database systems to implement indexing. In extendible hashing, each bucket can store multiple records and each bucket is identified by a local directory. The global directory keeps track of the local directories and is expanded as the number of buckets increases.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="assets/Untitled.png" alt="Untitled" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="assets/Untitled.png" alt="Untitled" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="assets/Untitled.png" alt="Untitled" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;ul>
&lt;li>目录：存储指向桶的指针。&lt;/li>
&lt;li>桶：哈希键值对，如果局部深度小于全局深度时，一个桶可能包含不止一个指针指向它。&lt;/li>
&lt;li>全局深度：和目录相关联，也就是哈希后取的比特数。&lt;/li>
&lt;li>局部深度：和桶关联，表示桶中的数据是哈希后取低&lt;code>x&lt;/code>位得到的。&lt;/li>
&lt;/ul>
&lt;p>插入数据，流程如下：&lt;/p>
&lt;ol>
&lt;li>将&lt;code>key&lt;/code>哈希后，如果全局深度为&lt;code>n&lt;/code>，那么取低&lt;code>n&lt;/code>位比特。比如&lt;code>n = 4&lt;/code>时，哈希出来的结果为&lt;code>xxx0011&lt;/code>，表示这个&lt;code>key&lt;/code>应该存储到&lt;code>dir[0011]&lt;/code>指向的桶中。&lt;/li>
&lt;li>如果桶已经满了，那么比较全局深度和局部深度的大小。&lt;/li>
&lt;li>如果相等，&lt;code>dir&lt;/code>目录项扩容一倍，全局深度相应增加&lt;code>1&lt;/code>。新增的那些&lt;code>dir[i]&lt;/code>指向&lt;code>dir[i - Size]&lt;/code>。比如开始时全局深度为&lt;code>2&lt;/code>，那么&lt;code>dir&lt;/code>大小为&lt;code>4&lt;/code>，扩容后大小为&lt;code>8&lt;/code>，其中&lt;code>dir[5]&lt;/code>应该指向&lt;code>dir[1]&lt;/code>。因为&lt;code>5&lt;/code>中对应的&lt;code>key&lt;/code>哈希值低&lt;code>3&lt;/code>位为&lt;code>101&lt;/code>，在还没扩容之前，低&lt;code>2&lt;/code>位为&lt;code>01&lt;/code>。也就意味着，现在本该存储在&lt;code>dir[5]&lt;/code>中的&lt;code>key&lt;/code>之前存在&lt;code>dir[1]&lt;/code>中，我们直接将&lt;code>dir[5] = dir[1]&lt;/code>，共用。当全局深度比局部深度大&lt;code>n&lt;/code>时，有&lt;code>2^n&lt;/code>个&lt;code>dir&lt;/code>目录项共用这个桶。&lt;/li>
&lt;li>&lt;code>dir&lt;/code>扩容后，再次尝试插入，这个时候桶还是满的，不过局部深度比全局深度小。&lt;/li>
&lt;li>如果局部深度比全局深度小，我们将局部深度增加&lt;code>1&lt;/code>，比如原来是&lt;code>2&lt;/code>，现在变成&lt;code>3&lt;/code>，那么里面的&lt;code>key&lt;/code>哈希值低两位都是相同的，第&lt;code>3&lt;/code>位的&lt;code>0&lt;/code>和&lt;code>1&lt;/code>可以把这里面的&lt;code>key&lt;/code>分成两部分。&lt;/li>
&lt;li>分完之后，让&lt;code>dir&lt;/code>中低&lt;code>3&lt;/code>位是&lt;code>0xx&lt;/code>和&lt;code>1xx&lt;/code>的目录项分别指向拆分后的桶。&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">ExtendibleHashTable&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">K&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">V&lt;/span>&lt;span class="o">&amp;gt;::&lt;/span>&lt;span class="n">Insert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">K&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">V&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// UNREACHABLE(&amp;#34;not implemented&amp;#34;);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">scoped_lock&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">mutex&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">lock&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">latch_&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">directory_index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">IndexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">target_bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dir_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">directory_index&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">target_bucket&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Insert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. If the local depth of the bucket is equal to the global depth,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// increment the global depth and double the size of the directory.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">LOG_DEBUG&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;stage1: dir_.size() = %ld&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dir_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">GetLocalDepth&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">directory_index&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">GetGlobalDepth&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">global_depth_&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LOG_DEBUG&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;start loop.. dir_.size() = %ld&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dir_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">length&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dir_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dir_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">length&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// &amp;lt;&amp;lt;1 is same as *2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">length&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dir_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">length&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dir_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// length =4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// dir[4]=dir[0] 100 000
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// dir[5]=dir[1] 101 001
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// dir[6]=dir[2] 110 010
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// dir[7]=dir[3] 111 011
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">LOG_DEBUG&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;stage2: Increment the local depth of the bucket.&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. If local depth lower than global depth , Increment the local depth of the bucket.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">target_bucket&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">IncrementDepth&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. Split the bucket and redistribute
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// directory pointers &amp;amp; the kv pairs in the bucket.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">LOG_DEBUG&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;stage3: Split the bucket and redistribute&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">mask&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">target_bucket&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetDepth&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">new_bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_shared&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Bucket&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_size_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">target_bucket&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetDepth&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">old_bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">make_shared&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Bucket&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">bucket_size_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">target_bucket&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetDepth&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">num_buckets_&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3.1. Redistribute the directory pointers.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">dir_&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">dir_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">target_bucket&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">mask&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dir_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">new_bucket&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dir_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">old_bucket&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3.2. Move the kv pairs from the target bucket to the new two bucket.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">auto&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nl">item&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">target_bucket&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">GetItems&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">cur_directory_index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">IndexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dir_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">cur_directory_index&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">Insert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">first&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">item&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">second&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">directory_index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">IndexOf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">target_bucket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dir_&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">directory_index&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>LRU-K</title><link>https://loloxwg.top/posts/cmu-15445/lru-k/</link><pubDate>Wed, 12 Apr 2023 16:16:26 +0800</pubDate><guid>https://loloxwg.top/posts/cmu-15445/lru-k/</guid><description>&lt;p>&lt;strong>一、LRU-K算法&lt;/strong>&lt;/p>
&lt;p>1、算法思想&lt;/p>
&lt;p>LRU-K中的K代表最近使用的次数，因此LRU可以认为是LRU-1。LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题，其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。&lt;/p>
&lt;p>2、工作原理&lt;/p>
&lt;p>相比LRU，LRU-K需要多维护一个队列，用于记录所有缓存数据被访问的历史。只有当数据的访问次数达到K次的时候，才将数据放入缓存。当需要淘汰数据时，LRU-K会淘汰第K次访问时间距当前时间最大的数据。详细实现如下&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="./assets/1240.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>(1).数据第一次被访问，加入到访问历史列表；&lt;/p>
&lt;p>(2).如果数据在访问历史列表里后没有达到K次访问，则按照一定规则（FIFO，LRU）淘汰；&lt;/p>
&lt;p>(3).当访问历史队列中的数据访问次数达到K次后，将数据索引从历史队列删除，将数据移到缓存队列中，并缓存此数据，缓存队列重新按照时间排序；&lt;/p>
&lt;p>(4).缓存数据队列中被再次访问后，重新排序；&lt;/p>
&lt;p>(5).需要淘汰数据时，淘汰缓存队列中排在末尾的数据，即：淘汰“倒数第K次访问离现在最久”的数据。&lt;/p>
&lt;p>LRU-K具有LRU的优点，同时能够避免LRU的缺点，实际应用中LRU-2是综合各种因素后最优的选择，LRU-3或者更大的K值命中率会高，但适应性差，需要大量的数据访问才能将历史访问记录清除掉。&lt;/p>
&lt;p>**二、Twoqueues（2Q）**本次LAB使用的思想&lt;/p>
&lt;p>1、算法思想&lt;/p>
&lt;p>该算法类似于LRU-2，不同点在于2Q将LRU-2算法中的访问历史队列（注意这不是缓存数据的）改为一个FIFO缓存队列，即：2Q算法有两个缓存队列，一个是FIFO队列，一个是LRU队列。&lt;/p>
&lt;p>2、工作原理&lt;/p>
&lt;p>当数据第一次访问时，2Q算法将数据缓存在FIFO队列里面，当数据第二次被访问时，则将数据从FIFO队列移到LRU队列里面，两个队列各自按照自己的方法淘汰数据。详细实现如下：&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="./assets/CleanShot%202023-04-11%20at%2010.46.44%402x.png" alt="CleanShot 2023-04-11 at 10.46.44@2x" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>(1).新访问的数据插入到FIFO队列；&lt;/p>
&lt;p>(2).如果数据在FIFO队列中一直没有被再次访问，则最终按照FIFO规则淘汰；&lt;/p>
&lt;p>(3).如果数据在FIFO队列中被再次访问，则将数据移到LRU队列头部；&lt;/p>
&lt;p>(4).如果数据在LRU队列再次被访问，则将数据移到LRU队列头部；&lt;/p>
&lt;p>(5).LRU队列淘汰末尾的数据。&lt;/p>
&lt;p>&lt;strong>参考资料：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.cs.cmu.edu/~christos/courses/721-resources/p297-o_neil.pdf" target="_blank" rel="noopener">The LRU-K Page Replacement Algorithm For Database Disk Buffering&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.vldb.org/conf/1994/P439.PDF" target="_blank" rel="noopener">2Q: A Low Overhead High Performance Buffer Management Replacement Algorithm&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>C++中的const</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84const-f78cd58e7f3c44adac55620e8d3efa13/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84const-f78cd58e7f3c44adac55620e8d3efa13/</guid><description>&lt;p>在C++中，&lt;code>const&lt;/code>是一个关键字，用于指定一个变量或对象的值将不会被修改。 &lt;code>const&lt;/code>可以用于不同的变量类型，例如整数，浮点数，字符，指针等。在这个文档中，我们将探讨&lt;code>const&lt;/code>的一些用途。&lt;/p>
&lt;h2 id="常量">常量&lt;/h2>
&lt;p>使用&lt;code>const&lt;/code>定义常量是C++中的一种最常见的用途。 在定义变量时，将变量定义为&lt;code>const&lt;/code>将确保在程序的其余部分中不会更改变量的值。 这对于程序的可维护性和易读性非常重要。&lt;/p>
&lt;p>以下是一个使用&lt;code>const&lt;/code>定义常量的示例：&lt;/p>
&lt;pre tabindex="0">&lt;code>const int MAX_VALUE = 100;
&lt;/code>&lt;/pre>&lt;p>在上面的示例中，我们定义了一个名为&lt;code>MAX_VALUE&lt;/code>的常量，并将其值设置为100。 在程序的其余部分中，当我们使用&lt;code>MAX_VALUE&lt;/code>时，我们知道它的值将始终为100。&lt;/p>
&lt;h2 id="函数参数">函数参数&lt;/h2>
&lt;p>在函数定义中，&lt;code>const&lt;/code>可以用于指定参数是只读的。 这可以确保在函数内部不会更改参数的值。 这对于编写安全和可靠的代码非常重要。&lt;/p>
&lt;p>以下是一个使用&lt;code>const&lt;/code>在函数参数中指定只读的示例：&lt;/p>
&lt;pre tabindex="0">&lt;code>int sum(const int x, const int y)
{
return x + y;
}
&lt;/code>&lt;/pre>&lt;p>在上面的示例中，我们定义了一个名为&lt;code>sum&lt;/code>的函数，它接受两个&lt;code>const&lt;/code>整数参数，这意味着函数内部不会更改这些参数的值。&lt;/p>
&lt;h2 id="成员函数">成员函数&lt;/h2>
&lt;p>在C++中，&lt;code>const&lt;/code>也可以用于类的成员函数。 &lt;code>const&lt;/code>成员函数保证不会修改类的数据成员，因此它们可以安全地由&lt;code>const&lt;/code>对象调用。&lt;/p>
&lt;p>以下是一个使用&lt;code>const&lt;/code>在类的成员函数中指定只读的示例：&lt;/p>
&lt;pre tabindex="0">&lt;code>class MyClass
{
public:
int getValue() const
{
return value;
}
private:
int value = 10;
};
int main()
{
const MyClass obj;
std::cout &amp;lt;&amp;lt; obj.getValue() &amp;lt;&amp;lt; std::endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>在上面的示例中，我们定义了一个名为&lt;code>MyClass&lt;/code>的类，它具有一个&lt;code>const&lt;/code>成员函数&lt;code>getValue()&lt;/code>，该函数返回类的值成员。 在&lt;code>main()&lt;/code>函数中，我们创建了一个&lt;code>const&lt;/code>对象&lt;code>obj&lt;/code>，并调用了&lt;code>getValue()&lt;/code>函数，因为该函数是&lt;code>const&lt;/code>成员函数，因此可以安全地由&lt;code>const&lt;/code>对象调用。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>在C++中，&lt;code>const&lt;/code>是一种非常有用的关键字，用于定义常量，指定函数参数为只读以及定义类的&lt;code>const&lt;/code>成员函数。 这对于编写安全和可维护的代码非常重要。&lt;/p>
&lt;h1 id="const-和static的区别">const 和static的区别&lt;/h1>
&lt;p>&lt;code>const&lt;/code>和&lt;code>static&lt;/code>是C++中两个非常常见的关键字，它们的用途有些相似，但也有很多不同之处。 &lt;code>const&lt;/code>用于指定变量值不会被修改，而&lt;code>static&lt;/code>用于指定变量在整个程序生命周期内只有一个实例。&lt;/p>
&lt;p>其中，&lt;code>const&lt;/code>和&lt;code>static&lt;/code>还有一个重要的区别：&lt;code>const&lt;/code>变量可以在编译时初始化，而&lt;code>static&lt;/code>变量必须在程序运行时初始化。这意味着&lt;code>const&lt;/code>变量的值不能在程序运行时更改，而&lt;code>static&lt;/code>变量的值可以在程序运行时更改。&lt;/p>
&lt;p>另一个重要的区别是，&lt;code>const&lt;/code>变量在程序的整个生命周期内都是只读的，而&lt;code>static&lt;/code>变量可以在程序的任何地方进行读写操作。因此，&lt;code>const&lt;/code>变量是更加安全和可靠的，而&lt;code>static&lt;/code>变量则更加灵活和易于使用。&lt;/p>
&lt;h1 id="const--int-和-int-const--和-int--const">const int *和 int const * 和 int * const&lt;/h1>
&lt;p>&lt;code>const int *&lt;/code>和&lt;code>int const *&lt;/code>是等效的，指针指向常量，它们都表示指向常量整数的指针，这意味着指向的内容不能更改。 （const 在 * 前）&lt;/p>
&lt;p>&lt;code>int * const&lt;/code>表示一个指针是常量，该指针不能修改，但是可以更改指针指向的内容。(const 在 * 后)&lt;/p>
&lt;blockquote>
&lt;p>int * const a 代表这个指针 const了 不能改
int const *b 代表这个指针指的内容不能动了&lt;/p>
&lt;/blockquote>
&lt;p>注意，&lt;code>const&lt;/code>关键字可以位于&lt;code>*&lt;/code>的左侧或右侧，含义不同&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Hello, World!&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">//a=new int(2); error
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// *b=2; error
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// *c=3; error
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>const int * const a&lt;/code>表示一个指针是常量，指向的内容也是常量，指针和指向的内容都不能修改。&lt;/p>
&lt;p>const int * const GetX() const&lt;/p>
&lt;p>&lt;code>const int *&lt;/code>和&lt;code>int const *&lt;/code>是等效的，都表示指向常量整数的指针。
&lt;code>int * const&lt;/code>表示指针是常量，指针指向的内容可以改变。
在函数返回值类型前面的&lt;code>const&lt;/code>表示函数返回值是常量，不能被修改。&lt;/p>
&lt;p>在C++中，&lt;code>const&lt;/code>也可以用于类的成员函数。 &lt;code>const&lt;/code>成员函数保证不会修改类的数据成员，因此它们可以安全地由&lt;code>const&lt;/code>对象调用。&lt;/p>
&lt;p>以下是一个使用&lt;code>const&lt;/code>在类的成员函数中指定只读的示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MyClass&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">getValue&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">const&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="n">MyClass&lt;/span> &lt;span class="n">obj&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">obj&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">getValue&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在上面的示例中，我们定义了一个名为&lt;code>MyClass&lt;/code>的类，它具有一个&lt;code>const&lt;/code>成员函数&lt;code>getValue()&lt;/code>，该函数返回类的值成员。 在&lt;code>main()&lt;/code>函数中，我们创建了一个&lt;code>const&lt;/code>对象&lt;code>obj&lt;/code>，并调用了&lt;code>getValue()&lt;/code>函数，因为该函数是&lt;code>const&lt;/code>成员函数，因此可以安全地由&lt;code>const&lt;/code>对象调用。&lt;/p>
&lt;ul>
&lt;li>&lt;code>const int *&lt;/code>和&lt;code>int const *&lt;/code>是等效的，都表示指向常量整数的指针。&lt;/li>
&lt;li>&lt;code>int * const&lt;/code>表示指针是常量，指针指向的内容可以改变。&lt;/li>
&lt;li>&lt;code>const int * const&lt;/code>表示指针是常量，指向的内容也是常量，指针和指向的内容都不能修改。&lt;/li>
&lt;/ul></description></item><item><title>C++中的mutable</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84mutable-0edc2ed4eb114446ae9c96b81a74de74/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84mutable-0edc2ed4eb114446ae9c96b81a74de74/</guid><description>&lt;p>在C++中，关键字&lt;code>mutable&lt;/code>用于允许一个&lt;code>const&lt;/code>对象的成员变量被修改。在使用&lt;code>mutable&lt;/code>关键字修饰的成员变量的时候，可以在&lt;code>const&lt;/code>成员函数中修改该变量。这是因为，&lt;code>const&lt;/code>成员函数默认情况下不允许修改类的数据成员。&lt;/p>
&lt;p>&lt;code>mutable&lt;/code>关键字通常用于缓存变量（cache variable）。一个&lt;code>const&lt;/code>成员函数可以使用&lt;code>mutable&lt;/code>修饰的变量作为缓存，来避免重复计算。同时，由于该变量是&lt;code>mutable&lt;/code>的，因此即使在&lt;code>const&lt;/code>成员函数中，也可以被修改。&lt;/p>
&lt;p>下面是一个示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MyClass&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">myFunc&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">cacheValid&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// perform some heavy calculations
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// and store the result in cache
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">cache&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">42&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cacheValid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// use cache
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">mutable&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">cache&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">mutable&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">cacheValid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在上面的示例中，cache和cacheValid被标记为mutable，因此可以在const成员函数myFunc()中被修改。如果没有mutable关键字，cache和cacheValid就不能在const成员函数中被修改。&lt;/p>
&lt;p>需要注意的是，mutable变量的修改只在当前对象中生效，不会影响到其他对象。因此，mutable变量不会破坏C++的const语义。&lt;/p></description></item><item><title>C++中的static</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84static-eb2478cbe8134fcf9c35f28028be93c5/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84static-eb2478cbe8134fcf9c35f28028be93c5/</guid><description>&lt;p>在C++中，static是一种关键字，它可以应用于不同的元素，例如变量、函数和类成员等，它的含义有所不同。&lt;/p>
&lt;h2 id="静态变量">静态变量&lt;/h2>
&lt;p>当static用于变量时，它可以将变量声明为静态变量。静态变量有以下特点：&lt;/p>
&lt;ul>
&lt;li>静态变量存储在静态存储区，而不是栈上或堆上。&lt;/li>
&lt;li>静态变量在程序运行时只被初始化一次，并一直存在于内存中，直到程序结束。&lt;/li>
&lt;li>静态变量默认被初始化为0。&lt;/li>
&lt;/ul>
&lt;p>下面是一个使用静态变量的示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">increment&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">increment&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">increment&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">increment&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">3&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="静态函数">静态函数&lt;/h2>
&lt;p>当static用于函数时，它可以将函数声明为静态函数。静态函数有以下特点：&lt;/p>
&lt;ul>
&lt;li>静态函数只能在声明它的文件中使用，不能被其他文件调用。&lt;/li>
&lt;li>静态函数不能访问非静态变量。&lt;/li>
&lt;/ul>
&lt;p>下面是一个使用静态函数的示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">static_function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;This is a static function.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">non_static_function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;This is a non-static function.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">static_function&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">non_static_function&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="n">This&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="k">static&lt;/span> &lt;span class="n">function&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">This&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="n">non&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="n">function&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="静态类成员">静态类成员&lt;/h2>
&lt;p>当static用于类的成员变量和成员函数时，它可以将它们声明为静态成员。静态成员有以下特点：&lt;/p>
&lt;ul>
&lt;li>静态成员是类的所有对象共享的，它们只有一份拷贝。&lt;/li>
&lt;li>静态成员可以被类的所有对象访问和修改，也可以通过类名和作用域运算符直接访问和修改。&lt;/li>
&lt;li>静态成员可以初始化为常量或表达式。&lt;/li>
&lt;/ul>
&lt;p>下面是一个使用静态类成员的示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">class&lt;/span> &lt;span class="n">MyClass&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">public&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">static_variable&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">static&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">static_function&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">non_static_variable&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">non_static_function&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">MyClass&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">static_variable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">MyClass&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">static_function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;This is a static function.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="n">MyClass&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">non_static_function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;This is a non-static function.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MyClass&lt;/span> &lt;span class="n">obj1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">obj2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">obj1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">static_variable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">obj2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">static_variable&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">obj1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">static_variable&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">obj2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">static_variable&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">obj1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">static_function&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">obj1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">non_static_function&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">This&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="k">static&lt;/span> &lt;span class="n">function&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">This&lt;/span> &lt;span class="n">is&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="n">non&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="k">static&lt;/span> &lt;span class="n">function&lt;/span>&lt;span class="p">.&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>单例模式是一种常见的设计模式，它可以确保一个类只有一个实例，并提供访问该实例的全局点。&lt;/p>
&lt;p>下面是使用静态方法实现单例模式的示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">class&lt;/span> &lt;span class="n">Singleton&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">public&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">static&lt;/span> &lt;span class="n">Singleton&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">getInstance&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">static&lt;/span> &lt;span class="n">Singleton&lt;/span> &lt;span class="n">instance&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">instance&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">showMessage&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Hello, World!&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nl">private&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Singleton&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Singleton&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Singleton&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Singleton&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">operator&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Singleton&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Singleton&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">instance1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Singleton&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">getInstance&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Singleton&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">instance2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Singleton&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">getInstance&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">instance1&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">showMessage&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">instance2&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">showMessage&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>输出：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Hello&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">World&lt;/span>&lt;span class="o">!&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Hello&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">World&lt;/span>&lt;span class="o">!&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在上面的示例中，getInstance方法是静态方法，它返回一个Singleton类的引用。在该方法中，我们使用了静态变量instance来保存Singleton类的唯一实例。由于静态变量存储在静态存储区，因此它们只会被初始化一次，并一直存在于内存中，直到程序结束。通过返回instance的引用，我们可以在程序的任何地方访问Singleton类的唯一实例。&lt;/p>
&lt;p>请注意，在Singleton类的构造函数、拷贝构造函数和赋值运算符中，我们将它们声明为私有的。这是因为我们不希望用户创建Singleton类的其他实例，从而确保Singleton类只有一个实例。&lt;/p></description></item><item><title>C++中的可见性</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E5%8F%AF%E8%A7%81%E6%80%A7-0ae232d21aa34b14aacc7c41515ef775/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E5%8F%AF%E8%A7%81%E6%80%A7-0ae232d21aa34b14aacc7c41515ef775/</guid><description>&lt;h1 id="c中的可见性">C++中的可见性&lt;/h1>
&lt;p>在C++中，可见性是一个非常重要的概念。它指的是变量、函数、类等在不同的作用域中的可见性范围。C++中的作用域分为全局作用域和局部作用域。&lt;/p>
&lt;p>在全局作用域中定义的变量、函数、类等可以在文件的任何地方被访问，也可以被其他文件所共享。而在局部作用域中定义的变量、函数、类等只能在其定义的块中被访问，无法被其他块所访问。&lt;/p>
&lt;p>在C++中，还有一些特殊的作用域，例如命名空间。命名空间可以将一组相关的变量、函数、类等组织在一起，以避免命名冲突并提高代码的可读性。&lt;/p>
&lt;h1 id="访问修饰符">访问修饰符&lt;/h1>
&lt;p>另外，在C++中，还可以使用访问修饰符来控制类成员的可见性。C++中有三种访问修饰符，分别是public、private和protected。public修饰符表示该成员对外可见，可以被类的外部和派生类访问；private修饰符表示该成员仅对类内可见，不可以被类的外部和派生类访问；protected修饰符表示该成员仅对类内和派生类可见，不可以被类的外部访问。&lt;/p>
&lt;blockquote>
&lt;p>class 里不写 默认 private
struct 里不写 默认 public&lt;/p>
&lt;/blockquote>
&lt;h2 id="友元类">友元类&lt;/h2>
&lt;p>除了访问修饰符，C++还提供了友元类的概念。友元类可以访问类的私有和保护成员，但并不是类的成员。使用友元类可以在一定程度上突破类成员访问权限的限制，但也需要慎重使用，以避免破坏类的封装性和数据安全。&lt;/p>
&lt;p>总之，C++中的可见性是一个非常重要的概念。了解和掌握好C++中的可见性，可以帮助我们更好地组织和管理代码，提高代码的可读性和可维护性。&lt;/p></description></item><item><title>C++中的字符串</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2-c1aea693e5cc47d8a53110577af87660/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2-c1aea693e5cc47d8a53110577af87660/</guid><description>&lt;p>在C++中，字符串是一组字符的序列。在处理字符串时，我们会用到char类型。&lt;/p>
&lt;h2 id="char类型">Char类型&lt;/h2>
&lt;p>Char是C++中的一种数据类型，表示单个字符。我们可以使用单引号将字符括起来，例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>char letter = &amp;#39;a&amp;#39;;
&lt;/code>&lt;/pre>&lt;p>Char类型占用1个字节的内存空间，可以存储所有ASCII字符集中的字符，包括字母、数字、符号等。在C++中，char类型的取值范围是-128到127。&lt;/p>
&lt;p>在处理字符串时，我们通常会将一组字符存储在char类型的数组中。&lt;/p>
&lt;h2 id="字符数组">字符数组&lt;/h2>
&lt;p>C++中的字符串可以通过字符数组来实现。例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>char str[10] = &amp;#34;Hello World&amp;#34;;
&lt;/code>&lt;/pre>&lt;p>这里定义了一个名为str的字符数组，并初始化为&amp;quot;Hello World&amp;quot;。我们可以使用类似于访问数组元素的方式来访问字符串中的每个字符。例如，要访问字符串中的第一个字符，可以使用以下代码：&lt;/p>
&lt;pre tabindex="0">&lt;code>char first = str[0];
&lt;/code>&lt;/pre>&lt;h2 id="字符串库">字符串库&lt;/h2>
&lt;p>C++中提供了一些字符串处理的函数，例如strlen、strcpy、strcat等。这些函数可以帮助我们更方便地处理字符串。&lt;/p>
&lt;ul>
&lt;li>strlen：用于获取字符串的长度。&lt;/li>
&lt;li>strcpy：用于将一个字符串复制到另一个字符串中。&lt;/li>
&lt;li>strcat：用于将一个字符串拼接到另一个字符串的末尾。&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code>char str1[6] = &amp;#34;Hello&amp;#34;;
char str2[6] = &amp;#34;World&amp;#34;;
char str3[50];
int len;
// 复制str1到str3
strcpy(str3, str1);
// 将str2拼接到str3末尾
strcat(str3, str2);
// 获取str3的长度
len = strlen(str3);
&lt;/code>&lt;/pre>&lt;p>以上代码将会得到str3的值为&amp;quot;Hello World&amp;quot;，len的值为11。&lt;/p>
&lt;h1 id="char--的隐式转换">char * 的隐式转换&lt;/h1>
&lt;p>在C++中，&lt;code>char * str&lt;/code> 和 &lt;code>char str[]&lt;/code> 都可以用来表示字符串，但是它们有一些区别。&lt;/p>
&lt;p>&lt;code>char * str&lt;/code> 实际上是一个指向字符数组的指针，它指向的是字符串的首地址。我们可以通过改变指针的位置来改变字符串的内容。例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>char * str = &amp;#34;Hello&amp;#34;;
str = &amp;#34;World&amp;#34;;
&lt;/code>&lt;/pre>&lt;p>这里我们将指针&lt;code>str&lt;/code>从原来的&amp;quot;Hello&amp;quot;改变为了&amp;quot;World&amp;quot;。&lt;/p>
&lt;p>另一方面，&lt;code>char str[]&lt;/code> 是一个字符数组，它在定义时需要指定数组的长度，且该长度不能被改变。例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>char str[10] = &amp;#34;Hello&amp;#34;;
&lt;/code>&lt;/pre>&lt;p>在这个例子中，定义了一个长度为10的字符数组，并将其初始化为&amp;quot;Hello&amp;quot;。如果我们想要将另一个字符串复制到该数组中，我们需要使用字符串库函数&lt;code>strcpy&lt;/code>，例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>char str[10];
strcpy(str, &amp;#34;World&amp;#34;);
&lt;/code>&lt;/pre>&lt;p>这里我们使用了&lt;code>strcpy&lt;/code>函数将&amp;quot;World&amp;quot;复制到了&lt;code>str&lt;/code>数组中。&lt;/p>
&lt;p>总的来说，&lt;code>char * str&lt;/code> 更加灵活，可以通过改变指针的位置来改变字符串的内容，但是需要注意指针的安全性。&lt;code>char str[]&lt;/code> 在定义时需要指定数组的长度，更加安全，但是不够灵活。&lt;/p>
&lt;p>举个例子：&lt;/p>
&lt;pre tabindex="0">&lt;code>char * str = &amp;#34;Hello&amp;#34;;
cout &amp;lt;&amp;lt; str &amp;lt;&amp;lt; endl; // 输出 &amp;#34;Hello&amp;#34;
str = &amp;#34;World&amp;#34;;
cout &amp;lt;&amp;lt; str &amp;lt;&amp;lt; endl; // 输出 &amp;#34;World&amp;#34;
char str2[10] = &amp;#34;Hello&amp;#34;;
cout &amp;lt;&amp;lt; str2 &amp;lt;&amp;lt; endl; // 输出 &amp;#34;Hello&amp;#34;
strcpy(str2, &amp;#34;World&amp;#34;);
cout &amp;lt;&amp;lt; str2 &amp;lt;&amp;lt; endl; // 输出 &amp;#34;World&amp;#34;
&lt;/code>&lt;/pre>&lt;blockquote>
&lt;p>一般用 char * str 好操作 ，要不你char str[] 这种数组你在最后得给他加\0 才算是字符串呢&lt;/p>
&lt;/blockquote>
&lt;h1 id="0">‘\0’&lt;/h1>
&lt;p>在C++中，&amp;rsquo;\0&amp;rsquo;是一个特殊的字符，表示字符串的结束。在使用字符数组表示字符串时，我们需要在数组的末尾添加&amp;rsquo;\0&amp;rsquo;来表示字符串的结束。例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>char str[10] = &amp;#34;Hello&amp;#34;;
str[5] = &amp;#39;\0&amp;#39;;
&lt;/code>&lt;/pre>&lt;p>这里我们将字符数组&lt;code>str&lt;/code>的第6个元素赋值为&amp;rsquo;\0&amp;rsquo;，表示字符串的结束。此时，&lt;code>str&lt;/code>中存储的字符串为&amp;quot;Hello&amp;quot;。&lt;/p>
&lt;p>总的来说，对于字符数组表示的字符串，我们需要注意添加&amp;rsquo;\0&amp;rsquo;来表示字符串的结束，避免出现意外的错误。&lt;/p>
&lt;h1 id="includestring">#include&lt;string>&lt;/h1>
&lt;p>在C++中，除了使用char类型和字符数组来处理字符串，我们还可以使用&lt;string>库。这个库提供了丰富的字符串处理函数，例如length、substr、find等。使用这些函数可以更加方便地进行字符串操作。&lt;/p>
&lt;p>例如，我们可以使用以下代码来创建一个字符串对象并输出其长度：&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;string&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;
int main() {
string str = &amp;#34;Hello World&amp;#34;;
cout &amp;lt;&amp;lt; str.length() &amp;lt;&amp;lt; endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>以上代码将会输出字符串&amp;quot;Hello World&amp;quot;的长度，也就是11。&lt;/p>
&lt;p>总的来说，C++提供了多种处理字符串的方法，我们可以根据实际情况选择合适的方法来进行字符串操作。&lt;/p>
&lt;blockquote>
&lt;p>底层是 &lt;strong>const&lt;/strong> char[]&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%20c1aea693e5cc47d8a53110577af87660/Untitled.png" alt="Untitled" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;/blockquote>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>在C++中，我们可以使用char类型和字符数组来处理字符串。同时，也可以利用C++提供的字符串库函数进行更方便的字符串处理操作。&lt;/p></description></item><item><title>C++中的字符串字面量</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E9%87%8F-0d677efe66d24fa48892844cda209126/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E9%87%8F-0d677efe66d24fa48892844cda209126/</guid><description>&lt;p>C++中的字符串字面量是由双引号包围的字符序列。它们被视为指向字符数组的指针，其中数组的最后一个元素是空字符。以下是一个字符串字面量的示例：&lt;/p>
&lt;pre tabindex="0">&lt;code>const char* str = &amp;#34;Hello, World!&amp;#34;;
&lt;/code>&lt;/pre>&lt;p>在上面的示例中，&amp;ldquo;Hello, World!&amp;ldquo;是字符串字面量。可以在程序中使用字符串字面量来表示文本或字符序列。例如，您可以将它们用作函数参数或将它们赋值给字符数组变量。&lt;/p>
&lt;blockquote>
&lt;p>需要注意的是，字符串字面量是常量，不允许修改其内容。如果您尝试修改字符串字面量中的任何字符，则会导致编译错误。
比如你想这么搞&lt;/p>
&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">str&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;Hello, World!&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">str&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="sc">&amp;#39;x&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">就会报错了&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>另外，C++11引入了原始字符串字面量，它们由R&amp;rdquo;()&amp;ldquo;语法表示，其中括号内的字符序列被视为原始字符串。这意味着在原始字符串中，转义字符不会被解释。以下是一个原始字符串字面量的示例：&lt;/p>
&lt;pre tabindex="0">&lt;code>const char* raw_str = R&amp;#34;(C:\\Users\\John\\Documents\\)&amp;#34;;
&lt;/code>&lt;/pre>&lt;p>在上面的示例中，R&amp;rdquo;()&amp;ldquo;中的字符序列被视为原始字符串，其中的反斜杠不需要进行转义。&lt;/p>
&lt;p>总之，字符串字面量是C++中常用的表示文本或字符序列的方式，而原始字符串字面量则可以方便地表示包含反斜杠的路径或正则表达式等。&lt;/p>
&lt;h1 id="别的char类型">别的char类型&lt;/h1>
&lt;p>在C++中，除了字符串字面量外，还有其他类型的字符字面量。例如，单引号括起来的字符字面量表示单个字符。例如，&amp;lsquo;a&amp;rsquo;表示字符a，&amp;lsquo;0&amp;rsquo;表示数字0。这些字符字面量可以用作函数参数或赋值给字符变量。&lt;/p>
&lt;p>此外，还有一种宽字符字面量，用L&amp;quot;&amp;ldquo;语法表示。它们与字符串字面量类似，但使用宽字符（wchar_t）而不是普通字符（char）。这些宽字符字面量在处理国际化和本地化问题时非常有用&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E9%87%8F%200d677efe66d24fa48892844cda209126/Untitled.png" alt="Untitled" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;h1 id="字符串相加不能直接用">字符串相加不能直接用+&lt;/h1>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E9%87%8F%200d677efe66d24fa48892844cda209126/Untitled%201.png" alt="Untitled" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>因为这些事字符串字面量，实际上他们是字符数组，或者字符指针，所以我们不能把两个指针相加&lt;/p>
&lt;h2 id="解决方案1">解决方案1&lt;/h2>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E9%87%8F%200d677efe66d24fa48892844cda209126/Untitled%202.png" alt="Untitled" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;h2 id="解决方案2">解决方案2&lt;/h2>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E9%87%8F%200d677efe66d24fa48892844cda209126/Untitled%203.png" alt="Untitled" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>s其实就是个操作函数&lt;/p>
&lt;h1 id="字符串字面量在内存中">字符串字面量在内存中&lt;/h1>
&lt;p>在C++中，字符串字面量被存储在程序的&lt;strong>只读数据区&lt;/strong>中，并在程序启动时加载到内存中。这意味着它们不能被修改，并且必须使用const char*或const char[]类型的指针来引用它们。&lt;/p>
&lt;p>由于字符串字面量是常量，因此它们可以在编译时进行优化。例如，如果两个字符串字面量具有相同的值，则它们可能会共享存储空间，从而减少程序的内存占用。&lt;/p>
&lt;p>需要注意的是，如果在程序中使用大量的字符串字面量，则可能会增加程序的内存占用。为了减少内存使用量，应该考虑使用动态分配内存来存储字符串，或者使用字符串流等工具来动态构建字符串。&lt;/p></description></item><item><title>C++中的引用</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E5%BC%95%E7%94%A8-052dd015f0c7427e8a99bfe77b873d1b/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E5%BC%95%E7%94%A8-052dd015f0c7427e8a99bfe77b873d1b/</guid><description>&lt;p>在C++中，引用是一种将变量绑定到另一个变量的方法。引用可以被视为变量的别名，它们与指针有些相似，但有一些重要的区别。&lt;/p>
&lt;p>引用是在定义时初始化的，一旦初始化完成，就不能再将其绑定到另一个变量或修改它所绑定的变量。与之相反的是，指针可以被重新赋值来指向不同的变量。&lt;/p>
&lt;p>另一个重要的区别是，引用不能为null，而指针可以为null。这意味着引用不需要在使用之前进行空指针检查，从而减少了代码的复杂性和错误的可能性。&lt;/p>
&lt;p>引用通常用于将函数的参数传递给其他函数。通过使用引用作为参数，可以避免将大型对象进行复制，从而提高程序的性能。同样，引用也可以用于从函数中返回值，而无需创建新的对象。&lt;/p>
&lt;p>在C++中，引用是非常有用的工具，可以用来编写高效且易于阅读的代码。理解引用的概念和用法对于成为C++开发人员是至关重要的一步。&lt;/p>
&lt;p>引用在C++中可以理解成一种语法糖，编译器对其不进行编译，只停留在代码文本层面，仅代表了一个变量的另一个名字，可以在程序员编码时更加高效，代码保持干净&lt;/p>
&lt;p>以下是引用的一个例子：&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;iostream&amp;gt;
using namespace std;
void swap(int&amp;amp; x, int&amp;amp; y) {
int temp = x;
x = y;
y = temp;
}
int main() {
int a = 5;
int b = 10;
cout &amp;lt;&amp;lt; &amp;#34;Before swap, a = &amp;#34; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &amp;#34; and b = &amp;#34; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; endl;
// Pass variables by reference to swap function
swap(a, b);
cout &amp;lt;&amp;lt; &amp;#34;After swap, a = &amp;#34; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &amp;#34; and b = &amp;#34; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们定义了一个&lt;code>swap&lt;/code>函数，它接受两个整数的引用作为参数，并交换它们的值。在&lt;code>main&lt;/code>函数中，我们声明了两个整数变量&lt;code>a&lt;/code>和&lt;code>b&lt;/code>，并将它们传递给&lt;code>swap&lt;/code>函数。由于我们使用了引用参数，&lt;code>swap&lt;/code>函数可以直接修改变量&lt;code>a&lt;/code>和&lt;code>b&lt;/code>的值，而不需要创建额外的变量或进行复制。这样可以提高程序的性能，并减少内存使用量。省的return， 带来内存的开销。&lt;/p></description></item><item><title>C++中的指针</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%8C%87%E9%92%88-9b510564ca8d4ad3b860d216573eb30b/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%8C%87%E9%92%88-9b510564ca8d4ad3b860d216573eb30b/</guid><description>&lt;p>在C++中，指针是一种非常强大的数据类型。指针是一种变量，它存储了一个内存地址，该内存地址指向另一个变量。指针可以用于访问和操作存储在内存中的数据。&lt;/p>
&lt;p>指针变量可以通过在变量名前面加上星号（*）来声明。例如，下面的代码声明了一个名为 *ptr 的整数类型指针变量：&lt;/p>
&lt;pre tabindex="0">&lt;code>int* ptr;
&lt;/code>&lt;/pre>&lt;p>在上面的代码中，星号（*）表示 ptr 是一个指向整数类型的指针变量。指针变量在声明时必须指定指针类型，因为不同类型的指针可以存储不同类型的数据。&lt;/p>
&lt;p>指针变量可以使用“&amp;amp;”运算符获取变量的地址。例如，下面的代码获取了整数变量 num 的地址，并将其存储在指针变量 ptr 中：&lt;/p>
&lt;pre tabindex="0">&lt;code>int num = 10;
int* ptr = &amp;amp;num;
&lt;/code>&lt;/pre>&lt;p>在上面的代码中，ptr 指向 num 变量的地址。&lt;/p>
&lt;p>使用指针可以访问指针所指向的变量。例如，下面的代码使用指针访问 num 变量，并将其值增加了 1：&lt;/p>
&lt;pre tabindex="0">&lt;code>*ptr = *ptr + 1;
&lt;/code>&lt;/pre>&lt;p>在上面的代码中，星号（*）表示访问指针所指向的变量，将其值加 1。&lt;/p>
&lt;p>指针还可以用于动态内存分配。通过使用 new 运算符，可以在程序运行时动态地分配内存。例如，下面的代码分配了一个整数类型的内存块，并将其地址存储在指针变量 ptr 中：&lt;/p>
&lt;pre tabindex="0">&lt;code>int* ptr = new int;
&lt;/code>&lt;/pre>&lt;p>在上面的代码中，new 运算符分配了一个整数类型的内存块，并返回其地址，该地址被存储在指针变量 ptr 中。&lt;/p>
&lt;p>指针还可以用于访问数组中的元素。例如，下面的代码声明了一个指向整数类型的指针变量，然后使用该指针访问数组中的元素：&lt;/p>
&lt;pre tabindex="0">&lt;code>int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr; // ptr 指向数组的第一个元素
for(int i = 0; i &amp;lt; 5; i++) {
cout &amp;lt;&amp;lt; *ptr &amp;lt;&amp;lt; endl;
ptr++;
}
&lt;/code>&lt;/pre>&lt;p>在上面的代码中，ptr 指向数组的第一个元素。然后，使用指针访问数组中的每个元素，并将其打印到控制台上。&lt;/p>
&lt;p>指针是一种非常强大的数据类型，在 C++ 中具有广泛的应用。了解指针的工作原理和使用方法是编写高效程序的重要一步。&lt;/p></description></item><item><title>C++中的接口（纯虚函数）</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%8E%A5%E5%8F%A3%E7%BA%AF%E8%99%9A%E5%87%BD%E6%95%B0-d02581428abd4cdead1da2c4b780467c/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%8E%A5%E5%8F%A3%E7%BA%AF%E8%99%9A%E5%87%BD%E6%95%B0-d02581428abd4cdead1da2c4b780467c/</guid><description>&lt;p>在C++中，接口通常用纯虚函数的形式来实现。纯虚函数是一种没有实现的虚函数，其定义需要在派生类中完成。接口中只有纯虚函数，没有数据成员和函数定义，因此派生类必须实现接口中定义的所有纯虚函数，否则编译器会报错。&lt;/p>
&lt;p>接口可以用于实现多态性，一个类可以继承多个接口，从而具有多个行为。例如，一个图形界面程序可以定义一个绘制图形的接口和一个处理用户输入的接口，不同的图形对象可以继承不同的接口，以实现不同的行为。&lt;/p>
&lt;p>以下是一个简单的接口示例：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Shape&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="n">draw&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Circle&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Shape&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">draw&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 绘制圆形
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Square&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Shape&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">draw&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 绘制正方形
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个示例中，Shape类是一个接口，定义了一个纯虚函数draw()。Circle和Square类都继承了Shape类，并实现了draw()函数来绘制不同的形状。&lt;/p>
&lt;p>接口的使用可以使代码更加模块化和可扩展，同时也可以提高代码的可读性和可维护性。但是，过多的接口也可能会使代码过于复杂，因此在设计时需要权衡利弊，尽量避免过多的接口设计。&lt;/p>
&lt;blockquote>
&lt;p>其实C++没有&lt;code>interface&lt;/code> 关键字&lt;/p>
&lt;p>在C++中，接口的实现通常采用纯虚函数的形式。纯虚函数是没有实现的虚函数，其定义需要在派生类中完成。接口中没有数据成员和函数定义，只有纯虚函数，因此派生类必须实现接口中定义的所有纯虚函数。接口可以实现多态性，一个类可以继承多个接口，以具有不同的行为。在设计时需要权衡利弊，尽量避免过多的接口设计。&lt;/p>
&lt;/blockquote>
&lt;p>下面是一个关于接口多态的例子：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Shape.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">Shape&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="n">draw&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Colorful.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">Colorful&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="n">setColor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Circle.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">Circle&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Shape&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Colorful&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">draw&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 绘制圆形
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">setColor&lt;/span> &lt;span class="nf">override&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 设置圆形的颜色
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Square.h
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">class&lt;/span> &lt;span class="nc">Square&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Shape&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Colorful&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">draw&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 绘制正方形
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">setColor&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="o">****&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">g&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 设置正方形的颜色
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// main.cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Shape&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">shapes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shapes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Circle&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shapes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Square&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shapes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Colorful&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">colorful&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">dynamic_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Colorful&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">shapes&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">colorful&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">colorful&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">setColor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">255&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个例子中，Shape类和Colorful类都是接口，Circle和Square类都继承了Shape类和Colorful类，并实现了draw()函数和setColor()函数来绘制不同的形状和设置不同的颜色。在main函数中，创建了一个Shape指针数组shapes，其中存放了一个Circle对象和一个Square对象。通过循环依次调用它们的draw()函数，并通过dynamic_cast将它们转换成Colorful指针，最后通过setColor()函数设置它们的颜色。&lt;/p>
&lt;p>这个例子展示了接口的多态特性，通过继承不同的接口，一个类可以具有多个行为，可以在不同的情况下调用不同的函数实现不同的功能。&lt;/p>
&lt;h1 id="多态接收一个接口作为参数">多态：接收一个接口作为参数&lt;/h1>
&lt;p>在C++中，多态可以通过接收一个接口作为参数来实现。例如，一个函数可以接收一个Shape接口作为参数，从而可以接收任意实现了Shape接口的类的对象。这样，函数就可以在&lt;strong>不知道具体对象类型的情况下调用对象的函数&lt;/strong>。&lt;/p>
&lt;p>以下是一个简单的例子：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">void&lt;/span> &lt;span class="nf">drawShape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Shape&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">shape&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">shape&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Circle&lt;/span> &lt;span class="n">circle&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Square&lt;/span> &lt;span class="n">square&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">drawShape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">circle&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">drawShape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">square&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个例子中，drawShape()函数接收一个Shape接口作为参数，并调用它的draw()函数来绘制图形。在main函数中，创建了一个Circle对象和一个Square对象，并将它们的地址传递给drawShape()函数。由于Circle和Square都继承了Shape接口并实现了它的draw()函数，因此可以成功地调用它们的draw()函数来绘制图形。&lt;/p>
&lt;p>多态可以使代码更加灵活和可扩展，同时也可以提高代码的可读性和可维护性。但是，过多的多态也可能会使代码过于复杂，因此在设计时需要权衡利弊，尽量避免过多的多态设计。&lt;/p></description></item><item><title>C++中的数组</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%95%B0%E7%BB%84-49ee11cae6c64590ac3b32234d9905c3/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%95%B0%E7%BB%84-49ee11cae6c64590ac3b32234d9905c3/</guid><description>&lt;p>数组是一种可以保存一系列相同类型数据的数据结构。在C++中，数组是一种容器，它可以存储一组固定大小的相同类型元素。数组中的每个元素都可以通过索引访问，索引从0开始，最大索引为数组大小减一。&lt;/p>
&lt;p>在C++中，声明一个数组需要指定数组的类型、名称和大小。例如，以下代码声明了一个名为&lt;code>myArray&lt;/code>的整数类型数组，大小为5：&lt;/p>
&lt;pre tabindex="0">&lt;code>int myArray[5];
&lt;/code>&lt;/pre>&lt;p>数组元素的访问方式是通过数组名称和索引。例如，要访问数组&lt;code>myArray&lt;/code>的第一个元素，可以使用以下代码：&lt;/p>
&lt;pre tabindex="0">&lt;code>int x = myArray[0];
&lt;/code>&lt;/pre>&lt;p>在C++中，数组的大小必须是一个常量表达式，因此不能使用变量来定义数组的大小。但是，可以使用&lt;code>const&lt;/code>关键字来定义一个常量，然后将其用作数组的大小。例如，以下代码定义了一个名为&lt;code>SIZE&lt;/code>的常量，并将其用作数组&lt;code>myArray&lt;/code>的大小：&lt;/p>
&lt;pre tabindex="0">&lt;code>const int SIZE = 5;
int myArray[SIZE];
&lt;/code>&lt;/pre>&lt;p>在C++中，还可以使用循环来遍历数组中的所有元素。例如，以下代码使用for循环遍历数组&lt;code>myArray&lt;/code>中的所有元素：&lt;/p>
&lt;pre tabindex="0">&lt;code>for(int i = 0; i &amp;lt; SIZE; i++) {
int x = myArray[i];
// 在这里使用元素值
}
&lt;/code>&lt;/pre>&lt;p>总之，在C++中，数组是一种非常有用的数据结构，它可以容纳一系列相同类型的元素，并允许通过索引访问这些元素。声明一个数组需要指定数组的类型、名称和大小，然后可以使用索引访问数组中的元素。可以使用常量表达式来定义数组的大小，并可以使用循环遍历数组中的所有元素。&lt;/p>
&lt;h1 id="在堆上创建数组">在堆上创建数组&lt;/h1>
&lt;p>在堆上创建数组需要使用&lt;code>new&lt;/code>运算符。例如，以下代码创建了一个大小为5的整数类型数组：&lt;/p>
&lt;pre tabindex="0">&lt;code>int* myArray = new int[5];
&lt;/code>&lt;/pre>&lt;p>在动态创建数组后，可以使用与静态数组相同的语法访问和操作数组元素。在完成使用数组后，必须使用&lt;code>delete[]&lt;/code>运算符释放动态分配的内存。例如，以下代码释放先前创建的动态数组：&lt;/p>
&lt;pre tabindex="0">&lt;code>delete[] myArray;
&lt;/code>&lt;/pre>&lt;p>总之，在C++中，可以使用&lt;code>new&lt;/code>运算符在堆上动态创建数组。创建数组后，可以使用与静态数组相同的语法访问和操作数组元素。在完成使用数组后，必须使用&lt;code>delete[]&lt;/code>运算符释放动态分配的内存。&lt;/p>
&lt;h1 id="c11中的数组">C++11中的数组&lt;/h1>
&lt;p>在C++11中，引入了一个名为&lt;code>array&lt;/code>的标准数组容器。&lt;code>array&lt;/code>容器与静态数组类似，但具有更好的类型安全性和迭代器支持。例如，以下代码声明了一个名为&lt;code>myArray&lt;/code>的整数类型&lt;code>array&lt;/code>，大小为5：&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;array&amp;gt;
std::array&amp;lt;int, 5&amp;gt; myArray;
&lt;/code>&lt;/pre>&lt;p>可以使用&lt;code>at()&lt;/code>成员函数或&lt;code>[]&lt;/code>运算符来访问&lt;code>array&lt;/code>容器中的元素。例如，以下代码访问&lt;code>myArray&lt;/code>的第一个元素：&lt;/p>
&lt;pre tabindex="0">&lt;code>int x = myArray.at(0); // 或者 myArray[0];
&lt;/code>&lt;/pre>&lt;p>&lt;code>array&lt;/code>容器还具有其他功能，例如&lt;code>size()&lt;/code>成员函数可以用于获取数组的大小，&lt;code>fill()&lt;/code>成员函数可以用于将所有元素设置为指定值，&lt;code>begin()&lt;/code>和&lt;code>end()&lt;/code>成员函数可以用于迭代数组中的所有元素。&lt;/p>
&lt;p>总之，在C++11中，可以使用&lt;code>array&lt;/code>容器代替静态数组。&lt;code>array&lt;/code>容器提供了更好的类型安全性和迭代器支持，并具有许多有用的成员函数。&lt;/p></description></item><item><title>C++中的智能指针</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88-376fa1bb49634219b9bdc5e8a5bd830a/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88-376fa1bb49634219b9bdc5e8a5bd830a/</guid><description>&lt;p>在 C++ 中，指针是一种非常有用的数据类型，但是它们也经常引起内存泄漏和悬空指针等问题。智能指针是一种特殊的指针，它能够自动管理内存，帮助我们避免这些常见的问题。&lt;/p>
&lt;p>智能指针是一个对象，它的行为类似于指针，但是它还有一些额外的功能。智能指针可以自动地跟踪对象的生命周期，并在不再需要时释放内存。这种自动化的内存管理使得代码更加健壮和可靠。&lt;/p>
&lt;p>C++ 有两种内置的智能指针类型：shared_ptr 和 unique_ptr。shared_ptr 可以跟踪多个指向同一对象的指针，而 unique_ptr 只能有一个指针指向一个对象。shared_ptr 使用引用计数来管理内存，unique_ptr 管理独占所有权。&lt;/p>
&lt;p>智能指针的使用非常简单。只需要声明一个智能指针对象，并将其初始化为指向对象的指针。智能指针将负责管理内存，即使在函数返回或抛出异常时也能确保内存被正确释放。&lt;/p>
&lt;p>智能指针是一种非常强大的工具，它可以帮助我们编写更加健壮和可靠的 C++ 代码。在编写 C++ 程序时，我们应该尽可能地使用智能指针来管理内存，以避免内存泄漏和悬空指针等问题。&lt;/p>
&lt;p>例如，我们可以使用智能指针来管理一个动态分配的对象：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;memory&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">MyClass&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MyClass&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;MyClass constructed&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">MyClass&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;MyClass destructed&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">Print&lt;/span>&lt;span class="p">(){}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">shared_ptr&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">MyClass&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">myClassPtr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">MyClass&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// do something with myClassPtr
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个例子中，我们使用 shared_ptr 来管理一个动态分配的 MyClass 对象。当 myClassPtr 超出作用域时，它会自动释放 MyClass 对象的内存。这种自动化的内存管理使得代码更加健壮和可靠。&lt;/p></description></item><item><title>C++中的构造函数</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0-4dfd196faf5f4b809efbd654fffb029a/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0-4dfd196faf5f4b809efbd654fffb029a/</guid><description>&lt;h1 id="为什么需要构造函数">为什么需要构造函数&lt;/h1>
&lt;p>构造函数是一种特殊的函数，用于在创建对象时初始化对象的成员变量。它们在对象创建时自动调用，因此它们很有用，可以确保在使用对象之前对象已经被正确地初始化。&lt;/p>
&lt;p>构造函数还可以用来设置默认值、分配内存和执行其他必要的初始化步骤，以确保对象能够正常工作。因此，它们是面向对象编程中不可或缺的一部分。&lt;/p>
&lt;p>主要是初始化很有用&lt;/p>
&lt;p>例如，以下是一个简单的类定义，它包含一个构造函数来初始化一个整数成员变量：&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;iostream&amp;gt;
class MyClass {
public:
int myInt;
MyClass(int x) {
myInt = x;
}
};
int main() {
MyClass obj(42);
std::cout &amp;lt;&amp;lt; obj.myInt &amp;lt;&amp;lt; std::endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们定义了一个名为MyClass的类，它有一个整数成员变量myInt和一个构造函数，该函数将传递的值分配给myInt。在创建对象时，构造函数将自动调用，从而初始化myInt。在这个例子中，我们创建了一个名为obj的MyClass对象，并将值42传递给构造函数。当我们打印obj.myInt时，将输出42。&lt;/p>
&lt;h1 id="默认构造函数">默认构造函数&lt;/h1>
&lt;p>默认构造函数是指一个类的构造函数，如果没有显式定义，编译器就会自动提供。默认构造函数没有参数，它的主要作用是初始化对象的成员变量，将它们设置为默认值。&lt;/p>
&lt;p>在C++中，如果定义了带参数的构造函数，编译器就不会自动提供默认构造函数。因此，在需要使用默认构造函数的情况下，必须显式定义它。&lt;/p>
&lt;p>例如，以下是一个简单的类定义，它包含一个默认构造函数：&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;iostream&amp;gt;
class MyClass {
public:
int myInt;
MyClass() {
myInt = 0;
}
};
int main() {
MyClass obj;
std::cout &amp;lt;&amp;lt; obj.myInt &amp;lt;&amp;lt; std::endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们定义了一个名为MyClass的类，它有一个整数成员变量myInt和一个默认构造函数，该函数将myInt设置为0。在创建对象时，构造函数将自动调用，从而初始化myInt。在这个例子中，我们创建了一个名为obj的MyClass对象，并打印了它的myInt成员变量，它的默认值为0。&lt;/p>
&lt;h1 id="怎样隐藏构造函数">怎样隐藏构造函数&lt;/h1>
&lt;p>可以使用访问修饰符来隐藏构造函数，使它们无法从外部访问。例如，将构造函数声明为private将防止在类外部创建对象，但允许类内部的其他函数创建对象。这种技术被称为单例模式，它可以确保只有一个类的实例存在于任何给定的时间。&lt;/p>
&lt;p>例如，我们可以创建一个名为Singleton的类，它有一个私有的构造函数和一个公共的静态方法GetInstance，用于获取类的唯一实例。在GetInstance方法中，我们使用一个静态的Singleton指针变量来保存类的唯一实例，并在第一次调用GetInstance时创建它。以后的每次调用都将返回相同的实例，因此只有一个Singleton实例存在于任何给定的时间。&lt;/p>
&lt;p>以下是Singleton类的示例代码：&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;iostream&amp;gt;
class Singleton {
private:
static Singleton* instance;
// 私有构造函数
Singleton() {}
public:
// 静态方法，用于获取唯一实例
static Singleton* GetInstance() {
if (!instance) {
instance = new Singleton();
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
int main() {
Singleton* obj1 = Singleton::GetInstance();
Singleton* obj2 = Singleton::GetInstance();
// obj1和obj2是同一个对象
std::cout &amp;lt;&amp;lt; obj1 &amp;lt;&amp;lt; std::endl;
std::cout &amp;lt;&amp;lt; obj2 &amp;lt;&amp;lt; std::endl;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们定义了一个名为Singleton的类，它有一个私有的构造函数和一个公共的静态方法GetInstance。我们还定义了一个静态的Singleton指针变量instance，用于保存类的唯一实例。&lt;/p>
&lt;p>在main函数中，我们首先使用GetInstance方法获取一个名为obj1的Singleton实例，然后再次调用GetInstance方法获取一个名为obj2的Singleton实例。由于GetInstance方法始终返回相同的实例，因此obj1和obj2是相同的对象。我们打印它们的地址，以证明它们是相同的对象。&lt;/p>
&lt;h1 id="析构函数">析构函数&lt;/h1>
&lt;p>除了构造函数外，C++中还有一个特殊的函数叫做析构函数。析构函数在对象被销毁时自动调用，用于释放对象使用的资源（例如内存）。&lt;/p>
&lt;p>在C++中，析构函数的名称是在类名称前面加上一个波浪号（~），例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>class MyClass {
public:
~MyClass() {
// 析构函数代码
}
};
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们定义了一个名为MyClass的类，它有一个析构函数。当对象被销毁时，析构函数将自动调用，执行代码以释放使用的资源。&lt;/p>
&lt;h1 id="移动构造函数和移动赋值运算符">移动构造函数和移动赋值运算符&lt;/h1>
&lt;p>移动构造函数和移动赋值运算符是C++11引入的新功能，用于提高代码的效率。它们允许将一个对象的资源（例如内存）移动到另一个对象中，而不是进行复制。&lt;/p>
&lt;p>移动构造函数在创建新对象时从现有对象中“窃取”资源。移动赋值运算符在将一个对象的值赋给另一个对象时执行相同的操作。&lt;/p>
&lt;p>例如，以下是一个简单的类定义，其中包含一个移动构造函数和一个移动赋值运算符：&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;iostream&amp;gt;
class MyClass {
public:
int* myArray;
int size;
// 移动构造函数
MyClass(MyClass&amp;amp;&amp;amp; other) {
myArray = other.myArray;
size = other.size;
other.myArray = nullptr;
other.size = 0;
}
// 移动赋值运算符
MyClass&amp;amp; operator=(MyClass&amp;amp;&amp;amp; other) {
if (this != &amp;amp;other) {
delete[] myArray;
myArray = other.myArray;
size = other.size;
other.myArray = nullptr;
other.size = 0;
}
return *this;
}
// 构造函数
MyClass(int s) {
myArray = new int[s];
size = s;
}
// 析构函数
~MyClass() {
delete[] myArray;
}
};
int main() {
MyClass obj1(10); // 创建一个对象
MyClass obj2(std::move(obj1)); // 使用移动构造函数创建一个新对象
obj1 = MyClass(20); // 使用移动赋值运算符将一个临时对象的值赋给一个对象
return 0;
}
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们定义了一个名为MyClass的类，它有一个整数数组成员变量myArray和一个整数成员变量size。我们还定义了一个移动构造函数、一个移动赋值运算符、一个构造函数和一个析构函数。&lt;/p>
&lt;p>在main函数中，我们首先创建了一个名为obj1的MyClass对象，然后使用移动构造函数创建了一个名为obj2的新对象。接下来，我们使用移动赋值运算符将一个临时的MyClass对象的值赋给obj1。这些操作允许我们“移动”资源，而不是进行复制，从而提高了代码的效率。&lt;/p></description></item><item><title>C++中的析构函数</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0-62fd40fc08b04b85b2fa8ca5d5e14ade/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0-62fd40fc08b04b85b2fa8ca5d5e14ade/</guid><description>&lt;p>在C++中，析构函数是一种特殊类型的函数，它在对象生命周期结束时被调用。析构函数的作用是清理对象在其生命周期中使用的资源，例如动态分配的内存和打开的文件等。因此，它是C++中重要的特性之一。&lt;/p>
&lt;p>在类定义中，析构函数的名称是与类名相同，前面加上一个波浪号(~)。如下所示：&lt;/p>
&lt;pre tabindex="0">&lt;code>class MyClass {
public:
// 构造函数
MyClass() {
// 对象创建时执行
}
// 析构函数
~MyClass() {
// 对象销毁时执行
}
};
&lt;/code>&lt;/pre>&lt;p>当对象超出其作用域或被显式地删除时，析构函数将被调用。例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>int main() {
MyClass obj;
// 对象超出作用域
return 0;
}
或
int main() {
MyClass *ptr = new MyClass();
// 显式地删除对象
delete ptr;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>当执行以上代码时，MyClass的析构函数将被自动调用，以便清理对象在其生命周期中使用的资源。&lt;/p>
&lt;p>需要注意的是，当一个对象被销毁时，它的成员变量和基类也将被销毁。因此，在析构函数中，通常需要对所有成员变量和基类进行清理操作。&lt;/p>
&lt;p>总之，析构函数是C++中非常重要的特性之一，它允许程序员在对象生命周期结束时清理对象所使用的资源。在编写C++程序时，需要了解析构函数的使用和语法，以便正确地管理程序所使用的资源。&lt;/p>
&lt;p>不推荐手动调用析构函数&lt;/p></description></item><item><title>C++中的枚举</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%9E%9A%E4%B8%BE-c789b3b413054bb5a75e493dfff9b88e/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E6%9E%9A%E4%B8%BE-c789b3b413054bb5a75e493dfff9b88e/</guid><description>&lt;p>在C++中，枚举是一种用户自定义的数据类型，它允许程序员定义一组命名的常量。这些常量可以被当作整数使用，并且在程序中具有固定的值。&lt;/p>
&lt;p>在C++中，要定义一个枚举，需要使用关键字&amp;rsquo;enum&amp;rsquo;。下面是一个例子：&lt;/p>
&lt;pre tabindex="0">&lt;code>enum Season {Spring, Summer, Fall, Winter};
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们定义了一个名为&amp;rsquo;Season&amp;rsquo;的枚举类型，其中包含了四个常量：Spring，Summer，Fall和Winter。这些常量的值分别为0，1，2和3。如果需要改变这些常量的值，可以显式地指定它们的值，如下所示：&lt;/p>
&lt;pre tabindex="0">&lt;code>enum Season {Spring=1, Summer=2, Fall=3, Winter=4};
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们将Spring的值指定为1，Summer的值指定为2，依此类推。&lt;/p>
&lt;p>枚举常量的值可以在程序中使用，例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>Season currentSeason = Summer;
if (currentSeason == Summer) {
cout &amp;lt;&amp;lt; &amp;#34;It&amp;#39;s Summer!&amp;#34; &amp;lt;&amp;lt; endl;
}
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们将currentSeason的值设为Summer，然后检查它是否等于Summer。如果是，就输出&amp;quot;It&amp;rsquo;s Summer!&amp;quot;。&lt;/p>
&lt;p>枚举类型还可以用于switch语句，例如：&lt;/p>
&lt;pre tabindex="0">&lt;code>switch(currentSeason) {
case Spring:
cout &amp;lt;&amp;lt; &amp;#34;It&amp;#39;s Spring!&amp;#34; &amp;lt;&amp;lt; endl;
break;
case Summer:
cout &amp;lt;&amp;lt; &amp;#34;It&amp;#39;s Summer!&amp;#34; &amp;lt;&amp;lt; endl;
break;
case Fall:
cout &amp;lt;&amp;lt; &amp;#34;It&amp;#39;s Fall!&amp;#34; &amp;lt;&amp;lt; endl;
break;
case Winter:
cout &amp;lt;&amp;lt; &amp;#34;It&amp;#39;s Winter!&amp;#34; &amp;lt;&amp;lt; endl;
break;
}
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们使用switch语句检查currentSeason的值，并根据其值输出相应的信息。&lt;/p>
&lt;p>总之，枚举是C++中一种非常有用的数据类型，它可以帮助我们定义一组常量，并在程序中使用它们。&lt;/p></description></item><item><title>C++中的继承</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E7%BB%A7%E6%89%BF-b70e00c1604e4b2e8a25eb4a67c1fd8e/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E7%BB%A7%E6%89%BF-b70e00c1604e4b2e8a25eb4a67c1fd8e/</guid><description>&lt;h1 id="c中的继承">C++中的继承&lt;/h1>
&lt;h1 id="c中的继承-1">C++中的继承&lt;/h1>
&lt;p>继承是面向对象编程中的一个重要概念，C++作为一门面向对象编程语言，支持类的继承。子类可以继承父类的属性和方法，从而避免了代码的重复编写，提高了代码的可重用性。&lt;/p>
&lt;p>C++中的继承有以下几种类型：&lt;/p>
&lt;h2 id="公有继承public-inheritance">公有继承（public inheritance）&lt;/h2>
&lt;p>公有继承是指子类继承父类的公有成员，父类的私有成员和保护成员不能被子类访问。&lt;/p>
&lt;h2 id="私有继承private-inheritance">私有继承（private inheritance）&lt;/h2>
&lt;p>私有继承是指子类继承父类的私有成员和保护成员，但不能访问父类的公有成员。私有继承主要用于实现“has-a”关系。&lt;/p>
&lt;h2 id="保护继承protected-inheritance">保护继承（protected inheritance）&lt;/h2>
&lt;p>保护继承是指子类继承父类的保护成员和公有成员，但不能访问父类的私有成员。保护继承主要用于实现“is-a”关系。&lt;/p>
&lt;p>在C++中，子类可以通过继承的方式获得父类的所有成员，包括数据成员和成员函数。在子类中可以通过父类名::成员名的方式来访问父类的成员。同时，C++中还支持虚继承和多重继承等高级特性，可以更加灵活地设计类的继承关系。&lt;/p>
&lt;p>继承是C++面向对象编程中的重要概念，掌握继承的原理和用法对于设计高质量的面向对象程序非常重要。&lt;/p>
&lt;p>例如，我们有一个 &lt;code>Animal&lt;/code> 类，里面包含了 &lt;code>name&lt;/code> 和 &lt;code>age&lt;/code> 两个属性以及 &lt;code>eat()&lt;/code> 和 &lt;code>sleep()&lt;/code> 两个方法。现在，我们需要实现一个 &lt;code>Dog&lt;/code> 类，它继承自 &lt;code>Animal&lt;/code> 类，并且还有一个 &lt;code>bark()&lt;/code> 方法。我们可以这样定义 &lt;code>Dog&lt;/code> 类：&lt;/p>
&lt;pre tabindex="0">&lt;code>class Animal {
public:
string name;
int age;
void eat() {
cout &amp;lt;&amp;lt; &amp;#34;I am eating.&amp;#34; &amp;lt;&amp;lt; endl;
}
void sleep() {
cout &amp;lt;&amp;lt; &amp;#34;I am sleeping.&amp;#34; &amp;lt;&amp;lt; endl;
}
};
&lt;/code>&lt;/pre>&lt;pre tabindex="0">&lt;code>class Dog : public Animal {
public:
void bark() {
cout &amp;lt;&amp;lt; &amp;#34;Woof!&amp;#34; &amp;lt;&amp;lt; endl;
}
};
&lt;/code>&lt;/pre>&lt;p>这样，&lt;code>Dog&lt;/code> 类就继承了 &lt;code>Animal&lt;/code> 类的属性和方法，并且还新增了一个 &lt;code>bark()&lt;/code> 方法。我们可以这样使用 &lt;code>Dog&lt;/code> 类：&lt;/p>
&lt;pre tabindex="0">&lt;code>Dog myDog;
myDog.name = &amp;#34;Fido&amp;#34;;
myDog.age = 3;
myDog.eat();
myDog.sleep();
myDog.bark();
&lt;/code>&lt;/pre>&lt;p>这样就可以创建一个 &lt;code>Dog&lt;/code> 对象，并且调用它的属性和方法了。&lt;/p>
&lt;p>在C++中，继承关系会影响成员的可见性。具体来说，成员的可见性取决于它在父类中的访问权限以及继承方式。&lt;/p>
&lt;p>如果使用公有继承，在子类中可以访问父类的公有成员和受保护的成员，但不能访问父类的私有成员。如果使用私有继承，在子类中可以访问父类的受保护的成员和私有成员，但不能访问父类的公有成员。如果使用保护继承，在子类中可以访问父类的公有成员和受保护的成员，但不能访问父类的私有成员。&lt;/p>
&lt;p>需要注意的是，在子类中可以通过父类名::成员名的方式来访问父类的成员，不受继承方式的限制。此外，如果在子类中定义了与父类同名的成员函数或成员变量，则会覆盖父类的同名成员，这被称为函数重载或变量隐藏。&lt;/p>
&lt;p>继承是C++面向对象编程中的重要概念，掌握继承的原理和用法对于设计高质量的面向对象程序非常重要。&lt;/p></description></item><item><title>C++虚函数</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E8%99%9A%E5%87%BD%E6%95%B0-e15af8370b37486d9ff31dea7793c5b9/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/c++%E4%B8%AD%E7%9A%84%E8%99%9A%E5%87%BD%E6%95%B0-e15af8370b37486d9ff31dea7793c5b9/</guid><description>&lt;p>C++中的虚函数是一种在运行时动态绑定的机制。虚函数使得C++中的多态性成为可能，因为它允许在运行时选择正确的函数实现。&lt;/p>
&lt;p>在C++中，虚函数是通过在基类函数声明前面添加关键字“virtual”来声明的。当派生类继承该函数时，如果它重写了该函数并且在派生类函数声明前面也加上了“virtual”关键字，那么该函数就变成了虚函数。派生类中的虚函数实现将覆盖基类中的虚函数实现，从而实现了运行时多态性。&lt;/p>
&lt;p>虚函数的工作原理是通过虚函数表来实现的。每个包含虚函数的类都有一个指向虚函数表的指针，虚函数表是一个包含该类中虚函数指针的数组。当调用虚函数时，程序会查找对象的虚函数表，然后根据表中存储的函数指针调用正确的函数实现。&lt;/p>
&lt;p>虚函数在C++中的使用非常普遍，特别是在面向对象编程中。它使得代码更加模块化和可扩展，并且提高了代码的可维护性和可读性。&lt;/p>
&lt;p>总之，虚函数是C++中一种重要的机制，它允许在运行时动态绑定函数实现，从而实现多态性。对于任何想要在C++中编写高效且易于维护的代码的开发者来说，了解和掌握虚函数的使用是非常重要的。&lt;/p>
&lt;p>以下是一个虚函数的简单示例，演示了如何在派生类中重写基类中的虚函数：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Base&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">**&lt;/span>&lt;span class="k">virtual&lt;/span>&lt;span class="o">**&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="n">say_hello&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Hello from Base&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Derived&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Base&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="n">say_hello&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Hello from Derived&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Base&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Derived&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">b&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">say_hello&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="c1">// 输出 &amp;#34;Hello from Derived&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">delete&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>在这个例子中，我们有一个基类 &lt;code>Base&lt;/code>，它有一个名为 &lt;code>say_hello&lt;/code> 的虚函数。我们还有一个派生类 &lt;code>Derived&lt;/code>，它重写了 &lt;code>say_hello&lt;/code> 函数。在 &lt;code>main&lt;/code> 函数中，我们首先创建了一个 &lt;code>Derived&lt;/code> 类型的对象，然后将其赋值给一个指向基类的指针 &lt;code>b&lt;/code>。接下来，我们调用 &lt;code>b&lt;/code> 的 &lt;code>say_hello&lt;/code> 函数。由于 &lt;code>say_hello&lt;/code> 是虚函数，程序会在运行时选择正确的函数实现，即 &lt;code>Derived&lt;/code> 类中的 &lt;code>say_hello&lt;/code> 函数。因此，程序将输出 &amp;ldquo;Hello from Derived&amp;rdquo;。&lt;/p>
&lt;blockquote>
&lt;p>其实就是base class 里边有个函数可以在 sub class 里重写，给了个标记 virtual, 告诉编译器，生成 v表吧，为这个函数。这样如果它被重写了，你就可以指向正确的函数&lt;/p>
&lt;/blockquote>
&lt;h1 id="c11-override关键字">C++11 override关键字&lt;/h1>
&lt;p>C++11中加入了一个新的关键字 &lt;code>override&lt;/code>，用于表示派生类中的函数是重写了基类中的虚函数。使用 &lt;code>override&lt;/code> 可以帮助我们避免因为函数签名不一致而无法正确重写虚函数的问题。如果派生类中的函数没有正确重写基类中的虚函数，编译器将会报错。&lt;/p>
&lt;p>以下是使用 &lt;code>override&lt;/code> 关键字的示例：&lt;/p>
&lt;pre tabindex="0">&lt;code>#include &amp;lt;iostream&amp;gt;
class Base {
public:
virtual void say_hello() {
std::cout &amp;lt;&amp;lt; &amp;#34;Hello from Base&amp;#34; &amp;lt;&amp;lt; std::endl;
}
};
class Derived : public Base {
public:
void say_hello() override {
std::cout &amp;lt;&amp;lt; &amp;#34;Hello from Derived&amp;#34; &amp;lt;&amp;lt; std::endl;
}
// 如果这里不加 override 关键字，编译器将会报错
};
int main() {
Base* b = new Derived();
b-&amp;gt;say_hello(); // 输出 &amp;#34;Hello from Derived&amp;#34;
delete b;
return 0;
}
&lt;/code>&lt;/pre>&lt;p>在这个例子中，我们在 &lt;code>Derived&lt;/code> 类中重写了 &lt;code>say_hello&lt;/code> 函数，并使用了 &lt;code>override&lt;/code> 关键字。如果我们不加 &lt;code>override&lt;/code> 关键字，编译器将会报错。&lt;/p>
&lt;h1 id="虚函数相关的运行成本">虚函数相关的运行成本&lt;/h1>
&lt;p>虚函数的使用可能会带来一些额外的运行时开销。两个&lt;/p>
&lt;ol>
&lt;li>由于每个包含虚函数的类都需要一个指向&lt;strong>虚函数表的指针&lt;/strong>，这会增加对象的大小。&lt;/li>
&lt;li>此外，在调用虚函数时，程序需要查找对象的&lt;strong>虚函数表&lt;/strong>并调用正确的函数实现，这可能会导致一些性能损失。&lt;/li>
&lt;/ol>
&lt;p>然而，这些开销通常是可以接受的，并且通过合理的设计和实现可以最小化这些开销。在大多数情况下，虚函数的使用对于代码的可维护性和可扩展性来说是非常重要的，因此我们应该仔细考虑是否需要使用虚函数。&lt;/p></description></item><item><title>CPP</title><link>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/</link><pubDate>Thu, 02 Mar 2023 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/cpp/cpp-0cfaab30bd8344c6aa29a581cb2d8ccf/</guid><description>&lt;p>C++ is a high-level, general-purpose programming language that was developed by Bjarne Stroustrup in 1983 as an extension of the C programming language. C++ provides support for object-oriented programming, generic programming, and low-level memory manipulation.&lt;/p>
&lt;p>One of the main advantages of C++ is its efficiency and performance. C++ code can be compiled into machine code, which makes it faster than other high-level programming languages. This makes it a popular choice for developing high-performance software such as video games, operating systems, and scientific simulations.&lt;/p>
&lt;p>Another advantage of C++ is its flexibility. It can be used for a wide range of applications, from desktop applications to mobile apps, and from large-scale enterprise systems to small-scale embedded systems.&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E6%8C%87%E9%92%88%209b510564ca8d4ad3b860d216573eb30b.md">C++中的指针&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%BC%95%E7%94%A8%20052dd015f0c7427e8a99bfe77b873d1b.md">C++中的引用&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84static%20eb2478cbe8134fcf9c35f28028be93c5.md">C++中的static &lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E6%9E%9A%E4%B8%BE%20c789b3b413054bb5a75e493dfff9b88e.md">C++中的枚举&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%204dfd196faf5f4b809efbd654fffb029a.md">C++中的构造函数&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0%2062fd40fc08b04b85b2fa8ca5d5e14ade.md">C++中的析构函数&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E7%BB%A7%E6%89%BF%20b70e00c1604e4b2e8a25eb4a67c1fd8e.md">C++中的继承&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E8%99%9A%E5%87%BD%E6%95%B0%20e15af8370b37486d9ff31dea7793c5b9.md">C++中的虚函数&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E6%8E%A5%E5%8F%A3%EF%BC%88%E7%BA%AF%E8%99%9A%E5%87%BD%E6%95%B0%EF%BC%89%20d02581428abd4cdead1da2c4b780467c.md">C++中的接口（纯虚函数）&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%8F%AF%E8%A7%81%E6%80%A7%200ae232d21aa34b14aacc7c41515ef775.md">C++中的可见性&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E6%95%B0%E7%BB%84%2049ee11cae6c64590ac3b32234d9905c3.md">C++中的数组&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%20c1aea693e5cc47d8a53110577af87660.md">C++中的字符串&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E9%9D%A2%E9%87%8F%200d677efe66d24fa48892844cda209126.md">C++中的字符串字面量&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84const%20f78cd58e7f3c44adac55620e8d3efa13.md">C++中的const&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84mutable%200edc2ed4eb114446ae9c96b81a74de74.md">C++中的mutable&lt;/a>&lt;/p>
&lt;p>&lt;a href="CPP%200cfaab30bd8344c6aa29a581cb2d8ccf/C&amp;#43;&amp;#43;%E4%B8%AD%E7%9A%84%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%20376fa1bb49634219b9bdc5e8a5bd830a.md">C++中的智能指针&lt;/a>&lt;/p></description></item><item><title>A fault-tolerant KV server on top of Raft</title><link>https://loloxwg.top/posts/a_fault-tolerant_kv_server_on_top_of_raft/</link><pubDate>Sat, 18 Jun 2022 19:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/a_fault-tolerant_kv_server_on_top_of_raft/</guid><description>&lt;p>分享一下前两天在 &lt;a href="https://asktug.com/t/topic/665859" target="_blank" rel="noopener">Talent Plan Community&lt;/a> 华东师范大学孙佳丽做的有关 TinyKV 的介绍&lt;/p>
&lt;p>这次分享主要从TinyKV的整体架构,组件介绍,调用流程,raft优化四个方面展开，并给出了一些有用的参考&lt;/p>
&lt;p>注：以下仅为图片，&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_01.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_02.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_03.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_04.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_05.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_06.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_07.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_08.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_09.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_10.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_11.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_12.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_13.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_14.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_15.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_16.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_17.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_18.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_19.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_20.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_21.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_22.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_23.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="A%20fault-tolerant%20KV%20server%20on%20top%20of%20Raft_24.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;h2 id="参考资料">参考资料&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://z.itpub.net/article/detail/29B4D408D967AE015AF40C2C47F7E5AE" target="_blank" rel="noopener">Etcd 之 Lease read&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://qtozeng.top/2019/01/15/etcd-raft-ReadIndex-%E7%BA%BF%E6%80%A7%E4%B8%80%E8%87%B4%E6%80%A7%E8%AF%BB%E6%BA%90%E7%A0%81%E7%AE%80%E6%9E%90/" target="_blank" rel="noopener">etcd-raft ReadIndex 线性一致性读源码简析&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://pingcap.com/zh/blog/optimizing-raft-in-tikv" target="_blank" rel="noopener">TiKV 功能介绍 - Raft 的优化 | PingCAP&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://pingcap.com/zh/blog/lease-read" target="_blank" rel="noopener">TiKV 功能介绍 - Lease Read | PingCAP&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://pingcap.com/zh/blog/tikv-source-code-reading-19" target="_blank" rel="noopener">TiKV 源码解析系列文章（十九）read index 和 local read 情景分析 | PingCAP&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://learn.pingcap.com/learner/course/510001/file/570002;offeringId=720002" target="_blank" rel="noopener">如何在 Raft 之上构建Key-Value Server&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>ClickHouse为什么这么快</title><link>https://loloxwg.top/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/</link><pubDate>Mon, 02 May 2022 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/</guid><description>&lt;p>Slide: &lt;a href="https://presentations.clickhouse.com/bdtc_2019/" target="_blank" rel="noopener">https://presentations.clickhouse.com/bdtc_2019/&lt;/a>&lt;/p>
&lt;h2 id="bottom-up自底向上的设计原则">Bottom-Up自底向上的设计原则&lt;/h2>
&lt;p>这个设计的思想就是：当你在考虑一个组件时，必须要考虑到它在底层的执行性能如何。
具体来说可以考虑这么几个问题：&lt;/p>
&lt;ol>
&lt;li>work的内层循环是怎么执行的&lt;/li>
&lt;li>内存中的数据布局是什么样的&lt;/li>
&lt;li>数据流是从哪里来的，又流向了哪里？&lt;/li>
&lt;li>我要设计GROUP By&lt;/li>
&lt;li>我要用Hash Table，并且要放到内存里&lt;/li>
&lt;li>如果HashTable太大那么没法使用L3级缓存&lt;/li>
&lt;li>如果GROUP BY的key不是在本地分配的，那么L3缓存对于每一行来说都是miss的&lt;/li>
&lt;li>L3 miss会造成70-100ns的损失&lt;/li>
&lt;li>目前ClickHouse的查询量是175million rows/s&lt;/li>
&lt;li>如果L3 miss的话只有40million rows/s&lt;/li>
&lt;/ol>
&lt;h2 id="algorithm-first-abstractions-go-after-算法优先于抽象">Algorithm First, Abstractions Go After 算法优先于抽象&lt;/h2>
&lt;p>应当根据业务，首先考虑出算法，再去考虑具体用什么样的接口去实现这个算法
&lt;strong>内置库函数为什么垃圾以及优化方法&lt;/strong>
很简单，Every problem is a landscape。
内置的库函数都是通用的，很难考虑到具体的业务。
比较常见的有&lt;/p>
&lt;ol>
&lt;li>子串查找strstr&lt;/li>
&lt;li>Sort&lt;/li>
&lt;li>HashTable&lt;/li>
&lt;/ol>
&lt;h3 id="子串查找">子串查找&lt;/h3>
&lt;p>考虑这么一种情况，有一个模式串(needle)和10000个匹配串(haystack)，如果要调用strstr的话需要&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="err">Searcher&lt;/span> &lt;span class="err">searcher(needle);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">for&lt;/span> &lt;span class="err">(const&lt;/span> &lt;span class="err">auto&lt;/span> &lt;span class="err">&amp;amp;haystack:&lt;/span> &lt;span class="err">haystacks)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="err">searcher.search(haystack)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>时间复杂度O(n * len(m))
那怎么办呢？&lt;del>当然是拿出AC自动机&lt;/del>
实际上有几十种子串查找算法可以使用 &lt;a href="https://www-igm.univ-mlv.fr/~lecroq/string" target="_blank" rel="noopener">https://www-igm.univ-mlv.fr/~lecroq/string&lt;/a>
但是ClickHouse一个都没用，而是根据needle是否为constant采用了下面几种算法
算法的具体介绍在这里 &lt;a href="https://habr.com/en/company/yandex/blog/466183/" target="_blank" rel="noopener">https://habr.com/en/company/yandex/blog/466183/&lt;/a>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="image-20220621013728023" srcset="
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013728023-5746658-5746660-5746661_hu1f3ec6513e50f057d0eb5cfe13c95990_160156_ee05aab82b8ea66c9d141ad11f42317c.webp 400w,
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013728023-5746658-5746660-5746661_hu1f3ec6513e50f057d0eb5cfe13c95990_160156_e9b2f27058c82c8708e35c947ef99b4c.webp 760w,
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013728023-5746658-5746660-5746661_hu1f3ec6513e50f057d0eb5cfe13c95990_160156_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013728023-5746658-5746660-5746661_hu1f3ec6513e50f057d0eb5cfe13c95990_160156_ee05aab82b8ea66c9d141ad11f42317c.webp"
width="760"
height="167"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;h3 id="sort排序">Sort排序&lt;/h3>
&lt;p>排序需要考虑的因素有&lt;/p>
&lt;ol>
&lt;li>array的类型，numbers/tuple/string/structure ？&lt;/li>
&lt;li>是否需要完全在内存中进行？&lt;/li>
&lt;li>是否需要多路归并？等等等等&lt;/li>
&lt;/ol>
&lt;p>ClickHouse使用了pdqsort和radix sort
但是实现一般般，需要重写&lt;/p>
&lt;h3 id="hashtable">HashTable&lt;/h3>
&lt;p>需要考虑的因素有&lt;/p>
&lt;ol>
&lt;li>hash函数的选择&lt;/li>
&lt;li>内存布局：开放地址or拉链法？&lt;/li>
&lt;li>value的大小&lt;/li>
&lt;li>是否需要支持value不能被移动？&lt;/li>
&lt;li>等等..&lt;/li>
&lt;/ol>
&lt;p>ClickHouse的实现方法是
针对不同的业务场景实现N多个Hash函数&lt;/p>
&lt;h2 id="积极采用最新的算法grab-the-best">积极采用最新的算法Grab the best！&lt;/h2>
&lt;p>积极采用业界最先进的算法
如果有性能提升就采用
否则drop it
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="image-20220621013827247" srcset="
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013827247_huf12aecf3d1a411d52c68f1d2a21d5dff_99434_727f9f1097b524aa6b61632aec7c1106.webp 400w,
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013827247_huf12aecf3d1a411d52c68f1d2a21d5dff_99434_9f3418f88592ccc9bcb0768096893c54.webp 760w,
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013827247_huf12aecf3d1a411d52c68f1d2a21d5dff_99434_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013827247_huf12aecf3d1a411d52c68f1d2a21d5dff_99434_727f9f1097b524aa6b61632aec7c1106.webp"
width="760"
height="194"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;h2 id="specialization-for-the-task-具体的问题具体处理">Specialization For the Task 具体的问题具体处理&lt;/h2>
&lt;h3 id="like查询">LIKE查询&lt;/h3>
&lt;p>以LIKE查询为例
对于
WHERE str LIKE &amp;lsquo;%hello%world!%&amp;rsquo;
来说，常见的解决方案是直接用re2匹配
但是ClickHouse会在这之前扫描一遍字符串，确保&amp;rsquo;world!&amp;lsquo;存在
而对于
WHERE str LIKE &amp;lsquo;%hello%&amp;rsquo;
则会执行substring search
对于
WHERE str LIKE &amp;lsquo;hello%&amp;rsquo;
则会执行prefix search&lt;/p>
&lt;h3 id="group-by">GROUP By&lt;/h3>
&lt;p>ClickHouse的GroupBy针对不同的数据类型，有40余种不同的HashTable实现
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="image-20220621013952264" srcset="
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013952264_huf546420f7c81e1d37bef2a2618289c75_645068_7464f7df6a1f7a41857c0a017862fac8.webp 400w,
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013952264_huf546420f7c81e1d37bef2a2618289c75_645068_a44d5873b40ab0024deab18f0813e5b4.webp 760w,
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013952264_huf546420f7c81e1d37bef2a2618289c75_645068_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621013952264_huf546420f7c81e1d37bef2a2618289c75_645068_7464f7df6a1f7a41857c0a017862fac8.webp"
width="760"
height="360"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;h3 id="quantiletiming-求中位数">QuantileTiming 求中位数&lt;/h3>
&lt;p>对于小于64个数字的数组，在arena申请内存
对于小于5670个数字的数组，在heap申请内存
对于其他的使用custom buckets&lt;/p>
&lt;h2 id="data-structures-are-always-in-context-of-the-task-数据结构需要考虑任务的上下文">Data Structures are Always in Context of the Task 数据结构需要考虑任务的上下文&lt;/h2>
&lt;p>如何选择一个数据结构？
想清楚，它需要实现什么，需要哪些，以及不需要哪些
例如std::string&lt;/p>
&lt;ol>
&lt;li>需要自己手动管理内存&lt;/li>
&lt;li>需要允许修改字符&lt;/li>
&lt;li>需要自己维护size&lt;/li>
&lt;/ol>
&lt;p>再比如，如何实现GROUP By？&lt;/p>
&lt;ol>
&lt;li>对数据排序后依次取&lt;/li>
&lt;li>Hash一下查Hash表&lt;/li>
&lt;/ol>
&lt;p>到底是用1还是2呢？
其实还是要看场景，如果数组几乎是排好序的，那么1合适，否则2合适&lt;/p>
&lt;h2 id="algorithm-know-about-data-distribution算法实现时考虑数据分布">Algorithm Know About Data Distribution算法实现时考虑数据分布&lt;/h2>
&lt;p>一个超硬核的例子。
理论上可以快12倍。
其实通过这个例子也可以看出，真实的数据应该是1/2/3位最多，在12位数以下，9位和10位最多
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="image-20220621014118592" srcset="
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621014118592_hu73e5a007f3e0a5f917d413306cf0f807_556697_c09a534a78a13719eb33fbd9c93c1739.webp 400w,
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621014118592_hu73e5a007f3e0a5f917d413306cf0f807_556697_2300f6cb9d5f041dee54537865be9e56.webp 760w,
/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621014118592_hu73e5a007f3e0a5f917d413306cf0f807_556697_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/clickhouse%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E4%B9%88%E5%BF%AB/image-20220621014118592_hu73e5a007f3e0a5f917d413306cf0f807_556697_c09a534a78a13719eb33fbd9c93c1739.webp"
width="739"
height="760"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;h2 id="multi-armed-bandit">Multi-Armed Bandit&lt;/h2>
&lt;p>Multi-Armed Bandit的来源是这样的：
这类似于赌场中的老虎机，它有几个杠杆，玩家可以拉动这些杠杆来获得一些随机数量的钱。玩家可以按任意顺序多次拉动操纵杆。每个杠杆都有一个固定的概率给出相应的金额，但玩家不知道它是如何工作的，只能从玩游戏的经验中学习。一旦他们弄清楚了，他们就可以最大化他们的奖金。 最大化奖励的一种方法是根据先前步骤的游戏统计数据评估每个步骤中每个杠杆的概率分布。然后，根据收到的分配，我们在心理上为每个杠杆“赢得”随机奖励。最后，我们拉动在我们的心理游戏中获得最佳结果的杠杆。这种方法称为汤普森抽样。
针对于CK，其应用是：对于不同的业务场景，需要选择不同的压缩算法，因此可以先对一小部分数据进行压缩，根据执行的效率并结合概率的因素，选出最终的压缩算法
具体实现看这里https://habr.com/en/company/yandex/blog/457612/&lt;/p>
&lt;h2 id="testing-on-real-data">Testing on Real Data&lt;/h2>
&lt;p>用真实数据进行测试&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;ol>
&lt;li>在设计系统时需要考虑底层的实现细节&lt;/li>
&lt;li>基于硬件容量设计&lt;/li>
&lt;li>根据任务需要决定数据结构和抽象&lt;/li>
&lt;li>对于特殊的case需要给出特殊的解法&lt;/li>
&lt;li>尝试新的，好的算法&lt;/li>
&lt;li>基于数据选择算法&lt;/li>
&lt;li>通过真实数据集进行测试&lt;/li>
&lt;li>在CI中测试数据衰减(databend好像是每天结束后会推送一次性能报告）&lt;/li>
&lt;li>测量和观察一切事物&lt;/li>
&lt;li>不断的重构代码&lt;/li>
&lt;/ol></description></item><item><title>Pingcap TalentPlan 分布式事务概述</title><link>https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/</link><pubDate>Thu, 28 Apr 2022 12:14:40 +0000</pubDate><guid>https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/</guid><description>&lt;p>分享一下前两天在 &lt;a href="https://tidb.net/talent-plan" target="_blank" rel="noopener">Talent Plan Community&lt;/a> 谭新宇做的有关分布式事务和 Distributed-Txn 代码框架的介绍。&lt;/p>
&lt;p>这次分享除了对 2021 VLDB summer school 中讲授的若干重要主题进行了概述，还着重介绍了事件排序这一很本质的问题，此外也参考了不少优质资料&lt;/p>
&lt;p>注：以下仅为图片，可以在&lt;a href="https://pingcap.feishu.cn/drive/folder/fldcn9zPuLSTqoL2JDQOT5jbpQd" target="_blank" rel="noopener">此处&lt;/a>在线浏览 PPT 原件和录屏。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/1_hu195b739fc8374e2a84b6aac63b9a0b73_131433_fb0584319323b3a634a0a4605e318c73.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/1_hu195b739fc8374e2a84b6aac63b9a0b73_131433_2c0c377ca1d4ce61b31cbdaecd1706bd.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/1_hu195b739fc8374e2a84b6aac63b9a0b73_131433_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/1_hu195b739fc8374e2a84b6aac63b9a0b73_131433_fb0584319323b3a634a0a4605e318c73.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/2_hu0d21cdbecd44533f2b51277648c1cea3_233756_f3e10c33fb667b5d78fec48f1ef19b90.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/2_hu0d21cdbecd44533f2b51277648c1cea3_233756_63ef2aa44ee80f5038f6307fdd4361d2.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/2_hu0d21cdbecd44533f2b51277648c1cea3_233756_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/2_hu0d21cdbecd44533f2b51277648c1cea3_233756_f3e10c33fb667b5d78fec48f1ef19b90.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/3_hubff2ea602d5c8319d6ea5b6cff4f41bb_76505_4ddfeaa9310f13ae7f0fa5ef8b4d5c98.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/3_hubff2ea602d5c8319d6ea5b6cff4f41bb_76505_1689d7b7d0676f3b80ca1908344e8759.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/3_hubff2ea602d5c8319d6ea5b6cff4f41bb_76505_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/3_hubff2ea602d5c8319d6ea5b6cff4f41bb_76505_4ddfeaa9310f13ae7f0fa5ef8b4d5c98.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/4_hu2cfabb6f52d8b9be176b27e4dd9448b8_344711_0d781c7a2ef843f1508709c90b21b6b4.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/4_hu2cfabb6f52d8b9be176b27e4dd9448b8_344711_ea4af3b56878c2c0fc7fe1caaf3c4fca.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/4_hu2cfabb6f52d8b9be176b27e4dd9448b8_344711_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/4_hu2cfabb6f52d8b9be176b27e4dd9448b8_344711_0d781c7a2ef843f1508709c90b21b6b4.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/5_hu46af6948e3151f3062df02f26112ea21_326525_70fa0a6fbcb59d718afce12d420df2e5.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/5_hu46af6948e3151f3062df02f26112ea21_326525_6eeda241b3993d0d9e5f5ad8b900f847.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/5_hu46af6948e3151f3062df02f26112ea21_326525_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/5_hu46af6948e3151f3062df02f26112ea21_326525_70fa0a6fbcb59d718afce12d420df2e5.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/6_hu1d804c4e4d4fafc7f6f40012a89e6ac1_380628_bc0e44e905529cf29475c7382e98366c.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/6_hu1d804c4e4d4fafc7f6f40012a89e6ac1_380628_db1a35993030b112d8fb037ecd5622f2.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/6_hu1d804c4e4d4fafc7f6f40012a89e6ac1_380628_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/6_hu1d804c4e4d4fafc7f6f40012a89e6ac1_380628_bc0e44e905529cf29475c7382e98366c.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/7_hua25ec52492e5c681fedec609a2ea6197_656803_370b36bc5f573846c64ef990abc82f9f.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/7_hua25ec52492e5c681fedec609a2ea6197_656803_d716f64719c232ffbfa7984777f7d23c.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/7_hua25ec52492e5c681fedec609a2ea6197_656803_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/7_hua25ec52492e5c681fedec609a2ea6197_656803_370b36bc5f573846c64ef990abc82f9f.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/8_hu34d684af600d93927918e54556a3954c_823843_198a33939ef90c320f26d8ea6e35c9b4.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/8_hu34d684af600d93927918e54556a3954c_823843_17b6abba27bc5e1a7a78737405869c6b.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/8_hu34d684af600d93927918e54556a3954c_823843_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/8_hu34d684af600d93927918e54556a3954c_823843_198a33939ef90c320f26d8ea6e35c9b4.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/9_hu34d684af600d93927918e54556a3954c_549056_2c74334a3c88c1d69c31cf6d0c8c9cc8.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/9_hu34d684af600d93927918e54556a3954c_549056_ae546b46587bae151a0595d801ca41bf.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/9_hu34d684af600d93927918e54556a3954c_549056_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/9_hu34d684af600d93927918e54556a3954c_549056_2c74334a3c88c1d69c31cf6d0c8c9cc8.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/10_hu03e2e45b0de402212cdce995892978c9_307246_be27029841e7eaad328eb59697289c1a.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/10_hu03e2e45b0de402212cdce995892978c9_307246_1aa40b915b0938c2d992c14726251d80.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/10_hu03e2e45b0de402212cdce995892978c9_307246_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/10_hu03e2e45b0de402212cdce995892978c9_307246_be27029841e7eaad328eb59697289c1a.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/11_huad96ec4abb4af3cea264ad839eb73acf_349765_bf3dcfdb748830927c5651ebb208c939.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/11_huad96ec4abb4af3cea264ad839eb73acf_349765_5e5741eb41140bcac0789e42723312f9.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/11_huad96ec4abb4af3cea264ad839eb73acf_349765_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/11_huad96ec4abb4af3cea264ad839eb73acf_349765_bf3dcfdb748830927c5651ebb208c939.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/12_hu9399c11705d20fd2cafad21758a51c37_280832_8dccb5156cf6a35fa35eb47dc625042b.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/12_hu9399c11705d20fd2cafad21758a51c37_280832_19f1baaf08238695f67769464bff6b90.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/12_hu9399c11705d20fd2cafad21758a51c37_280832_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/12_hu9399c11705d20fd2cafad21758a51c37_280832_8dccb5156cf6a35fa35eb47dc625042b.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/13_hua5aa59f91dbeaedbe27985df41aee35c_398685_292898c3a668e1884e7b9fb30058d961.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/13_hua5aa59f91dbeaedbe27985df41aee35c_398685_90c238db691d31796380910e1dce8d09.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/13_hua5aa59f91dbeaedbe27985df41aee35c_398685_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/13_hua5aa59f91dbeaedbe27985df41aee35c_398685_292898c3a668e1884e7b9fb30058d961.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/14_hu3c43a309c98cada278fd22c1b86bde42_328597_920a1acd4005f9c8ff128a0d84ff0a1a.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/14_hu3c43a309c98cada278fd22c1b86bde42_328597_78ecc104339c6de6d8f142d4eddb35b7.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/14_hu3c43a309c98cada278fd22c1b86bde42_328597_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/14_hu3c43a309c98cada278fd22c1b86bde42_328597_920a1acd4005f9c8ff128a0d84ff0a1a.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/15_hu064e056f5b41a037c182c88066b5c921_242929_be2176a5419dd839c0516ead5085d052.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/15_hu064e056f5b41a037c182c88066b5c921_242929_7e28d55ff0d314309f70aee71d7fd647.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/15_hu064e056f5b41a037c182c88066b5c921_242929_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/15_hu064e056f5b41a037c182c88066b5c921_242929_be2176a5419dd839c0516ead5085d052.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/16_hu484c78abb2d79953016dcb64f4f9bacd_492108_d823bcc5ea9af3d94049179e3daa7f86.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/16_hu484c78abb2d79953016dcb64f4f9bacd_492108_39fd6aac36284090ec37c88ad8b67103.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/16_hu484c78abb2d79953016dcb64f4f9bacd_492108_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/16_hu484c78abb2d79953016dcb64f4f9bacd_492108_d823bcc5ea9af3d94049179e3daa7f86.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/17_hu4a95f58099fa21bad4c9d7284312d4a3_742902_751d860c9bb2dece2026fac07a5c2587.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/17_hu4a95f58099fa21bad4c9d7284312d4a3_742902_e9d98b05312ef04b8c112623ad73774b.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/17_hu4a95f58099fa21bad4c9d7284312d4a3_742902_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/17_hu4a95f58099fa21bad4c9d7284312d4a3_742902_751d860c9bb2dece2026fac07a5c2587.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/18_hua97ad5c1e772c4d13503c0935dd2e68c_285494_e2c96f4e0c3818f72d61a8f799da2912.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/18_hua97ad5c1e772c4d13503c0935dd2e68c_285494_34640653a4d8484908a143adba0076ec.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/18_hua97ad5c1e772c4d13503c0935dd2e68c_285494_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/18_hua97ad5c1e772c4d13503c0935dd2e68c_285494_e2c96f4e0c3818f72d61a8f799da2912.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/19_hu63236887e80aaf3db8cdc3f5da547b79_209530_fb6dafd401e1e0f134606f42d3086eed.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/19_hu63236887e80aaf3db8cdc3f5da547b79_209530_2ca2a0e317c6336bb2cdfb7e7d12165d.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/19_hu63236887e80aaf3db8cdc3f5da547b79_209530_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/19_hu63236887e80aaf3db8cdc3f5da547b79_209530_fb6dafd401e1e0f134606f42d3086eed.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/20_hu63236887e80aaf3db8cdc3f5da547b79_250790_e3f801765f8049c685de68a89123e28b.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/20_hu63236887e80aaf3db8cdc3f5da547b79_250790_060f758371dc0ebe19e178f58e8731bd.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/20_hu63236887e80aaf3db8cdc3f5da547b79_250790_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/20_hu63236887e80aaf3db8cdc3f5da547b79_250790_e3f801765f8049c685de68a89123e28b.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/21_hua974b6a1f72f329cc6d6dd5386a4c949_630727_cb8e6c9255b3da04e41bd34b4005bf20.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/21_hua974b6a1f72f329cc6d6dd5386a4c949_630727_78db420d49427c91c7a3a4fd56488310.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/21_hua974b6a1f72f329cc6d6dd5386a4c949_630727_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/21_hua974b6a1f72f329cc6d6dd5386a4c949_630727_cb8e6c9255b3da04e41bd34b4005bf20.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/22_hu156440095f5c7f67213e8b6f8b78c8fe_340858_d06720e2c77bc8b7dccbf13e2b461202.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/22_hu156440095f5c7f67213e8b6f8b78c8fe_340858_5bdc1242e2751e8e45193c42085a954a.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/22_hu156440095f5c7f67213e8b6f8b78c8fe_340858_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/22_hu156440095f5c7f67213e8b6f8b78c8fe_340858_d06720e2c77bc8b7dccbf13e2b461202.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/23_hu78c00a4ea17abf0faa1e8690697113d3_266942_f28b6de5f4929fe4866905c6d7de7d23.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/23_hu78c00a4ea17abf0faa1e8690697113d3_266942_2a4eb5be00ac2dcf7edd75c6349c1672.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/23_hu78c00a4ea17abf0faa1e8690697113d3_266942_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/23_hu78c00a4ea17abf0faa1e8690697113d3_266942_f28b6de5f4929fe4866905c6d7de7d23.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/24_hu5bf42364b5f41021aaf0dcfc112685da_330357_40a77bdcc150fe0d34c9e0269800bddf.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/24_hu5bf42364b5f41021aaf0dcfc112685da_330357_207dcad366cedf381e62ec0d936a3ee8.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/24_hu5bf42364b5f41021aaf0dcfc112685da_330357_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/24_hu5bf42364b5f41021aaf0dcfc112685da_330357_40a77bdcc150fe0d34c9e0269800bddf.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/25_hubff2ea602d5c8319d6ea5b6cff4f41bb_84690_d489f788cff72bed347a4c57aa38dbaf.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/25_hubff2ea602d5c8319d6ea5b6cff4f41bb_84690_27a427756a7a85fec69fadbbd3f109ea.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/25_hubff2ea602d5c8319d6ea5b6cff4f41bb_84690_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/25_hubff2ea602d5c8319d6ea5b6cff4f41bb_84690_d489f788cff72bed347a4c57aa38dbaf.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/26_hu8ff1fb647ddef5db9f4b1933f160e41e_287860_1df0b553b6973583d19b6adde56e91eb.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/26_hu8ff1fb647ddef5db9f4b1933f160e41e_287860_fdd449bf6b2da9e46af818d83c26fbfa.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/26_hu8ff1fb647ddef5db9f4b1933f160e41e_287860_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/26_hu8ff1fb647ddef5db9f4b1933f160e41e_287860_1df0b553b6973583d19b6adde56e91eb.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/27_hu886acf51ce00ab502b416194b2aadc2b_257010_7e2ebe6995053e566461910e4722c10a.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/27_hu886acf51ce00ab502b416194b2aadc2b_257010_2331090b104ae09170daf8517d5a78cb.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/27_hu886acf51ce00ab502b416194b2aadc2b_257010_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/27_hu886acf51ce00ab502b416194b2aadc2b_257010_7e2ebe6995053e566461910e4722c10a.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/28_hua85a31bb887e2b397e19ea8183932429_415060_e0f83e69cd7183fdcb4b097ef911410d.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/28_hua85a31bb887e2b397e19ea8183932429_415060_1c3ed1c3a5514143ac68a3590112b569.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/28_hua85a31bb887e2b397e19ea8183932429_415060_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/28_hua85a31bb887e2b397e19ea8183932429_415060_e0f83e69cd7183fdcb4b097ef911410d.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/29_hu9d5e1633759363194017fa8389682e18_786984_a65ec2a836f46830d3be3d9474ec17a6.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/29_hu9d5e1633759363194017fa8389682e18_786984_dfe2bda66b78e6ba9798953bf6c9d440.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/29_hu9d5e1633759363194017fa8389682e18_786984_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/29_hu9d5e1633759363194017fa8389682e18_786984_a65ec2a836f46830d3be3d9474ec17a6.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/30_hu22c4581bd1e8555a5534c576cd13b0e2_1597229_23c332c0c23badf50bcc0578f73af9ce.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/30_hu22c4581bd1e8555a5534c576cd13b0e2_1597229_f97e878264a3a03ec83f500aa371ff8e.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/30_hu22c4581bd1e8555a5534c576cd13b0e2_1597229_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/30_hu22c4581bd1e8555a5534c576cd13b0e2_1597229_23c332c0c23badf50bcc0578f73af9ce.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/31_hu0e4605e9577f20fd175ba71312da6293_240661_57aecae162121f9cdbdeefbc848e7ebe.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/31_hu0e4605e9577f20fd175ba71312da6293_240661_60e7bd11152507ee7b03c9e9d51c5b9d.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/31_hu0e4605e9577f20fd175ba71312da6293_240661_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/31_hu0e4605e9577f20fd175ba71312da6293_240661_57aecae162121f9cdbdeefbc848e7ebe.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/32_hu4bb9e52a2edd3f5f9d6b850b08182821_69234_cd588e542e18e1b2a50a6711f9dd8818.webp 400w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/32_hu4bb9e52a2edd3f5f9d6b850b08182821_69234_f6c6fdf98410ae8227adc7aff41cb3ce.webp 760w,
/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/32_hu4bb9e52a2edd3f5f9d6b850b08182821_69234_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%A6%82%E8%BF%B0/32_hu4bb9e52a2edd3f5f9d6b850b08182821_69234_cd588e542e18e1b2a50a6711f9dd8818.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p></description></item><item><title>共识论文总结Paxos Raft and etc 资料和笔记总结</title><link>https://loloxwg.top/posts/%E5%85%B1%E8%AF%86%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93paxos-raft-and-etc-%E8%B5%84%E6%96%99%E5%92%8C%E7%AC%94%E8%AE%B0%E6%80%BB%E7%BB%93/</link><pubDate>Wed, 02 Feb 2022 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/%E5%85%B1%E8%AF%86%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93paxos-raft-and-etc-%E8%B5%84%E6%96%99%E5%92%8C%E7%AC%94%E8%AE%B0%E6%80%BB%E7%BB%93/</guid><description>&lt;h2 id="一共识">一、共识&lt;/h2>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/%E5%85%B1%E8%AF%86%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93paxos-raft-and-etc-%E8%B5%84%E6%96%99%E5%92%8C%E7%AC%94%E8%AE%B0%E6%80%BB%E7%BB%93/featured_huf66b6f22c477385c7bad973c53b12342_458587_8a71d3ab6c84619cb65d1f5af6d46e51.webp 400w,
/posts/%E5%85%B1%E8%AF%86%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93paxos-raft-and-etc-%E8%B5%84%E6%96%99%E5%92%8C%E7%AC%94%E8%AE%B0%E6%80%BB%E7%BB%93/featured_huf66b6f22c477385c7bad973c53b12342_458587_84be5ab18c5d549ef1fc654132bb65d9.webp 760w,
/posts/%E5%85%B1%E8%AF%86%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93paxos-raft-and-etc-%E8%B5%84%E6%96%99%E5%92%8C%E7%AC%94%E8%AE%B0%E6%80%BB%E7%BB%93/featured_huf66b6f22c477385c7bad973c53b12342_458587_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/%E5%85%B1%E8%AF%86%E8%AE%BA%E6%96%87%E6%80%BB%E7%BB%93paxos-raft-and-etc-%E8%B5%84%E6%96%99%E5%92%8C%E7%AC%94%E8%AE%B0%E6%80%BB%E7%BB%93/featured_huf66b6f22c477385c7bad973c53b12342_458587_8a71d3ab6c84619cb65d1f5af6d46e51.webp"
width="760"
height="261"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>分布式系统中的一致性实在是太杂糅了，有共识、隔离级别等等，这里只关注共识，尤其是raft、paxos等各种算法。&lt;/p>
&lt;p>raft、paxos等各种变形算法，来自On the Parallels between Paxos and Raft, and how to Port Optimizations&lt;/p>
&lt;h2 id="二需要了解的先验知识">二、需要了解的先验知识：&lt;/h2>
&lt;h3 id="1论文">1.论文&lt;/h3>
&lt;p>Raft-In Search of an Understandable Consensus Algorithm(Extended Version)阅读笔记 - 不想说的文章 - 知乎 &lt;a href="https://zhuanlan.zhihu.com/p/248136033" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/248136033&lt;/a>&lt;/p>
&lt;p>PAXOS Paxos Made Simp阅读笔记 - 不想说的文章 - 知乎 &lt;a href="https://zhuanlan.zhihu.com/p/258929773" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/258929773&lt;/a>&lt;/p>
&lt;p>Raft-Paxos关系对应 On the Parallels between Paxos and Raft, and how to Port Optimizations阅读笔记 - 不想说的文章 - 知乎 &lt;a href="https://zhuanlan.zhihu.com/p/453400329" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/453400329&lt;/a>&lt;/p>
&lt;p>PBFT Practical Byzantine Fault Tolerance阅读笔记 - 不想说的文章 - 知乎 &lt;a href="https://zhuanlan.zhihu.com/p/465031927" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/465031927&lt;/a>&lt;/p>
&lt;p>Byzantizing Paxos by Refinement阅读笔记 - 不想说的文章 - 知乎 &lt;a href="https://zhuanlan.zhihu.com/p/295250296" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/295250296&lt;/a>&lt;/p>
&lt;p>略学The Part-Time Parliament阅读笔记 - 不想说的文章 - 知乎 &lt;a href="https://zhuanlan.zhihu.com/p/465763532" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/465763532&lt;/a> （本文应该早点看，可惜顺序在这里，应该是第二篇，在paxos made simple前）&lt;/p>
&lt;h3 id="2形式化验证">2.形式化验证&lt;/h3>
&lt;p>COQ、dafny、TLA+&lt;/p>
&lt;p>关于TLA+的入门请参照 里面有&lt;/p>
&lt;h3 id="3未来计划">3.未来计划：&lt;/h3>
&lt;p>更新epaxos、Mecius、FastPaxos、Paxos Quorum Lease、Byzantine Paxos等&lt;/p>
&lt;p>&lt;strong>For Epaxos（资料先行整理，当结束了就会放入&lt;em>二、1.论文&lt;/em>中）&lt;/strong>&lt;/p>
&lt;p>&lt;a href="https://zhuanlan.zhihu.com/p/269388025" target="_blank" rel="noopener">祥光：EPaxos三部曲之一：EPaxos基本概念与直观理解&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://zhuanlan.zhihu.com/p/387468959" target="_blank" rel="noopener">祥光：EPaxos三部曲之二：EPaxos核心协议流程&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://www.zhihu.com/search?type=content&amp;amp;q=%E5%BC%BA%E8%BF%9E%E9%80%9A%E5%88%86%E9%87%8F" target="_blank" rel="noopener">强连通分量 - 搜索结果 - 知乎&lt;/a>（epaxos前序知识）&lt;/p>
&lt;p>epaxos详解 - 北侠的文章 - 知乎 &lt;a href="https://zhuanlan.zhihu.com/p/35562236" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/35562236&lt;/a>&lt;/p>
&lt;p>下面是一个超级强的学长的推荐，一定要看！&lt;/p>
&lt;p>&lt;a href="https://www.zhihu.com/people/d8600e722afa7f33d8ff51d156f9aeb3" target="_blank" rel="noopener">@我做分布式数据库&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://link.zhihu.com/?target=https%3A//escholarship.org/uc/item/9w79h2jg" target="_blank" rel="noopener">SoK: A Generalized Multi-Leader State Machine Replication Tutorial&lt;/a>。Michael Whittaker 论文推荐都读一读&lt;/p>
&lt;p>&lt;a href="https://zhuanlan.zhihu.com/p/380622806" target="_blank" rel="noopener">drdr xp：Multi-Master-Paxos: 3&lt;/a> 一个实现toy 很强大&lt;/p>
&lt;h2 id="三学习实现待续-估计是持续超过一年的项目">三、学习实现（待续 估计是持续超过一年的项目）&lt;/h2>
&lt;p>纸上得来终觉浅，绝知此事要躬行，比如自己实现一个toy 分布式数据库with raft or paxos&lt;/p>
&lt;h2 id="未来">未来&lt;/h2>
&lt;p>会持续更新，最后可能会得出一个结论&lt;/p></description></item><item><title>IOT 规则引擎介绍与实现</title><link>https://loloxwg.top/posts/iot%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E/</link><pubDate>Sun, 02 Jan 2022 12:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/iot%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E/</guid><description>&lt;h2 id="什么是规则引擎">&lt;strong>什么是规则引擎&lt;/strong>&lt;/h2>
&lt;p>规则引擎起源于基于规则的专家系统，而基于规则的专家系统又是专家系统的其中一个分支。专家系统属于人工智能的范畴，它模仿人类的推理方式，使用试探性的方法进行推理，并使用人类能理解的术语解释和证明它的推理结论。&lt;/p>
&lt;p>利用它就可以在应用系统中分离商业决策者的商业决策逻辑和应用开发者的技术决策，并把这些商业决策放在中心数据库或其他统一的地方，让它们能在运行时可以动态地管理和修改，从而为企业保持灵活性和竞争力提供有效的技术支持。&lt;/p>
&lt;p>在需求里面我们往往把约束，完整性，校验，分支流等都可以算到业务规则里面。在规则引擎里面谈的业务规则重点是谈当满足什么样的条件的时候，需要执行什么样的操作。因此一个完整的业务规则包括了条件和触发操作两部分内容。而引擎是事物内部的重要的运行机制，规则引擎即重点是解决规则如何描述，如何执行，如何监控等一系列问题。&lt;/p>
&lt;p>规则引擎由推理引擎发展而来，是一种嵌入在应用程序中的组件，实现了将业务决策从应用程序代码中分离出来，并使用预定义的语义模块编写业务决策。接受数据输入，解释业务规则，并根据业务规则做出业务决策。&lt;/p>
&lt;h2 id="为什么要使用规则引擎">&lt;strong>为什么要使用规则引擎&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>规则可以很容易地解决困难的问题，并得到解决方案的验证。与代码不同，规则以较不复杂的语言编写; 业务分析师可以轻松阅读和验证一套规则。&lt;/li>
&lt;li>写入Drools的Rete OO算法已经是一个成熟的算法。在Drools的帮助下，您的应用程序变得非常可扩展。如果频繁更改请求，可以添加新规则，而无需修改现有规则。&lt;/li>
&lt;li>在物联网系统中，当设备基于&lt;a href="https://link.zhihu.com/?target=https%3A//www.alibabacloud.com/help/zh/doc-detail/73732.htm%23concept-ogz-vnl-vdb" target="_blank" rel="noopener">Topic&lt;/a>进行通信时，您可以使用规则引擎，编写SQL对Topic中的数据进行处理，并配置转发规则将处理后的数据转发到其他服务，非常方便的对设备的数据进行保存和分析。&lt;/li>
&lt;/ul>
&lt;h2 id="业界规则引擎方案">&lt;strong>业界规则引擎方案&lt;/strong>&lt;/h2>
&lt;p>java开源的规则引擎有：Drools、Easy Rules、Mandarax、IBM ILOG。使用最为广泛并且开源的是Drools。&lt;/p>
&lt;h2 id="规则引擎在物联网中的使用">&lt;strong>规则引擎在物联网中的使用&lt;/strong>&lt;/h2>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://pic4.zhimg.com/v2-21ecdc4916aa94bc189d39482fc31abf_r.jpg" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>如上图为阿里云物联网架构图，其中规则引擎把数据转发到RDS/OSS/OTS 等其他服务&lt;/p>
&lt;h2 id="规则引擎的简单开发示例">&lt;strong>规则引擎的简单开发示例&lt;/strong>&lt;/h2>
&lt;p>本示例采用 drools 开源方案，从 kafka 消费数据，转发到其他服务，目前实现的是从 kafka 转发到 kafka，要扩展到其他服务也很简单。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>创建规则引擎模板&lt;/p>
&lt;p>template header&lt;/p>
&lt;p>rule
eventType&lt;/p>
&lt;p>package com.netease.push;&lt;/p>
&lt;p>global com.netease.push.destination.DestinationManager destinationManager;
global com.netease.push.rule.Action action;&lt;/p>
&lt;p>template &amp;ldquo;filter&amp;rdquo;&lt;/p>
&lt;p>rule &amp;ldquo;filter_@{row.rowNumber}&amp;rdquo;
when
m: @{eventType}(@{rule})
then
m.setAction(action);
destinationManager.processMessage(m);
end&lt;/p>
&lt;p>end template&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>相关规则引擎语法请参见： &lt;a href="https://link.zhihu.com/?target=https%3A//docs.jboss.org/drools/release/6.5.0.Final/drools-docs/html_single/%23d0e6464" target="_blank" rel="noopener">https://docs.jboss.org/drools/release/6.5.0.Final/drools-docs/html_single/#d0e6464&lt;/a>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>解析 sql 语句，动态生成规则引擎&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">update&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">RuleMetadata&lt;/span> &lt;span class="n">ruleMetadata&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">String&lt;/span> &lt;span class="n">sql&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ruleMetadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getSql&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// first parse sql
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">try&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Statement&lt;/span> &lt;span class="n">statement&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CCJSqlParserUtil&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">parse&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">sql&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Select&lt;/span> &lt;span class="n">selectStatement&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="n">Select&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="n">statement&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TablesNamesFinder&lt;/span> &lt;span class="n">tablesNamesFinder&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">TablesNamesFinder&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">List&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">tableList&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tablesNamesFinder&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getTableList&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">selectStatement&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// just use the first table name which is kafka topic
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">topic&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tableList&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">0&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Rule&lt;/span> &lt;span class="n">topicRule&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Rule&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Condition&lt;/span> &lt;span class="n">topicCondition&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Condition&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">topicCondition&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">setField&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;topic&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">topicCondition&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">setOperator&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Condition&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">Operator&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">EQUAL_TO&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">topicCondition&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">setValue&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">topic&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// In reality, you would have multiple rules for different types of events.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// The eventType property would be used to find rules relevant to the event
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">topicRule&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">setEventType&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Rule&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">eventType&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">ORDER&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">topicRule&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">setConditions&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Arrays&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">asList&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">topicCondition&lt;/span>&lt;span class="o">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">drl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">applyRuleTemplate&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="n">OnlineStatus&lt;/span>&lt;span class="o">(),&lt;/span> &lt;span class="n">topicRule&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">printStackTrace&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="n">JSQLParserException&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">e&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">printStackTrace&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// second dynamic generate rule
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">KieServices&lt;/span> &lt;span class="n">kieServices&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">KieServices&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">Factory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">KieFileSystem&lt;/span> &lt;span class="n">kieFileSystem&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">kieServices&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">newKieFileSystem&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">kieFileSystem&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">write&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;src/main/resources/rule.drl&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">drl&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">kieServices&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">newKieBuilder&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">kieFileSystem&lt;/span>&lt;span class="o">).&lt;/span>&lt;span class="na">buildAll&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">KieContainer&lt;/span> &lt;span class="n">kieContainer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">kieServices&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">newKieContainer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">kieServices&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getRepository&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">getDefaultReleaseId&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">statelessKieSession&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">kieContainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getKieBase&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">newStatelessKieSession&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">statelessKieSession&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getGlobals&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">set&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;destinationManager&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">destinationManager&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">statelessKieSession&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getGlobals&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">set&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;action&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">ruleMetadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getAction&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="nf">applyRuleTemplate&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Event&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">Rule&lt;/span> &lt;span class="n">rule&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="kd">throws&lt;/span> &lt;span class="n">Exception&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Map&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">Object&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">HashMap&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">Object&lt;/span>&lt;span class="o">&amp;gt;();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ObjectDataCompiler&lt;/span> &lt;span class="n">objectDataCompiler&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">ObjectDataCompiler&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;rule&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">rule&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;eventType&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">event&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getClass&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">getName&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">objectDataCompiler&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">compile&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Arrays&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">asList&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="o">),&lt;/span> &lt;span class="n">Thread&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">currentThread&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">getContextClassLoader&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">getResourceAsStream&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;rule-template.drl&amp;#34;&lt;/span>&lt;span class="o">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>这里使用的 sqlparser 是如下库文件:&lt;/p>
&lt;pre>&lt;code>&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;com.github.jsqlparser&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;jsqlparser&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;1.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code>&lt;/pre>
&lt;p>当前只做了简单的数据库解析，只使用了 table name；&lt;/p>
&lt;ul>
&lt;li>应用规则引擎&lt;/li>
&lt;/ul>
&lt;p>从 kafka 消费到数据后，应用规则引擎，转发到 kafka&lt;/p>
&lt;pre>&lt;code>public void evaluate(Event event) {
statelessKieSession.execute(event);
}
&lt;/code>&lt;/pre>
&lt;p>至此一个简单的规则引擎开发完成，细节请参见：&lt;a href="https://link.zhihu.com/?target=https%3A//github.com/tian-yuan/RuleEngin" target="_blank" rel="noopener">https://github.com/tian-yuan/RuleEngin&lt;/a>&lt;/p></description></item><item><title>Raft 论文阅读</title><link>https://loloxwg.top/posts/rarf%E7%AE%97%E6%B3%95%E4%BB%8B%E7%BB%8D/</link><pubDate>Wed, 10 Feb 2021 01:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/rarf%E7%AE%97%E6%B3%95%E4%BB%8B%E7%BB%8D/</guid><description>&lt;h2 id="背景">背景&lt;/h2>
&lt;h3 id="共识算法">共识算法&lt;/h3>
&lt;p>共识算法允许一组节点像一个整体一样一起工作，即使其中一些节点出现故障也能够继续工作下去，其正确性主要源于复制状态机的性质：&lt;/p>
&lt;blockquote>
&lt;p>任何初始状态一样的状态机，如果执行的命令序列一样，则最终达到的状态也一样。如果将此特性应用在多参与者进行协商共识上，可以理解为系统中存在多个具有完全相同的状态机（参与者），这些状态机能最终保持一致的关键就是起始状态完全一致和执行命令序列完全一致。&lt;/p>
&lt;/blockquote>
&lt;p>共识算法常被用来确保每一个节点上的状态机一定都会按相同的顺序执行相同的命令， 并且最终会处于相同的状态。换句话说，可以理解为共识算法就是用来确保每个节点上的日志顺序都是一致的。（不过需要注意的是，只确保“提交给状态机的日志”顺序是一致的，而有些日志项可能只是暂时添加，尚未决定要提交给状态机）。正因为如此，共识算法在构建可容错的大规模分布式系统中扮演着重要的角色。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://tanxinyu.work/raft/rsm.png#crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;id=I8l2d&amp;amp;originHeight=616&amp;amp;originWidth=810&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>上图就是每个节点的状态机，日志模块，共识模块与客户端交互的过程。&lt;/p>
&lt;p>当然，实际使用系统中的共识算法一般满足以下特性：&lt;/p>
&lt;ul>
&lt;li>在非拜占庭条件下保证共识的一致性。（非拜占庭条件，指的就是每一个节点都是诚实可信的，每一次信息的传递都是真实的且符合协议要求的，当节点无法满足协议所要求的条件时，就停止服务，节点仅会因为网络延迟或崩溃出现不一致，而不会有节点传递错误的数据或故意捏造假数据。）&lt;/li>
&lt;li>在多数节点存活时，保持可用性。（“多数”永远指的是配置文件中所有节点的多数，而不是存活节点的多数。）&lt;/li>
&lt;li>不依赖于时间，错误的时钟和高延迟只会导致可用性问题，而不会导致一致性问题。&lt;/li>
&lt;li>在多数节点一致后就返回结果，而不会受到个别慢节点的影响。&lt;/li>
&lt;/ul>
&lt;h3 id="raft-的由来与宗旨">Raft 的由来与宗旨&lt;/h3>
&lt;p>众所周知，Paxos 是一个非常划时代的共识算法。在 Raft 出现之前的 10 年里，Paxos 几乎统治着共识算法这一领域：因为绝大多数共识算法的实现都是基于 Paxos 或者受其影响，同时 Paxos 也成为了教学领域里讲解共识问题时的示例。&lt;/p>
&lt;p>但是不幸的是，尽管有很多工作都在尝试降低 Paxos 的复杂性，但是它依然十分难以理解。并且，Paxos 自身的算法结构需要进行大幅的修改才能够应用到实际的系统中。这些都导致了工业界和学术界都对 Paxos 算法感到十分头疼。比如 &lt;code>Google Chubby&lt;/code> 的论文就提到，因为 Paxos 的描述和现实差距太大，所以最终人们总会实现一套未经证实的类 Paxos 协议。&lt;/p>
&lt;p>基于以上背景，&lt;code>Diego Ongaro&lt;/code> 在就读博士期间，深入研究 Paxos 协议后提出了 Raft 协议，旨在提供更为易于理解的共识算法。Raft 的宗旨在于可实践性和可理解性，并且相比 Paxos 几乎没有牺牲多少性能。&lt;/p>
&lt;blockquote>
&lt;p>趣闻：[Raft 名字的来源](&lt;a href="https://groups.google.com/forum/" target="_blank" rel="noopener">https://groups.google.com/forum/&lt;/a> target=)。简而言之，其名字即来自于 &lt;code>R{eliable|plicated|dundant} And Fault-Tolerant&lt;/code>， 也来自于这是一艘可以帮助你逃离 Paxos 小岛的救生筏（Raft）。&lt;/p>
&lt;/blockquote>
&lt;h3 id="工业界的实现">工业界的实现&lt;/h3>
&lt;ul>
&lt;li>&lt;code>tikv&lt;/code>&lt;/li>
&lt;li>&lt;code>consul&lt;/code>&lt;/li>
&lt;li>&lt;code>etcd&lt;/code>&lt;/li>
&lt;li>&lt;code>sofajraft&lt;/code>&lt;/li>
&lt;li>…&lt;/li>
&lt;/ul>
&lt;h2 id="概述">概述&lt;/h2>
&lt;p>这一部分会简单介绍 Raft 的一些基本概念。若暂时没看懂并没有关系，后面会一一介绍清楚，带着问题耐心读完此博客即可。&lt;/p>
&lt;h3 id="子问题">子问题&lt;/h3>
&lt;p>Raft 将共识算法这个难解决的问题分解成了多个易解决，相对独立的子问题，这些问题都会在接下来的章节中进行介绍。&lt;/p>
&lt;ul>
&lt;li>&lt;code>Leader election&lt;/code>：选出集群的 leader 来统筹全局。&lt;/li>
&lt;li>&lt;code>Log replication&lt;/code>：leader 负责从客户端接收请求，并且在集群中扩散同步。&lt;/li>
&lt;li>&lt;code>Safety&lt;/code>：各节点间状态机的一致性保证。&lt;/li>
&lt;/ul>
&lt;p>在博士论文和实际生产系统中，还有更多可以探讨的模块或细节功能：&lt;/p>
&lt;ul>
&lt;li>&lt;code>Log compaction&lt;/code>：压缩日志以节约磁盘空间；加速重启后节点恢复速率；加速新节点 catch up 速率。&lt;/li>
&lt;li>&lt;code>Leader transfer&lt;/code>：能够将 leader 禅让另一个 follower，便于平滑的负载均衡。&lt;/li>
&lt;li>&lt;code>Pre vote&lt;/code>：在竞选开始时先进行一轮预备竞选，若被允许再转变为 candidate，这样有助于防止某些异常节点扰乱整个集群的正常工作。&lt;/li>
&lt;li>&lt;code>Membership change&lt;/code>：集群动态增删节点。&lt;/li>
&lt;li>&lt;code>Client interaction&lt;/code>：客户端交互。&lt;/li>
&lt;li>&lt;code>Linearizable read&lt;/code>：线性一致性读。&lt;/li>
&lt;li>&lt;code>Optimization&lt;/code>：业界常见优化。&lt;/li>
&lt;li>…&lt;/li>
&lt;/ul>
&lt;h3 id="节点类型">节点类型&lt;/h3>
&lt;p>Raft 将所有节点分为三个身份：&lt;/p>
&lt;ul>
&lt;li>&lt;code>Leader&lt;/code>：集群内最多只会有一个 leader，负责发起心跳，响应客户端，创建日志，同步日志。&lt;/li>
&lt;li>&lt;code>Candidate&lt;/code>：leader 选举过程中的临时角色，由 follower 转化而来，发起投票参与竞选。&lt;/li>
&lt;li>&lt;code>Follower&lt;/code>：接受 leader 的心跳和日志同步数据，投票给 candidate。&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://tanxinyu.work/raft/state.png#crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;id=XjD4z&amp;amp;originHeight=508&amp;amp;originWidth=804&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>上图可以看出 Raft 中节点状态之间变迁的条件。&lt;/p>
&lt;p>在博士论文和实际生产系统中，其实又增加了两种身份：&lt;/p>
&lt;ul>
&lt;li>&lt;code>Learner&lt;/code>：不具有选举权，参与日志复制过程但不计数的节点。可以作为新节点加入集群时的过渡状态以提升可用性，也可以作为一种类似于 binlog 的对 leader 日志流进行订阅的角色，比如可以参考 PingCAP 公司 tikv 和 tiflash 的架构。&lt;/li>
&lt;li>&lt;code>Pre candidate&lt;/code>：刚刚发起竞选，还在等待 &lt;code>Pre-Vote&lt;/code> 结果的临时状态， 取决于 &lt;code>Pre-Vote&lt;/code> 的结果，可能进化为 candidate，可能退化为 follower。&lt;/li>
&lt;/ul>
&lt;h3 id="节点状态">节点状态&lt;/h3>
&lt;p>每一个节点都应该有的持久化状态：&lt;/p>
&lt;ul>
&lt;li>&lt;code>currentTerm&lt;/code>：当前任期，保证重启后任期不丢失。&lt;/li>
&lt;li>&lt;code>votedFor&lt;/code>：在当前 term，给哪个节点投了票，值为 null 或 &lt;code>candidate id&lt;/code>。即使节点重启，Raft 算法也能保证每个任期最多只有一个 leader。&lt;/li>
&lt;li>&lt;code>log[]&lt;/code>：已经 committed 的日志，保证状态机可恢复。&lt;/li>
&lt;/ul>
&lt;p>每一个节点都应该有的非持久化状态：&lt;/p>
&lt;ul>
&lt;li>&lt;code>commitindex&lt;/code>：已提交的最大 index。leader 节点重启后可以通过 appendEntries rpc 逐渐得到不同节点的 matchIndex，从而确认 commitIndex，follower 只需等待 leader 传递过来的 commitIndex 即可。&lt;/li>
&lt;li>&lt;code>lastApplied&lt;/code>：已被状态机应用的最大 index。raft 算法假设了状态机本身是易失的，所以重启后状态机的状态可以通过 log[] （部分 log 可以压缩为 snapshot) 来恢复。&lt;/li>
&lt;/ul>
&lt;p>leader 的非持久化状态：&lt;/p>
&lt;ul>
&lt;li>&lt;code>nextindex[]&lt;/code>：为每一个 follower 保存的，应该发送的下一份 &lt;code>entry index&lt;/code>；初始化为本地 last index + 1。&lt;/li>
&lt;li>&lt;code>matchindex[]&lt;/code>：已确认的，已经同步到每一个 follower 的 &lt;code>entry index&lt;/code>。初始化为 0，根据复制状态不断递增，
（注：每次选举后，leader 的此两个数组都应该立刻重新初始化并开始探测）&lt;/li>
&lt;/ul>
&lt;h3 id="任期">任期&lt;/h3>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://tanxinyu.work/raft/term.png#crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;id=Ltfi5&amp;amp;originHeight=204&amp;amp;originWidth=519&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>Raft 将时间划分成为任意不同长度的 term。term 用连续的数字进行表示。每一个 term 的开始都是一次选举，一个或多个 candidate 会试图成为 leader。如果一个 candidate 赢得了选举，它就会在该 term 担任 leader。在某些情况下，选票会被均分，即 &lt;code>split vote&lt;/code>（例如总数为偶数节点时两个 candidate 节点各获得了两票），此时无法选出该 term 的 leader，那么在该 term 的选举超时后将会开始另一个 term 的选举。&lt;/p>
&lt;p>不同的服务器节点可能多次观察到 term 之间的转换，但在某些情况下，一个节点也可能观察不到任何一次选举或者整个 term 全程。term 在 Raft 算法中充当逻辑时钟（类似于 Lamport timestamp）的作用，这会允许服务器节点查明一些过期的信息比如过期的 leader。&lt;/p>
&lt;p>每个节点都会存储当前 term 号，这一编号在整个时间内单调增长。当服务器之间通信的时候会交换当前 term 号；如果一个服务器的当前 term 号比其他人小，那么他会更新自己的 term 到较大的 term 值。如果一个 candidate 或者 leader 发现自己的 term 过期了，那么他会立即退回 follower。如果一个节点接收到一个包含过期 term 号的请求，那么它会拒绝或忽略这个请求。这实际上就是一个 Lamport 逻辑时钟的具体实现。&lt;/p>
&lt;h3 id="日志">日志&lt;/h3>
&lt;ul>
&lt;li>&lt;code>entry&lt;/code>：Raft 中，将每一个事件都称为一个 entry，每一个 entry 都有一个表明它在 log 中位置的 index（之所以从 1 开始是为了方便 &lt;code>prevLogIndex&lt;/code> 从 0 开始）。只有 leader 可以创建 entry。entry 的内容为 &lt;code>&amp;lt;term, index, cmd&amp;gt;&lt;/code>，其中 cmd 是可以应用到状态机的操作。在 raft 组大部分节点都接收这条 entry 后，entry 可以被称为是 committed 的。&lt;/li>
&lt;li>&lt;code>log&lt;/code>：由 entry 构成的数组，只有 leader 可以改变其他节点的 log。 entry 总是先被 leader 添加进本地的 log 数组中去，然后才发起共识请求，获得 quorum 同意后才会被 leader 提交给状态机。follower 只能从 leader 获取新日志和当前的 commitIndex，然后应用对应的 entry 到自己的状态机。&lt;/li>
&lt;/ul>
&lt;h3 id="保证">保证&lt;/h3>
&lt;ul>
&lt;li>&lt;code>Election Safety&lt;/code>：每个 term 最多只会有一个 leader；集群同时最多只会有一个可以读写的 leader。&lt;/li>
&lt;li>&lt;code>Leader Append-Only&lt;/code>：leader 的日志是只增的。&lt;/li>
&lt;li>&lt;code>Log Matching&lt;/code>：如果两个节点的日志中有两个 entry 有相同的 index 和 term，那么它们就是相同的 entry。&lt;/li>
&lt;li>&lt;code>Leader Completeness&lt;/code>：一旦一个操作被提交了，那么在之后的 term 中，该操作都会存在于日志中。&lt;/li>
&lt;li>&lt;code>State Machine Safety&lt;/code>：一致性，一旦一个节点应用了某个 index 的 entry 到状态机，那么其他所有节点应用的该 index 的操作都是一致的。&lt;/li>
&lt;/ul>
&lt;h2 id="领导人选举">领导人选举&lt;/h2>
&lt;p>Raft 使用心跳来维持 leader 身份。任何节点都以 follower 的身份启动。 leader 会定期的发送心跳给所有的 follower 以确保自己的身份。 每当 follower 收到心跳后，就刷新自己的 electionElapsed，重新计时。&lt;/p>
&lt;p>（后文中，会将预设的选举超时称为 electionTimeout，而将当前经过的选举耗时称为 electionElapsed）&lt;/p>
&lt;p>一旦一个 follower 在指定的时间内没有收到任何 RPC（称为 electionTimeout），则会发起一次选举。 当 follower 试图发起选举后，其身份转变为 candidate，在增加自己的 term 后， 会向所有节点发起 RequestVoteRPC 请求，candidate 的状态会一直持续直到：&lt;/p>
&lt;ul>
&lt;li>赢得选举&lt;/li>
&lt;li>其他节点赢得选举&lt;/li>
&lt;li>一轮选举结束，无人胜出&lt;/li>
&lt;/ul>
&lt;p>选举的方式非常简单，谁能获取到多数选票 &lt;code>(N/2 + 1)&lt;/code>，谁就成为 leader。 在一个 candidate 节点等待投票响应的时候，它有可能会收到其他节点声明自己是 leader 的心跳， 此时有两种情况：&lt;/p>
&lt;ul>
&lt;li>该请求的 term 和自己一样或更大：说明对方已经成为 leader，自己立刻退为 follower。&lt;/li>
&lt;li>该请求的 term 小于自己：拒绝请求并返回当前 term 以让请求节点更新 term。&lt;/li>
&lt;/ul>
&lt;p>为了防止在同一时间有太多的 follower 转变为 candidate 导致无法选出绝对多数， Raft 采用了随机选举超时（&lt;code>randomized election timeouts&lt;/code>）的机制， 每一个 candidate 在发起选举后，都会随机化一个新的选举超时时间， 一旦超时后仍然没有完成选举，则增加自己的 term，然后发起新一轮选举。 在这种情况下，应该能在较短的时间内确认出 leader。 （因为 term 较大的有更大的概率压倒其他节点）&lt;/p>
&lt;p>etcd 中将随机选举超时设置为 &lt;code>[electiontimeout, 2 * electiontimeout - 1]&lt;/code>。&lt;/p>
&lt;p>通过一个节点在一个 term 只能给一个节点投票，Raft 保证了对于给定的一个 term 最多只有一个 leader，从而避免了选举导致的 &lt;code>split brain&lt;/code> 以确保 safety；通过不同节点每次随机化选举超时时间，Raft 在实践中（注意：并没有在理论上）避免了活锁以确保 liveness。&lt;/p>
&lt;p>以下是 6.824 lab2 中选举相关逻辑的具体实现，以供参考。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Raft&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">RequestVote&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">RequestVoteRequest&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">RequestVoteResponse&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">persist&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v}&amp;#39;s state is {state %v,term %v,commitIndex %v,lastApplied %v,firstLog %v,lastLog %v} before processing requestVoteRequest %v and reply requestVoteResponse %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lastApplied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getLastLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">votedFor&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">votedFor&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CandidateId&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">VoteGranted&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ChangeState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">StateFollower&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">votedFor&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">isLogUpToDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">LastLogTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">LastLogIndex&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">VoteGranted&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">votedFor&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">CandidateId&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">electionTimer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Reset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">RandomizedElectionTimeout&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">VoteGranted&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Raft&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">StartElection&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">request&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">genRequestVoteRequest&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v} starts election with RequestVoteRequest %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// use Closure
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">grantedVotes&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">votedFor&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">persist&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="nx">peer&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="k">range&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">peers&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">peer&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">go&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">peer&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nb">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">RequestVoteResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">sendRequestVote&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v} receives RequestVoteResponse %v from {Node %v} after sending RequestVoteRequest %v in term %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nx">StateCandidate&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">VoteGranted&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">grantedVotes&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">grantedVotes&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">peers&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">2&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v} receives majority votes in term %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ChangeState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">StateLeader&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">BroadcastHeartbeat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v} finds a new leader {Node %v} with term %v and steps down in term %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ChangeState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">StateFollower&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">votedFor&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">persist&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}(&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日志同步">日志同步&lt;/h2>
&lt;p>leader 被选举后，则负责所有的客户端请求。每一个客户端请求都包含一个命令，该命令可以被作用到 RSM。&lt;/p>
&lt;p>leader 收到客户端请求后，会生成一个 entry，包含 &lt;code>&amp;lt;index, term, cmd&amp;gt;&lt;/code>，再将这个 entry 添加到自己的日志末尾后，向所有的节点广播该 entry。&lt;/p>
&lt;p>follower 如果同意接受该 entry，则在将 entry 添加到自己的日志后，返回同意。&lt;/p>
&lt;p>如果 leader 收到了多数的成功答复，则将该 entry 应用到自己的 RSM， 之后可以称该 entry 是 committed 的。该 committed 信息会随着随后的 AppendEntries 或 Heartbeat RPC 被传达到其他节点。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://tanxinyu.work/raft/log.png#crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;id=MYVcP&amp;amp;originHeight=1098&amp;amp;originWidth=1088&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>Raft 保证下列两个性质：&lt;/p>
&lt;ul>
&lt;li>如果在两个日志（节点）里，有两个 entry 拥有相同的 index 和 term，那么它们一定有相同的 cmd；&lt;/li>
&lt;li>如果在两个日志（节点）里，有两个 entry 拥有相同的 index 和 term，那么它们前面的 entry 也一定相同。&lt;/li>
&lt;/ul>
&lt;p>通过”仅有 leader 可以生成 entry”来确保第一个性质， 第二个性质则通过一致性检查（consistency check）来保证，该检查包含几个步骤：&lt;/p>
&lt;p>leader 在通过 AppendEntriesRPC 和 follower 通讯时，会带上上一块 entry 的信息， 而 follower 在收到后会对比自己的日志，如果发现这个 entry 的信息（index、term）和自己日志内的不符合，则会拒绝该请求。一旦 leader 发现有 follower 拒绝了请求，则会与该 follower 再进行一轮一致性检查， 找到双方最大的共识点，然后用 leader 的 entries 记录覆盖 follower 所有在最大共识点之后的数据。&lt;/p>
&lt;p>寻找共识点时，leader 还是通过 AppendEntriesRPC 和 follower 进行一致性检查， 方法是发送再上一块的 entry， 如果 follower 依然拒绝，则 leader 再尝试发送更前面的一块，直到找到双方的共识点。 因为分歧发生的概率较低，而且一般很快能够得到纠正，所以这里的逐块确认一般不会造成性能问题。当然，在这里进行二分查找或者某些规则的查找可能也能够在理论上得到收益。&lt;/p>
&lt;p>每个 leader 都会为每一个 follower 保存一个 nextIndex 的变量， 标志了下一个需要发送给该 follower 的 entry 的 index。 在 leader 刚当选时，该值初始化为该 leader 的 log 的 index+1。 一旦 follower 拒绝了 entry，则 leader 会执行 nextIndex—，然后再次发送。直到 follower 接收后将 matchIndex 设置为此时的 nextIndex - 1，然后开始正常的复制。这里还可以做一些更细粒度的优化，比如在正常复制时可以批量复制日志以减少系统调用的开销；在寻找共识点时可以只携带一条日志以减少不必要的流量传输，具体可以参考 etcd 的 &lt;a href="https://github.com/etcd-io/etcd/blob/main/raft/tracker/state.go" target="_blank" rel="noopener">设计&lt;/a>。&lt;/p>
&lt;p>以下是 6.824 lab2 中 日志同步相关逻辑的具体实现，以供参考。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Raft&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">replicateOneRound&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">peer&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">RLock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="nx">StateLeader&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">RUnlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">prevLogIndex&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">nextIndex&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">prevLogIndex&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// only snapshot can catch up
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">request&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">genInstallSnapshotRequest&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">RUnlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nb">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">InstallSnapshotResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">sendInstallSnapshot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">handleInstallSnapshotResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// just entries can catch up
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">request&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">genAppendEntriesRequest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">prevLogIndex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">RUnlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nb">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">AppendEntriesResponse&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">sendAppendEntries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">handleAppendEntriesResponse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Raft&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">AppendEntries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">AppendEntriesRequest&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">AppendEntriesResponse&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">persist&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v}&amp;#39;s state is {state %v,term %v,commitIndex %v,lastApplied %v,firstLog %v,lastLog %v} before processing AppendEntriesRequest %v and reply AppendEntriesResponse %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lastApplied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getLastLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Success&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">votedFor&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ChangeState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">StateFollower&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">electionTimer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Reset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">RandomizedElectionTimeout&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PrevLogIndex&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Success&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v} receives unexpected AppendEntriesRequest %v from {Node %v} because prevLogIndex %v &amp;lt; firstLogIndex %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">LeaderId&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PrevLogIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">!&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">matchLog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PrevLogTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PrevLogIndex&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Success&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">lastIndex&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getLastLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">lastIndex&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PrevLogIndex&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ConflictTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ConflictIndex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">lastIndex&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">firstIndex&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ConflictTerm&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PrevLogIndex&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">firstIndex&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">Term&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">index&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">PrevLogIndex&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="nx">firstIndex&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">firstIndex&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ConflictTerm&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">index&lt;/span>&lt;span class="o">--&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ConflictIndex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">index&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">firstIndex&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">entry&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="k">range&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Entries&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">entry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Index&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">firstIndex&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">entry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Index&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">firstIndex&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="nx">entry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nf">shrinkEntriesArray&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="nx">entry&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Index&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">firstIndex&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Entries&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">:]&lt;/span>&lt;span class="o">...&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">advanceCommitIndexForFollower&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">LeaderCommit&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Success&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="安全">安全&lt;/h2>
&lt;h3 id="选举限制">选举限制&lt;/h3>
&lt;p>因为 leader 的强势地位，所以 Raft 在投票阶段就确保选举出的 leader 一定包含了整个集群中目前已 committed 的所有日志。&lt;/p>
&lt;p>当 candidate 发送 RequestVoteRPC 时，会带上最后一个 entry 的信息。 所有的节点收到该请求后，都会比对自己的日志，如果发现自己的日志更新一些，则会拒绝投票给该 candidate。 （Pre-Vote 同理，如果 follower 认为 Pre-Candidate 没有资格的话，会拒绝 PreVote）&lt;/p>
&lt;p>判断日志新旧的方式：获取请求的 entry 后，比对自己日志中的最后一个 entry。 首先比对 term，如果自己的 term 更大，则拒绝请求。 如果 term 一样，则比对 index，如果自己的 index 更大（说明自己的日志更长），则拒绝请求。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// used by RequestVote Handler to judge which log is newer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Raft&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">isLogUpToDate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">lastLog&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getLastLog&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">term&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nx">lastLog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">term&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="nx">lastLog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="nx">lastLog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Index&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://tanxinyu.work/raft/leader_restriction.png#crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;id=Prt3l&amp;amp;originHeight=1126&amp;amp;originWidth=1094&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>在上图中，raft 为了避免出现一致性问题，要求 leader 绝不会提交过去的 term 的 entry （即使该 entry 已经被复制到了多数节点上）。leader 永远只提交当前 term 的 entry， 过去的 entry 只会随着当前的 entry 被一并提交。（上图中的 c，term2 只会跟随 term4 被提交。）&lt;/p>
&lt;p>如果一个 candidate 能取得多数同意，说明它的日志已经是多数节点中最完备的， 那么也就可以认为该 candidate 已经包含了整个集群的所有 committed entries。&lt;/p>
&lt;p>因此 leader 当选后，应当立刻发起 AppendEntriesRPC 提交一个 no-op entry。注意，这是一个 &lt;code>Must&lt;/code>，不是一个 &lt;code>Should&lt;/code>，否则会有许多 corner case 存在问题。比如：&lt;/p>
&lt;ul>
&lt;li>读请求：leader 此时的状态机可能并不是最新的，若服务读请求可能会违反线性一致性，即出现 safety 的问题；若不服务读请求则可能会有 liveness 的问题。&lt;/li>
&lt;li>配置变更：可能会导致数据丢失，具体原因和例子可以参考此 &lt;a href="https://zhuanlan.zhihu.com/p/359206808" target="_blank" rel="noopener">博客&lt;/a>。&lt;/li>
&lt;/ul>
&lt;p>实际上，leader 当选后提交一个 no-op entry 日志的做法就是 Raft 算法解决 “幽灵复现” 问题的解法，感兴趣的可以看看此 &lt;a href="https://mp.weixin.qq.com/s/jzx05Q781ytMXrZ2wrm2Vg" target="_blank" rel="noopener">博客&lt;/a>。&lt;/p>
&lt;h3 id="节点崩溃">节点崩溃&lt;/h3>
&lt;p>如果 leader 崩溃，集群中的所有节点在 electionTimeout 时间内没有收到 leader 的心跳信息就会触发新一轮的选主。总而言之，最终集群总会选出唯一的 leader 。按论文中的说法，计算一次 RPC 耗时高达 &lt;code>30～40ms&lt;/code> 时，&lt;code>99.9%&lt;/code> 的选举依然可以在 &lt;code>3s&lt;/code> 内完成，但一般一个机房内一次 RPC 只需 1ms。当然，选主期间整个集群对外是不可用的。&lt;/p>
&lt;p>如果 follower 和 candidate 奔溃相对而言就简单很多， 因为 Raft 所有的 RPC 都是幂等的，所以 Raft 中所有的请求，只要超时，就会无限的重试。follower 和 candidate 崩溃恢复后，可以收到新的请求，然后按照上面谈论过的追加或拒绝 entry 的方式处理请求。&lt;/p>
&lt;h3 id="时间与可用性">时间与可用性&lt;/h3>
&lt;p>Raft 原则上可以在绝大部分延迟情况下保证一致性， 不过为了保证选择和 leader 的正常工作，最好能满足下列时间条件：&lt;/p>
&lt;pre tabindex="0">&lt;code>broadcastTime &amp;lt;&amp;lt; electionTimeout &amp;lt;&amp;lt; MTBF
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>&lt;code>broadcastTime&lt;/code>：向其他节点并发发送消息的平均响应时间；&lt;/li>
&lt;li>&lt;code>electionTimeout&lt;/code>：follower 判定 leader 已经故障的时间（heartbeat 的最长容忍间隔）；&lt;/li>
&lt;li>&lt;code>MTBF(mean time between failures)&lt;/code>：单台机器的平均健康时间；&lt;/li>
&lt;/ul>
&lt;p>一般来说，broadcastTime 一般为 &lt;code>0.5～20ms&lt;/code>，electionTimeout 可以设置为 &lt;code>10～500ms&lt;/code>，MTBF 一般为一两个月。&lt;/p>
&lt;h2 id="日志压缩">日志压缩&lt;/h2>
&lt;p>Raft 的日志在正常运行期间会增长以合并更多的客户请求，但是在实际的系统中，Raft 的日志无法不受限制地增长。随着日志的增长，日志会占用更多空间，并且需要花费更多时间进行重放。如果没有某种机制可以丢弃日志中累积的过时信息，这最终将导致可用性问题。因此需要定时去做 snapshot。&lt;/p>
&lt;p>snapshot 会包括：&lt;/p>
&lt;ul>
&lt;li>状态机当前的状态。&lt;/li>
&lt;li>状态机最后一条应用的 entry 对应的 index 和 term。&lt;/li>
&lt;li>集群最新配置信息。&lt;/li>
&lt;li>为了保证 exactly-once 线性化语义的去重表（之后会介绍到）。&lt;/li>
&lt;/ul>
&lt;p>各个节点自行择机完成自己的 snapshot 即可，如果 leader 发现需要发给某一个 follower 的 nextIndex 已经被做成了 snapshot，则需要将 snapshot 发送给该 follower。注意 follower 拿到非过期的 snapshot 之后直接覆盖本地所有状态即可，不需要留有部分 entry，也不会出现 snapshot 之后还存在有效的 entry。因此 follower 只需要判断 &lt;code>InstallSnapshot RPC&lt;/code> 是否过期即可。过期则直接丢弃，否则直接替换全部状态即可。&lt;/p>
&lt;p>snapshot 可能会带来两个问题：&lt;/p>
&lt;ol>
&lt;li>做 snapshot 的策略？
一般为定时或者定大小，达到阈值即做 snapshot，做完后对状态机和 raft log 进行原子性替换即可。&lt;/li>
&lt;li>做 snapshot 时是否还可继续提供写请求？
一般情况下，做 snapshot 期间需要保证状态机不发生变化，也就是需要保证 snapshot 期间状态机不处理写请求。当然 raft 层依然可以去同步，只是状态机不能变化，即不能 apply 新提交的日志到状态机中而已。要想做的更好，可以对状态机采用 &lt;code>copy-on-write&lt;/code> 的复制来不阻塞写请求。&lt;/li>
&lt;/ol>
&lt;p>以下是 6.824 lab2 中 日志压缩相关逻辑的具体实现，以供参考。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Raft&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">Snapshot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">index&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">snapshot&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="kt">byte&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">snapshotIndex&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="nx">snapshotIndex&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v} rejects replacing log with snapshotIndex %v as current snapshotIndex %v is larger in term %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">snapshotIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nf">shrinkEntriesArray&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">snapshotIndex&lt;/span>&lt;span class="p">:])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">Command&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">persister&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SaveStateAndSnapshot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">encodeState&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">snapshot&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v}&amp;#39;s state is {state %v,term %v,commitIndex %v,lastApplied %v,firstLog %v,lastLog %v} after replacing log with snapshotIndex %v as old snapshotIndex %v is smaller&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lastApplied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getLastLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">index&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">snapshotIndex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Raft&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">InstallSnapshot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">request&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">InstallSnapshotRequest&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">InstallSnapshotResponse&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v}&amp;#39;s state is {state %v,term %v,commitIndex %v,lastApplied %v,firstLog %v,lastLog %v} before processing InstallSnapshotRequest %v and reply InstallSnapshotResponse %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lastApplied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getLastLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">&amp;lt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">votedFor&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">persist&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">ChangeState&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">StateFollower&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">electionTimer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Reset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">RandomizedElectionTimeout&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// outdated snapshot
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">LastIncludedIndex&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">go&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">applyCh&lt;/span> &lt;span class="o">&amp;lt;-&lt;/span> &lt;span class="nx">ApplyMsg&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">SnapshotValid&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">Snapshot&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Data&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">SnapshotTerm&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">LastIncludedTerm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">SnapshotIndex&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nx">request&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">LastIncludedIndex&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">Raft&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">CondInstallSnapshot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">lastIncludedTerm&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span> &lt;span class="kt">int&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">snapshot&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="kt">byte&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Lock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">defer&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">mu&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Unlock&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v} service calls CondInstallSnapshot with lastIncludedTerm %v and lastIncludedIndex %v to check whether snapshot is still valid in term %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">lastIncludedTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// outdated snapshot
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v} rejects the snapshot which lastIncludedIndex is %v because commitIndex %v is larger&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">false&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span> &lt;span class="p">&amp;gt;&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getLastLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">make&lt;/span>&lt;span class="p">([]&lt;/span>&lt;span class="nx">Entry&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nf">shrinkEntriesArray&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nx">lastIncludedIndex&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="nx">Index&lt;/span>&lt;span class="p">:])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">Command&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// update dummy entry with lastIncludedTerm and lastIncludedIndex
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">Term&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">logs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">Index&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">lastIncludedTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lastApplied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">persister&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">SaveStateAndSnapshot&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">encodeState&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">snapshot&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">DPrintf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;{Node %v}&amp;#39;s state is {state %v,term %v,commitIndex %v,lastApplied %v,firstLog %v,lastLog %v} after accepting the snapshot which lastIncludedTerm is %v, lastIncludedIndex is %v&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">me&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">currentTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">commitIndex&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">lastApplied&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getFirstLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">rf&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">getLastLog&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="nx">lastIncludedTerm&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">lastIncludedIndex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="禅让">禅让&lt;/h2>
&lt;p>有时候，会希望取消当前 leader 的管理权，比如：&lt;/p>
&lt;ul>
&lt;li>leader 节点因为运维原因需要重启；&lt;/li>
&lt;li>有其他更适合当 leader 的节点；&lt;/li>
&lt;/ul>
&lt;p>直接将 leader 节点停机的话，其他节点会等待 electionTimeout 后进入选举状态， 这期间会集群会停止响应。为了避免这一段不可用的时间，可以采用禅让机制（&lt;code>leadership transfer&lt;/code>）。&lt;/p>
&lt;p>禅让的步骤为：&lt;/p>
&lt;ol>
&lt;li>leader 停止响应客户端请求；&lt;/li>
&lt;li>leader 向 target 节点发起一次日志同步；&lt;/li>
&lt;li>leader 向 target 发起一次 TimeoutNowRPC，target 收到该请求后立刻发起一轮投票。&lt;/li>
&lt;/ol>
&lt;p>etcd 中实现了更多的细节（也有一些改动）：&lt;/p>
&lt;ol>
&lt;li>leader 先检查禅让对象（leadTransferee）的身份，如果是 follower，直接忽略；&lt;/li>
&lt;li>leader 检查是否有正在进行的禅让，如果有，则中止之前的禅让状态，开始处理最新的请求；&lt;/li>
&lt;li>检查禅让对象是否是自己，如果是，忽略；&lt;/li>
&lt;li>将禅让状态信息计入 leader 的状态，并且重置 electionElapsed（因为禅让应该在 electionTimeout 内完成）；&lt;/li>
&lt;li>检查禅让对象的日志是否是最新的&lt;/li>
&lt;li>如果禅让对象已经是最新，则直接发送 TimeoutNowRPC&lt;/li>
&lt;li>如果不是，则发送 AppendEntriesRPC，待节点响应成功后，再发送 TimeoutNowRPC&lt;/li>
&lt;/ol>
&lt;p>可以看出，在 etcd 中，leader 除了重置 electionElapsed 外，不会改动自己的状态。 既不会停止对客户端的响应，同时还会继续发送心跳。&lt;/p>
&lt;p>因为 target 机器会更新自己的 term，而且率先发起投票，其有很大的概率赢得选举。 需要注意的是，target 发起的 RequestVoteRPC 中的 &lt;code>isLeaderTransfer=true&lt;/code>， 以防止被其他节点忽略。&lt;/p>
&lt;p>如果 target 机器没能在一次 electionTimeout 内完成选举，那么 leader 认为本次禅让失败， 立刻恢复响应客户端的请求。（这时可以再次重新发起一次禅让请求）&lt;/p>
&lt;p>在 etcd/raft 中，RequestVoteRPC.context 会被设置为 campaignTransfer, 表明本次投票请求来源于 leader transfer，可以强行打断 follower 的租约发起选举。&lt;/p>
&lt;h2 id="预投票">预投票&lt;/h2>
&lt;p>一个暂时脱离集群网络的节点，在重新加入集群后会干扰到集群的运行。&lt;/p>
&lt;p>因为当一个节点和集群失去联系后，在等待 electionTimeout 后，它就会增加自己的 term 并发起选举， 因为联系不上其他节点，所以在 electionTimeout 后，它会继续增加自己的 term 并继续发起选举。&lt;/p>
&lt;p>一段时间以后，它的 term 就会显著的高于原集群的 term。如果此后该节点重新和集群恢复了联络， 它的高 term 会导致 leader 立刻退位，并重新举行选举。&lt;/p>
&lt;p>为了避免这一情形，引入了 Pre-Vote 的机制。在该机制下，一个 candidate 必须在获得了多数赞同的情形下， 才会增加自己的 term。一个节点在满足下述条件时，才会赞同一个 candidate：&lt;/p>
&lt;ul>
&lt;li>该 candidate 的日志足够新；&lt;/li>
&lt;li>当前节点已经和 leader 失联（electionTimeout）。&lt;/li>
&lt;/ul>
&lt;p>也就是说，candidate 会先发起一轮 Pre-Vote，获得多数同意后，更新自己的 term， 再发起一轮 RequestVoteRPC。&lt;/p>
&lt;p>这种情形下，脱离集群的节点，只会不断的发起 Pre-Vote，而不会更新自己的 term。&lt;/p>
&lt;p>在 etcd 的实现中，如果某个节点赞同了某个 candidate， 是不需要更新自己的状态的，它依然可以赞同其他 candidate。 而且，即使收到的 PreVote 的 term 大于自己，也不会更新自己的 term。 也就是说，PreVote 不会改变其他节点的任何状态。&lt;/p>
&lt;p>etcd 中还有一个设计是，当发起 PreVote 的时候，针对的是下一轮的 term， 所以会向所有的节点发送一个 term+1 的 PreVoteReq。&lt;/p>
&lt;pre tabindex="0">&lt;code>func (r *raft) campaign(t CampaignType) { var term uint64 var voteMsg pb.MessageType if t == campaignPreElection { r.becomePreCandidate() voteMsg = pb.MsgPreVote // 这里需要注意的是，PreVote 会针对“下一轮 term”发起投票， // 而 Vote 则是针对当前 term // PreVote RPCs are sent for the next term before we&amp;#39;ve incremented r.Term. term = r.Term + 1 } else { r.becomeCandidate() voteMsg = pb.MsgVote term = r.Term } // ... // 发送投票请求 r.send(pb.Message{Term: term, To: id, Type: voteMsg, Index: r.raftLog.lastIndex(), LogTerm: r.raftLog.lastTerm(), Context: ctx}) // ...}
&lt;/code>&lt;/pre>&lt;h2 id="配置变更">配置变更&lt;/h2>
&lt;p>raft 的配置变更一般分为两种方式：一次变更一个和一次变更多个，前者实现相对简单（谬误，其实并不简单，感兴趣可以参考该 &lt;a href="https://zhuanlan.zhihu.com/p/342319702" target="_blank" rel="noopener">博客&lt;/a>），改动多个节点的配置变更可以被分成多个一次变更一个来执行；后者相对复杂，并且在某些场景下可用性更强，具体可参考 TiDB 5.0 的 &lt;a href="https://mp.weixin.qq.com/s/nLWbEEBBVYuNGde0IbE3XQ" target="_blank" rel="noopener">介绍&lt;/a>。&lt;/p>
&lt;p>以下仅会简单介绍两种成员变更方式，具体可以参考 raft 作者博士论文的 [第四章](&lt;a href="https://github.com/OneSizeFitsQuorum/raft-thesis-zh_cn/blob/master/raft-thesis-zh_cn.md" target="_blank" rel="noopener">https://github.com/OneSizeFitsQuorum/raft-thesis-zh_cn/blob/master/raft-thesis-zh_cn.md&lt;/a> target=) 和 &lt;a href="https://zhuanlan.zhihu.com/p/359206808" target="_blank" rel="noopener">Raft 成员变更的工程实践&lt;/a>。&lt;/p>
&lt;h3 id="一次变更一台">一次变更一台&lt;/h3>
&lt;p>因为在 Raft 算法中，集群中每一个节点都存有整个集群的信息，而集群的成员有可能会发生变更（节点增删、替换节点等）。 Raft 限制一次性只能增／删一个节点，在一次变更结束后，才能继续进行下一次变更。&lt;/p>
&lt;p>如果一次性只变更一个节点，那么只需要简单的要求“在新／旧集群中，都必须取得多数（N/2+1）”， 那么这两个多数中必然会出现交集，这样就可以保证不会因为配置不一致而导致脑裂。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://tanxinyu.work/raft/singlechange.png#crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;id=d4hLb&amp;amp;originHeight=988&amp;amp;originWidth=1582&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>当 leader 收到集群变更的请求后，就会生成一个特殊的 entry 项用来保存配置， 在将配置项添加到 log 后，该配置立刻生效（也就是说任何节点在收到新配置后，就立刻启用新配置）。 然后 leader 将该 entry 扩散至多数节点，成功后则提交该 entry。 一旦一个新配置项被 committed，则视为该次变更已结束，可以继续处理下一次变更了。&lt;/p>
&lt;p>为了保证可用性，需要新增一项规则，节点在响应 RPC 时，不考虑来源节点是否在自己的配置文件之中。 也就是说，即使收到了一个并不在自己配置文件之中的节点发来的 RPC， 也需要正常处理和响应，包括 AppendEntriesRPC 和 RequestVoteRPC。&lt;/p>
&lt;h3 id="一次变更多台">一次变更多台&lt;/h3>
&lt;p>这种变更方式可以一次性变更多个节点（arbitrary configuration）。&lt;/p>
&lt;p>当集群成员在变更时，为了保证服务的可用性（不发生中断），以及避免因为节点变更导致的一致性问题， Raft 提出了两阶段变更，当接收到新的配置文件后，集群会首先进入 joint consensus 状态， 待新的配置文件提交成功后，再回到普通状态。&lt;/p>
&lt;p>更具体的，joint consensus 指的是包含新／旧配置文件全部节点的中间状态：&lt;/p>
&lt;ul>
&lt;li>entries 会被复制到新／旧配置文件中的所有节点；&lt;/li>
&lt;li>新／旧配置文件中的任何一个节点都有可能被选为 leader；&lt;/li>
&lt;li>共识（选举或提交）需要同时在新／旧配置文件中分别获取到多数同意（&lt;code>separate majorities&lt;/code>）&lt;/li>
&lt;/ul>
&lt;p>（注：&lt;code>separate majorities&lt;/code>的意思是需要新／旧集群中的多数都同意。比如如果是从 3 节点切换为全新的 9 节点， 那么要求旧配置中的 2 个节点，和新配置中的 5 个节点都同意，才被认为达成了一次共识）&lt;/p>
&lt;p>所以，在一次配置变更中，一共有三个状态：&lt;/p>
&lt;ul>
&lt;li>&lt;code>C_old&lt;/code>：使用旧的配置文件；&lt;/li>
&lt;li>&lt;code>C_old,new&lt;/code>：同时使用新旧配置文件，也就是新／旧节点的并集；&lt;/li>
&lt;li>&lt;code>C_new&lt;/code>：使用新的配置文件。&lt;/li>
&lt;/ul>
&lt;p>配置文件使用特殊的 entries 进行存储，一个节点一旦获取到新的配置文件， 即使该配置 entry 并没有 committed，也会立刻使用该配置。 所以一次完整的配置变更可以表示为下图：&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://tanxinyu.work/raft/jointchange.png#crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;id=tAhx6&amp;amp;originHeight=844&amp;amp;originWidth=1562&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;ol>
&lt;li>C_old,new 被创建，集群进入 joint consensus，leader 开始传播该 entry；&lt;/li>
&lt;li>C_old,new 被 committed，也就是说此时多数节点都拥有了 C_old,new，此后 C_old 已经不再可能被选为 leader；&lt;/li>
&lt;li>leader 创建并传播 C_new；&lt;/li>
&lt;li>C_new 被提交，此后不在 C_new 内的节点不允许被选为 leader，如有 leader 不在 C_new 则自行退位。&lt;/li>
&lt;/ol>
&lt;h3 id="注意事项">注意事项&lt;/h3>
&lt;p>不在 C_new 的节点可能会干扰集群。&lt;/p>
&lt;p>当 C_new 开始生效后，被移除的节点就会收不到 heartbeat，所以在 electionTimeout 后， 这些节点会更新自己的 term 然后开始尝试竞选，这会导致目前的 leader 被迫退回 follower 并启动一轮投票。 这会反复发生，严重影响效率。&lt;/p>
&lt;p>解决办法是，每一个 follower 增加一个机制，当节点处于 minimum election timeout 之内时， 也就是当一个节点认为自己的 leader 依然存活时，将会拒绝 RequestVoteRPC，既不会同意投票， 也不会根据 RequestVoteRPC 更新自己的 term。&lt;/p>
&lt;p>但如果此时集群正好处于选举之中，那么 C_old 集群的节点可能还是会造成干扰， 所以结合 PreVote 更为可靠。&lt;/p>
&lt;p>这一机制会干扰到 leader transfer 机制，因为在 leader transfer 中，即使 electionTimeout 未到， RequestVoteRPC 也应该打断所有的节点，要求立刻开始进行选举。 所以需要给 RequestVoteRPC 增加一个 flag 来表明是否来自于 leader transfer。&lt;/p>
&lt;p>etcd 中也是增加了一个 flag campaignTransfer 来做特殊标记，见上面的「禅让」一节。&lt;/p>
&lt;h2 id="客户端交互">客户端交互&lt;/h2>
&lt;p>虽然说 raft 算法只是一个 RSM，其只需要保证不同节点上的日志相同即可，其他的事情它都不需要关心。但是要想保证线性一致性语义，对于基于 raft 的 KV 往往还需要额外做一些事情，比如即使客户端会超时重试，也要保证日志的 exactly-once 执行。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="https://tanxinyu.work/raft/client.png#crop=0&amp;amp;crop=0&amp;amp;crop=1&amp;amp;crop=1&amp;amp;id=U0r5h&amp;amp;originHeight=636&amp;amp;originWidth=676&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>考虑这样一个场景，客户端向服务端提交了一条日志，服务端将其在 raft 组中进行了同步并成功 commit，接着在 apply 后返回给客户端执行结果。然而不幸的是，该 rpc 在传输中发生了丢失，客户端并没有收到写入成功的回复。因此，客户端只能进行重试直到明确地写入成功或失败为止，这就可能会导致相同地命令被执行多次，从而违背线性一致性。&lt;/p>
&lt;p>有人可能认为，只要写请求是幂等的，那重复执行多次也是可以满足线性一致性的，实际上则不然。考虑这样一个例子：对于一个仅支持 put 和 get 接口的 raftKV 系统，其每个请求都具有幂等性。设 x 的初始值为 0，此时有两个并发客户端，客户端 1 执行 put(x,1)，客户端 2 执行 get(x) 再执行 put(x,2)，问（客户端 2 读到的值，x 的最终值）是多少。对于线性一致的系统，答案可以是 (0,1)，(0,2) 或 (1,2)。然而，如果客户端 1 执行 put 请求时发生了上段描述的情况，然后客户端 2 读到 x 的值为 1 并将 x 置为了 2，最后客户端 1 超时重试且再次将 x 置为 1。对于这种场景，答案是 (1,1)，这就违背了线性一致性。归根究底还是由于幂等的 put(x,1) 请求在状态机上执行了两次，有两个 LZ 点。因此，即使写请求的业务语义能够保证幂等，不进行额外的处理让其重复执行多次也会破坏线性一致性。当然，读请求由于不改变系统的状态，重复执行多次是没问题的。&lt;/p>
&lt;p>对于这个问题，raft 作者介绍了想要实现线性化语义，就需要保证日志仅被执行一次，即它可以被 commit 多次，但一定只能 apply 一次。其解决方案原文如下：&lt;/p>
&lt;blockquote>
&lt;p>The solution is for clients to assign unique serial numbers to every command. Then, the state machine tracks the latest serial number processed for each client, along with the associated response. If it receives a command whose serial number has already been executed, it responds immediately without re-executing the request.&lt;/p>
&lt;/blockquote>
&lt;p>基本思路便是：&lt;/p>
&lt;ul>
&lt;li>每个 client 都需要一个唯一的标识符，它的每个不同命令需要有一个顺序递增的 commandId，clientId 和这个 commandId，clientId 可以唯一确定一个不同的命令，从而使得各个 raft 节点可以记录保存各命令是否已应用以及应用以后的结果。&lt;/li>
&lt;/ul>
&lt;p>为什么要记录应用的结果？因为通过这种方式同一个命令的多次 apply 最终只会实际应用到状态机上一次，之后相同命令 apply 的时候实际上是不应用到状态机上的而是直接返回的，那么这时候应该返回什么呢？直接返回成功吗？不行，如果第一次应用时状态机报了什么例如 key not exist 等业务上的错而没有被记录，之后就很难捕捉到这个执行结果了，所以也需要将应用结果保存下来。&lt;/p>
&lt;p>如果默认一个客户端只能串行执行请求的话，服务端这边只需要记录一个 map，其 key 是 clientId，其 value 是该 clientId 执行的最后一条日志的 commandId 和状态机的输出即可。&lt;/p>
&lt;p>raft 论文中还考虑了对这个 map 进行一定大小的限制，防止其无线增长。这就带来了两个问题：&lt;/p>
&lt;ul>
&lt;li>集群间的不同节点如何就某个 clientId 过期达成共识。&lt;/li>
&lt;li>不小心驱逐了活跃的 clientId 怎么办，其之后不论是新建一个 clientId 还是复用之前的 clientId 都可能导致命令的重执行。&lt;/li>
&lt;/ul>
&lt;p>这些问题在工程实现上都较为麻烦。比如后者如果业务上是事务那直接 abort 就行，但如果不是事务就很难办了。&lt;/p>
&lt;p>实际上，个人感觉 clientId 是与 session 绑定的，其生命周期应该与 session 一致，开启 session 时从 map 中保存该 clientId，关闭 session 时从 map 中删除该 clientId 及其对应的 value 即可。map 中一个 clientId 对应的内存占用可能都不足 30 字节，相比携带更多业务语义的 session 其实很小，所以感觉没太大必要去严格控制该 map 的内存占用，还不如考虑下怎么控制 session 更大地内存占用呢。这样就不用去纠结前面提到的两个问题了。&lt;/p>
&lt;h2 id="优化">优化&lt;/h2>
&lt;p>业界的实践很多，可以参考 tikv 的 &lt;a href="https://zhuanlan.zhihu.com/p/25735592" target="_blank" rel="noopener">优化&lt;/a> 和 dragonboat 的 &lt;a href="https://zhuanlan.zhihu.com/p/52620657" target="_blank" rel="noopener">优化&lt;/a>。
以下简单列举几种：&lt;/p>
&lt;ul>
&lt;li>batching：降低 system call 的开销。&lt;/li>
&lt;li>pipeline：提升 leader 向 follower 同步的吞吐量。&lt;/li>
&lt;li>异步 apply：提升 raft 算法吞吐量。无法降低延迟，但能增加吞吐量。&lt;/li>
&lt;li>并行同步日志与刷盘：并行 IO，提升 raft 算法吞吐量。&lt;/li>
&lt;li>…&lt;/li>
&lt;/ul>
&lt;h2 id="接口">接口&lt;/h2>
&lt;p>所有节点间仅通过三种类型的 RPC 进行通信：&lt;/p>
&lt;ul>
&lt;li>&lt;code>AppendEntriesRPC&lt;/code>：最常用的，leader 向 follower 发送心跳或同步日志。&lt;/li>
&lt;li>&lt;code>RequestVoteRPC&lt;/code>：选举时，candidate 发起的竞选请求。&lt;/li>
&lt;li>&lt;code>InstallsnapshotRPC&lt;/code>：用于 leader 下发 snapshot。&lt;/li>
&lt;/ul>
&lt;p>在 Diego 后续的博士论文中，又增加了一些 RPCs：&lt;/p>
&lt;ul>
&lt;li>&lt;code>AddServerRPC&lt;/code>：添加单台节点。&lt;/li>
&lt;li>&lt;code>RemoveServerRPC&lt;/code>：移除一个节点。&lt;/li>
&lt;li>&lt;code>TimeoutNowRPC&lt;/code>：立刻发起竞选。
（实际上 etcd 的实现中定义了几十种消息类型，甚至把内部事件也封装为消息一并处理。）&lt;/li>
&lt;/ul>
&lt;h3 id="appendentriesrpc">AppendEntriesRPC&lt;/h3>
&lt;p>参数：&lt;/p>
&lt;ul>
&lt;li>&lt;code>term&lt;/code>：leader 当前的 term；&lt;/li>
&lt;li>&lt;code>leaderId&lt;/code>：leader 的 节点 id，让 follower 可以重定向客户端的连接；&lt;/li>
&lt;li>&lt;code>prevLogIndex&lt;/code>：前一块 entry 的 index；&lt;/li>
&lt;li>&lt;code>prevlogterm&lt;/code>：前一块 entry 的 term；&lt;/li>
&lt;li>&lt;code>entries[]&lt;/code>：给 follower 发送的 entry，可以一次发送多个，heartbeat 时该项可缺省；&lt;/li>
&lt;li>&lt;code>leaderCommit&lt;/code>：leader 当前的 &lt;code>committed index&lt;/code>，follower 收到后可用于自己的状态机。&lt;/li>
&lt;/ul>
&lt;p>返回：&lt;/p>
&lt;ul>
&lt;li>&lt;code>term&lt;/code>：响应者自己的 term；&lt;/li>
&lt;li>&lt;code>success&lt;/code>：bool，是否接受请求。
该请求通过 leaderCommit 通知 follower 提交相应的 entries 到。通过 entries[] 复制 leader 的日志到所有的 follower。&lt;/li>
&lt;/ul>
&lt;p>实现细节：&lt;/p>
&lt;ol>
&lt;li>如果 &lt;code>term &amp;lt; currentTerm&lt;/code>，立刻返回 false&lt;/li>
&lt;li>如果 prevLogIndex 不匹配，返回 false&lt;/li>
&lt;li>如果自己有块 entry 和新的 entry 不匹配（在相同的 index 上有不同的 term）， 删除自己的那一块以及之后的所有 entry；&lt;/li>
&lt;li>把新的 entries 添加到自己的 log；
5 。如果 &lt;code>leaderCommit &amp;gt; commitindex&lt;/code>，将 commitIndex 设置为 &lt;code>min(leaderCommit, last index)&lt;/code>， 并且提交相应的 entries。&lt;/li>
&lt;/ol>
&lt;h3 id="requestvoterpc">RequestVoteRPC&lt;/h3>
&lt;p>参数：&lt;/p>
&lt;ul>
&lt;li>&lt;code>term&lt;/code>：candidate 当前的 term；&lt;/li>
&lt;li>&lt;code>candidateId&lt;/code>：candidate 的节点 id&lt;/li>
&lt;li>&lt;code>lastlogindex&lt;/code>：candidate 最后一个 entry 的 index；&lt;/li>
&lt;li>&lt;code>lastlogterm&lt;/code>：candidate 最后一个 entry 的 term。&lt;/li>
&lt;li>&lt;code>isleaderTransfer&lt;/code>：用于表明该请求来自于禅让，无需等待 electionTimeout，必须立刻响应。&lt;/li>
&lt;li>&lt;code>isPreVote&lt;/code>：用来表明当前是 PreVote 还是真实投票&lt;/li>
&lt;/ul>
&lt;p>返回：&lt;/p>
&lt;ul>
&lt;li>&lt;code>term&lt;/code>：响应者当前的 term；&lt;/li>
&lt;li>&lt;code>voteGranted&lt;/code>：bool，是否同意投票。&lt;/li>
&lt;/ul>
&lt;p>实现细节：&lt;/p>
&lt;ol>
&lt;li>如果 &lt;code>term &amp;lt; currentTerm&lt;/code>，返回 false；&lt;/li>
&lt;li>如果 votedFor 为空或者为该 &lt;code>candidated id&lt;/code>，且日志项不落后于自己，则同意投票。&lt;/li>
&lt;/ol>
&lt;h3 id="installsnapshotrpc">InstallsnapshotRPC&lt;/h3>
&lt;p>参数:&lt;/p>
&lt;ul>
&lt;li>&lt;code>term&lt;/code>：leader 的 term&lt;/li>
&lt;li>&lt;code>leaderId&lt;/code>：leader 的 节点 id&lt;/li>
&lt;li>&lt;code>lastIncludedindex&lt;/code>：snapshot 中最后一块 entry 的 index；&lt;/li>
&lt;li>&lt;code>lastIncludedterm&lt;/code>：snapshot 中最后一块 entry 的 term；&lt;/li>
&lt;li>&lt;code>offset&lt;/code>：该份 chunk 的 offset；&lt;/li>
&lt;li>&lt;code>data[]&lt;/code>：二进制数据；&lt;/li>
&lt;li>&lt;code>done&lt;/code>：是否是最后一块 chunk&lt;/li>
&lt;/ul>
&lt;p>返回：&lt;/p>
&lt;ul>
&lt;li>&lt;code>term&lt;/code>：follower 当前的 term&lt;/li>
&lt;/ul>
&lt;p>实现细节：&lt;/p>
&lt;ol>
&lt;li>如果 &lt;code>term &amp;lt; currentTerm&lt;/code> 就立即回复&lt;/li>
&lt;li>如果是第一个分块（offset 为 0）就创建一个新的快照&lt;/li>
&lt;li>在指定偏移量写入数据&lt;/li>
&lt;li>如果 done 是 false，则继续等待更多的数据&lt;/li>
&lt;li>保存快照文件，丢弃索引值小于快照的日志&lt;/li>
&lt;li>如果现存的日志拥有相同的最后任期号和索引值，则后面的数据继续保持&lt;/li>
&lt;li>丢弃整个日志&lt;/li>
&lt;li>使用快照重置状态机&lt;/li>
&lt;/ol>
&lt;h3 id="addserverrpc">AddServerRPC&lt;/h3>
&lt;p>参数:&lt;/p>
&lt;ul>
&lt;li>&lt;code>newServer&lt;/code>：新节点地址&lt;/li>
&lt;/ul>
&lt;p>返回：&lt;/p>
&lt;ul>
&lt;li>&lt;code>status&lt;/code>：bool，是否添加成功；&lt;/li>
&lt;li>&lt;code>leaderHint&lt;/code>：当前 leader 的信息。&lt;/li>
&lt;/ul>
&lt;p>实现细节：&lt;/p>
&lt;ol>
&lt;li>如果节点不是 leader，返回 NOT_LEADER；&lt;/li>
&lt;li>如果没有在 electionTimeout 内处理，则返回 TIMEOUT；&lt;/li>
&lt;li>等待上一次配置变更完成后，再处理当前变更；&lt;/li>
&lt;li>将新的配置项加入 log，然后发起多数共识，通过后再提交；&lt;/li>
&lt;li>返回 OK。&lt;/li>
&lt;/ol>
&lt;h3 id="removeserverrpc">RemoveServerRPC&lt;/h3>
&lt;p>参数：&lt;/p>
&lt;ul>
&lt;li>&lt;code>oldServer&lt;/code>：要删除的节点的地址&lt;/li>
&lt;/ul>
&lt;p>返回：&lt;/p>
&lt;ul>
&lt;li>&lt;code>status&lt;/code>：bool，是否删除成功；&lt;/li>
&lt;li>&lt;code>leaderHint&lt;/code>：当前 leader 的信息。&lt;/li>
&lt;/ul>
&lt;p>实现细节：&lt;/p>
&lt;ol>
&lt;li>如果节点不是 leader，返回 NOT_LEADER；&lt;/li>
&lt;li>等待上一次配置变更完成后，再处理当前变更；&lt;/li>
&lt;li>将新的配置项加入 log，然后发起多数共识，通过后再提交；&lt;/li>
&lt;li>返回 OK。&lt;/li>
&lt;/ol>
&lt;h3 id="timeoutnowrpc">TimeoutNowRPC&lt;/h3>
&lt;p>由 leader 发起，告知 target 节点立刻发起竞选，无视 electionTimeout。主要用于禅让。&lt;/p>
&lt;h2 id="总结">总结&lt;/h2>
&lt;p>本篇博客较为详细的介绍了 raft 算法的实现，希望能对读者理解 raft 算法有所帮助。&lt;/p>
&lt;h2 id="相关资料">相关资料&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://raft.github.io/" target="_blank" rel="noopener">raft 官网&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://thesecretlivesofdata.com/raft/" target="_blank" rel="noopener">raft 动画教程&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://raft.github.io/raft.pdf" target="_blank" rel="noopener">raft 会议论文&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://web.stanford.edu/~ouster/cgi-bin/papers/OngaroPhD.pdf" target="_blank" rel="noopener">raft 博士论文&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://blog.laisky.com/p/raft/#%E9%9B%86%E7%BE%A4%E8%8A%82%E7%82%B9%E5%8F%98%E6%9B%B4-ohtdR" target="_blank" rel="noopener">raft 论文笔记&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://nil.csail.mit.edu/6.824/2020/notes/l-raft.txt" target="_blank" rel="noopener">6.824 Raft 讲义 1&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://nil.csail.mit.edu/6.824/2020/notes/l-raft2.txt" target="_blank" rel="noopener">6.824 Raft 讲义 2&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>MapReduce 论文阅读</title><link>https://loloxwg.top/posts/mapreduce/</link><pubDate>Wed, 20 Jan 2021 11:34:40 +0000</pubDate><guid>https://loloxwg.top/posts/mapreduce/</guid><description>&lt;h2 id="相关背景">相关背景&lt;/h2>
&lt;p>在 20 世纪初，包括本文作者在内的 Google 的很多程序员，为了处理海量的原始数据，已经实现了数以百计的、专用的计算方法。这些计算方法用来处理大量的原始数据，比如，文档抓取（类似网络爬虫的程序）、Web 请求日志等等；也为了计算处理各种类型的衍生数据，比如倒排索引、Web 文档的图结构的各种表示形势、每台主机上网络爬虫抓取的页面数量的汇总、每天被请求的最多的查询的集合等等。&lt;/p>
&lt;h2 id="要解决的问题">要解决的问题&lt;/h2>
&lt;p>大多数以上提到的数据处理运算在概念上很容易理解。然而由于输入的数据量巨大，因此要想在可接受的时间内完成运算，只有将这些计算分布在成百上千的主机上。如何处理并行计算、如何分发数据、如何处理错误？所有这些问题综合在一起，需要大量的代码处理，因此也使得原本简单的运算变得难以处理。&lt;/p>
&lt;h2 id="解决方法">解决方法&lt;/h2>
&lt;h3 id="模型">模型&lt;/h3>
&lt;p>为了解决上述复杂的问题，本文设计一个新的抽象模型，使用这个抽象模型，用户只要表述想要执行的简单运算即可，而不必关心并行计算、容错、数据分布、负载均衡等复杂的细节，这些问题都被封装在了一个库里面：利用一个输入 key/value pair 集合来产生一个输出的 key/value pair 集合。&lt;/p>
&lt;p>MapReduce 库的用户可以用两个函数表达这个计算：Map 和 Reduce。&lt;/p>
&lt;ul>
&lt;li>用户自定义的 Map 函数接受一个输入的 key/value pair 值，然后产生一个中间 key/value pair 值的集合。MapReduce 库把所有具有相同中间 key 值 I 的中间 value 值集合在一起后传递给 reduce 函数。&lt;/li>
&lt;li>用户自定义的 Reduce 函数接受一个中间 key 的值 I 和相关的一个 value 值的集合。Reduce 函数合并这些 value 值，形成一个较小的 value 值的集合。一般的，每次 Reduce 函数调用只产生 0 或 1 个输出 value 值。通常 Map 通过一个迭代器把中间 value 值提供给 Reduce 函数，这样 Reduce Worker 就可以处理无法全部放入内存中的大量的 value 值的集合。&lt;/li>
&lt;/ul>
&lt;p>在概念上，用户定义的 Map 和 Reduce 函数都有相关联的类型：&lt;/p>
&lt;pre>&lt;code>1 map(k1,v1) -&amp;gt;list(k2,v2)
2 reduce(k2,list(v2)) -&amp;gt;list(v2)
&lt;/code>&lt;/pre>
&lt;p>比如，输入的 key 和 value 值与输出的 key 和 value 值在类型上推导的域不同。此外，中间 key 和 value 值与输出 key 和 value 值在类型上推导的域相同。&lt;/p>
&lt;h3 id="执行流程">执行流程&lt;/h3>
&lt;p>通过将 Map 调用的输入数据自动分割为 M 个数据片段的集合，Map 调用被分布到多台机器上执行。输入的数据片段能够在不同的机器上并行处理。使用分区函数将 Map 调用产生的中间 key 值分成 R 个不同分区（例如，hash(key) mod R），Reduce 调用也被分布到多台机器上执行。分区数量（R）和分区函数由用户来指定。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="" srcset="
/posts/mapreduce/featured_hud838a653d4b651199c7715e0335ad9f9_531837_5736f21c93678d38dd77c88d3dfbf614.webp 400w,
/posts/mapreduce/featured_hud838a653d4b651199c7715e0335ad9f9_531837_fba80d0e279e82f794f2355e8d625835.webp 760w,
/posts/mapreduce/featured_hud838a653d4b651199c7715e0335ad9f9_531837_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://loloxwg.top/posts/mapreduce/featured_hud838a653d4b651199c7715e0335ad9f9_531837_5736f21c93678d38dd77c88d3dfbf614.webp"
width="760"
height="517"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>上图展示了 MapReduce 实现中操作的全部流程。当用户调用 MapReduce 函数时，将发生下面的一系列动作：&lt;/p>
&lt;ol>
&lt;li>用户程序首先调用的 MapReduce 库将输入文件分成 M 个数据片度，每个数据片段的大小一般从 16MB 到 64MB（可以通过可选的参数来控制每个数据片段的大小）。然后用户程序在机群中创建大量的程序副本。&lt;/li>
&lt;li>这些程序副本中的有一个特殊的程序–master。副本中其它的程序都是 worker 程序，由 master 分配任务。有 M 个 Map 任务和 R 个 Reduce 任务将被分配，master 将一个 Map 任务或 Reduce 任务分配给一个空闲的 worker。&lt;/li>
&lt;li>被分配了 map 任务的 worker 程序读取相关的输入数据片段，从输入的数据片段中解析出 key/value pair，然后把 key/value pair 传递给用户自定义的 Map 函数，由 Map 函数生成并输出的中间 key/value pair，并缓存在内存中。&lt;/li>
&lt;li>缓存中的 key/value pair 通过分区函数分成 R 个区域，之后周期性的写入到本地磁盘上。缓存的 key/value pair 在本地磁盘上的存储位置将被回传给 master，由 master 负责把这些存储位置再传送给 Reduce worker&lt;/li>
&lt;li>当 Reduce worker 程序接收到 master 程序发来的数据存储位置信息后，使用 RPC 从 Map worker 所在主机的磁盘上读取这些缓存数据。当 Reduce worker 读取了所有的中间数据后，通过对 key 进行排序后使得具有相同 key 值的数据聚合在一起。由于许多不同的 key 值会映射到相同的 Reduce 任务上，因此必须进行排序。如果中间数据太大无法在内存中完成排序，那么就要在外部进行排序。&lt;/li>
&lt;li>Reduce worker 程序遍历排序后的中间数据，对于每一个唯一的中间 key 值，Reduce worker 程序将这个 key 值和它相关的中间 value 值的集合传递给用户自定义的 Reduce 函数。Reduce 函数的输出被追加到所属分区的输出文件。&lt;/li>
&lt;li>当所有的 Map 和 Reduce 任务都完成之后，master 唤醒用户程序。在这个时候，在用户程序里的对 MapReduce 调用才返回。&lt;/li>
&lt;/ol>
&lt;h3 id="容错">容错&lt;/h3>
&lt;h4 id="worker-故障">worker 故障&lt;/h4>
&lt;p>master 与 worker 之间同步心跳，对于失效的 worker，根据其类型来做进一步处理：&lt;/p>
&lt;ul>
&lt;li>Map worker 故障：由于 Map 任务将数据临时存储在本地，所以需要重新执行。&lt;/li>
&lt;li>Reduce worker 故障：由于 Reduce 任务将数据存储在全局文件系统中 ，所以不需要重新执行。&lt;/li>
&lt;/ul>
&lt;h4 id="master-故障">master 故障&lt;/h4>
&lt;p>MapReduce 任务重新执行&lt;/p>
&lt;h4 id="故障语义保证">故障语义保证&lt;/h4>
&lt;p>当用户提供的 Map 和 Reduce 操作是输入确定性函数（即相同的输入产生相同的输出）时，MapReduce 的分布式实现在任何情况下的输出都和所有程序没有出现任何错误、顺序的执行产生的输出是一样的。&lt;/p>
&lt;ul>
&lt;li>Map worker 任务的原子提交：每个 Map 任务生成 R 个本地临时文件，当一个 Map 任务完成时，worker 发送一个包含 R 个临时文件名的完成消息给 master。如果 master 从一个已经完成的 Map 任务再次接收到一个完成消息，master 将忽略这个消息；&lt;/li>
&lt;li>Reduce worker 任务的原子提交：当 Reduce 任务完成时，Reduce worker 进程以原子的方式把临时文件重命名为最终的输出文件。如果同一个 Reduce 任务在多台机器上执行，针对同一个最终的输出文件将有多个重命名操作执行。MapReduce 依赖底层文件系统提供的重命名操作的原子性来保证最终的文件系统状态仅仅包含一个 Reduce 任务产生的数据。&lt;/li>
&lt;/ul>
&lt;h3 id="存储位置优化">存储位置优化&lt;/h3>
&lt;p>核心思想：本地读文件以减少流量消耗&lt;/p>
&lt;p>MapReduce 的 master 在调度 Map 任务时会考虑输入文件的位置信息，尽量将一个 Map 任务调度在包含相关输入数据拷贝的机器上执行；如果上述努力失败了，master 将尝试在保存有输入数据拷贝的机器附近的机器上执行 Map 任务（例如，分配到一个和包含输入数据的机器在一个交换机里的 worker 机器上执行）。&lt;/p>
&lt;h3 id="任务粒度">任务粒度&lt;/h3>
&lt;p>理想情况下，M 和 R 应当比集群中 worker 的机器数量要多得多。在每台 worker 机器都执行大量的不同任务能够提高集群的动态的负载均衡能力，并且能够加快故障恢复的速度：失效机器上执行的大量 Map 任务都可以分布到所有其他的 worker 机器上去执行。&lt;/p>
&lt;p>实际使用时建议用户选择合适的 M 值，以使得每一个独立任务都是处理大约 16M 到 64M 的输入数据（这样，上面描写的输入数据本地存储优化策略才最有效），另外，也建议把 R 值设置使用的 worker 机器数量的小倍数。比如：M=200000，R=5000，使用 2000 台 worker 机器。&lt;/p>
&lt;h3 id="备用任务">备用任务&lt;/h3>
&lt;p>影响一个 MapReduce 的总执行时间最通常的因素是“落伍者”：在运算过程中，如果有一台机器花了很长的时间才完成最后几个 Map 或 Reduce 任务，导致 MapReduce 操作总的执行时间超过预期。&lt;/p>
&lt;p>为了解决落伍者的问题，当一个 MapReduce 操作接近完成的时候，master 调度备用（backup）任务进程来执行剩下的、处于处理中状态（in-progress）的任务。无论是最初的执行进程、还是备用（backup）任务进程完成了任务，MapReduce 都把这个任务标记成为已经完成。此个机制通常只会占用比正常操作多几个百分点的计算资源。但能减少近 50% 的任务完成总时间。&lt;/p>
&lt;h3 id="技巧">技巧&lt;/h3>
&lt;h4 id="分区函数">分区函数&lt;/h4>
&lt;p>MapReduce 缺省的分区函数是使用 hash 方法（比如，hash(key) mod R) 进行分区。hash 方法能产生非常平衡的分区。然而，有的时候，其它的一些分区函数对 key 值进行的分区将非常有用。比如，输出的 key 值是 URLs，有的用户希望每个主机的所有条目保持在同一个输出文件中。为了支持类似的情况，MapReduce 库的用户需要提供专门的分区函数。例如，使用“hash(Hostname(urlkey))mod R”作为分区函数就可以把所有来自同一个主机的 URLs 保存在同一个输出文件中。&lt;/p>
&lt;h4 id="顺序保证">顺序保证&lt;/h4>
&lt;p>MapReduce 确保在给定的分区中，中间 key/value pair 数据的处理顺序是按照 key 值增量顺序处理的。这样的顺序保证对每个分成生成一个有序的输出文件，这对于需要对输出文件按 key 值随机存取的应用非常有意义，对在排序输出的数据集也很有帮助。&lt;/p>
&lt;h4 id="combiner-函数">Combiner 函数&lt;/h4>
&lt;p>在某些情况下，Map 函数产生的中间 key 值的重复数据会占很大的比重，并且，用户自定义的 Reduce 函数满足结合律和交换律。在 2.1 节的词数统计程序是个很好的例子。由于词频率倾向于一个 zipf 分布（齐夫分布），每个 Map 任务将产生成千上万个这样的记录。所有的这些记录将通过网络被发送到一个单独的 Reduce 任务，然后由这个 Reduce 任务把所有这些记录累加起来产生一个数字。MapReduce 允许用户指定一个可选的 combiner 函数，combiner 函数首先在本地将这些记录进行一次合并，然后将合并的结果再通过网络发送出去。&lt;/p>
&lt;p>Combiner 函数在每台执行 Map 任务的机器上都会被执行一次。一般情况下，Combiner 和 Reduce 函数是一样的。Combiner 函数和 Reduce 函数之间唯一的区别是 MapReduce 库怎样控制函数的输出。Reduce 函数的输出被保存在最终的输出文件里，而 Combiner 函数的输出被写到中间文件里，然后被发送给 Reduce 任务。&lt;/p>
&lt;p>部分的合并中间结果可以显著的提高一些 MapReduce 操作的速度。&lt;/p>
&lt;h4 id="输入和输出的类型">输入和输出的类型&lt;/h4>
&lt;p>支持常用的类型，可以通过提供一个简单的 Reader 接口实现来支持一个新的输入类型。Reader 并非一定要从文件中读取数据，比如可以很容易的实现一个从数据库里读记录的 Reader，或者从内存中的数据结构读取数据的 Reader。&lt;/p>
&lt;h4 id="副作用">副作用&lt;/h4>
&lt;p>在某些情况下，MapReduce 的使用者发现，如果在 Map 或 Reduce 操作过程中增加辅助的输出文件会比较省事。MapReduce 依靠程序 writer 把这种“副作用”变成原子的和幂等的。通常应用程序首先把输出结果写到一个临时文件中，在输出全部数据之后，在使用系统级的原子操作 rename 重新命名这个临时文件。&lt;/p>
&lt;h4 id="跳过损坏的记录">跳过损坏的记录&lt;/h4>
&lt;p>每个 worker 进程都设置了信号处理函数捕获内存段异常（segmentation violation）和总线错误（bus error）。 在执行 Map 或者 Reduce 操作之前，MapReduce 库通过全局变量保存记录序号。如果用户程序触发了一个系统信号，消息处理函数将用“最后一口气”通过 UDP 包向 master 发送处理的最后一条记录的序号。当 master 看到在处理某条特定记录不止失败一次时，master 就标志着条记录需要被跳过，并且在下次重新执行相关的 Map 或者 Reduce 任务的时候跳过这条记录。&lt;/p>
&lt;h4 id="本地执行">本地执行&lt;/h4>
&lt;p>支持本地串行执行以方便调试&lt;/p>
&lt;h4 id="状态信息">状态信息&lt;/h4>
&lt;p>master 支持嵌入 HTTP 服务器以显示一组状态信息页面，用户可以监控各种执行状态。状态信息页面显示了包括计算执行的进度，比如已经完成了多少任务、有多少任务正在处理、输入的字节数、中间数据的字节数、输出的字节数、处理百分比等等&lt;/p>
&lt;h4 id="计数器">计数器&lt;/h4>
&lt;p>MapReduce 库使用计数器统计不同事件发生次数。比如，用户可能想统计已经处理了多少个单词、已经索引的多少篇 German 文档等等。&lt;/p>
&lt;p>这些计数器的值周期性的从各个单独的 worker 机器上传递给 master（附加在 ping 的应答包中传递）。master 把执行成功的 Map 和 Reduce 任务的计数器值进行累计，当 MapReduce 操作完成之后，返回给用户代码。&lt;/p>
&lt;p>计数器当前的值也会显示在 master 的状态页面上，这样用户就可以看到当前计算的进度。当累加计数器的值的时候，master 要检查重复运行的 Map 或者 Reduce 任务，避免重复累加（之前提到的备用任务和失效后重新执行任务这两种情况会导致相同的任务被多次执行）。&lt;/p>
&lt;h2 id="应用场景">应用场景&lt;/h2>
&lt;ul>
&lt;li>分布式的 Grep：Map 函数输出匹配某个模式的一行，Reduce 函数是一个恒等函数，即把中间数据复制到输出。&lt;/li>
&lt;li>计算 URL 访问频率：Map 函数处理日志中 web 页面请求的记录，然后输出 (URL,1)。Reduce 函数把相同 URL 的 value 值都累加起来，产生 (URL, 记录总数）结果。&lt;/li>
&lt;li>网络链接倒排：Map 函数在源页面（source）中搜索所有的链接目标（target）并输出为 (target,source)。Reduce 函数把给定链接目标（target）的链接组合成一个列表，输出 (target,list(source))。&lt;/li>
&lt;li>每个主机的检索词向量：检索词向量用一个（词，频率）列表来概述出现在文档或文档集中的最重要的一些词。Map 函数为每一个输入文档输出（主机名，检索词向量），其中主机名来自文档的 URL。Reduce 函数接收给定主机的所有文档的检索词向量，并把这些检索词向量加在一起，丢弃掉低频的检索词，输出一个最终的（主机名，检索词向量）。&lt;/li>
&lt;li>倒排索引：Map 函数分析每个文档输出一个（词，文档号）的列表，Reduce 函数的输入是一个给定词的所有（词，文档号），排序所有的文档号，输出（词，list（文档号）)。所有的输出集合形成一个简单的倒排索引，它以一种简单的算法跟踪词在文档中的位置。&lt;/li>
&lt;li>分布式排序：Map 函数从每个记录提取 key，输出 (key,record)。Reduce 函数不改变任何的值。这个运算依赖分区机制和排序属性。&lt;/li>
&lt;/ul>
&lt;h2 id="经验分享">经验分享&lt;/h2>
&lt;ul>
&lt;li>约束编程模式使得并行和分布式计算非常容易，也易于构造容错的计算环境；&lt;/li>
&lt;li>网络带宽是稀有资源。大量的系统优化是针对减少网络传输量为目的的：本地优化策略使大量的数据从本地磁盘读取，中间文件写入本地磁盘、并且只写一份中间文件也节约了网络带宽。&lt;/li>
&lt;li>多次执行相同的任务可以减少硬件配置不平衡带来的负面影响，同时解决了由于机器失效导致的数据丢失问题。&lt;/li>
&lt;/ul>
&lt;h2 id="创新之处">创新之处&lt;/h2>
&lt;ul>
&lt;li>通过简单的接口实现了自动的并行化和大规模的分布式计算，通过使用 MapReduce 模型接口实现了在大量普通 PC 机上的高性能计算。&lt;/li>
&lt;li>向工业界证明了 MapReduce 模型在分布式计算上的可行性，拉开了分布式计算的序幕并影响了其后所有的计算框架，包括现在流行的批处理框架 Spark 和流处理框架 Flink 都很受其影响。&lt;/li>
&lt;/ul>
&lt;h2 id="不足之处">不足之处&lt;/h2>
&lt;ul>
&lt;li>基于历史局限性和当时的成本考虑，没有利用内存去更高效的处理数据，不过也为 Spark 提供了思路。&lt;/li>
&lt;li>没有将资料调度和计算调度分离，使得 MapReduce 系统看起来较为冗杂。在开源的 Hadoop 生态中，MapReduce 现只关注于计算，具体的资源调度由 Yarn 管理。&lt;/li>
&lt;/ul>
&lt;h2 id="相关系统">相关系统&lt;/h2>
&lt;ul>
&lt;li>分布式存储系统：GFS/Colossus/HDFS&lt;/li>
&lt;li>批处理框架：Spark&lt;/li>
&lt;li>流处理框架：Flink&lt;/li>
&lt;li>高可用机制：Chubby/ZooKeeper&lt;/li>
&lt;/ul>
&lt;h2 id="相关资料">相关资料&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://nil.csail.mit.edu/6.824/2020/notes/l01.txt" target="_blank" rel="noopener">6.824 讲义&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://nil.csail.mit.edu/6.824/2020/video/1.html" target="_blank" rel="noopener">6.824 视频&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/mapreduce-osdi04.pdf" target="_blank" rel="noopener">论文&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Cxka/paper/blob/0a72fe0b354b65bac25e45163163eb2573f1faf2/map-reduce/map-reduce-cn.pdf" target="_blank" rel="noopener">中文翻译&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>如何实现内存页的分配与释放</title><link>https://loloxwg.top/posts/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%86%85%E5%AD%98%E9%A1%B5%E7%9A%84%E5%88%86%E9%85%8D%E4%B8%8E%E9%87%8A%E6%94%BE/</link><pubDate>Wed, 02 Sep 2020 12:12:28 +0000</pubDate><guid>https://loloxwg.top/posts/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%86%85%E5%AD%98%E9%A1%B5%E7%9A%84%E5%88%86%E9%85%8D%E4%B8%8E%E9%87%8A%E6%94%BE/</guid><description>&lt;p>一、再梳理一下内存结构&lt;br>
1、内存memmgrob_t被划分为多个功能分区，每个功能分区用一个memarea_t描述&lt;br>
2、每个memarea_t都有一个memdivmer_t ，每个memdivmer_t 都有一个bafhlst_t数组[0-51]&lt;br>
3、每个bafhlst_t都有一个链表，用于存放内存段，规则为：&lt;br>
bafhlst_t数组中的每个bafhlst_t，会根据其在数组中的序号n，存放全部2的n次方的连续页面，也就是说：&lt;br>
第0个bafhlst_t，存放全部长度为1的内存段&lt;br>
第1个bafhlst_t，存放全部长度为2的内存段&lt;br>
第2个bafhlst_t，存放全部长度为4的内存段&lt;br>
&amp;hellip;&amp;hellip;&lt;br>
4、在内存段处理时，将开始的msadsc_t指向了最后msadsc_t结构，内存段的起止就都清晰了，而且无论首尾，都记录了分配情况，方便各类操作&lt;br>
5、所以从设计层面来讲，页面的分配和释放，也一定只会是2的n次方大小&lt;br>
&lt;br>
二、申请&lt;br>
1、根据类型找到内存区，也就是定位到了memarea_t-&amp;gt;memdivmer_t-&amp;gt;bafhlst_t数组&lt;br>
2、根据申请内存大小，用二进制1的查找，确定要至少要从bafhlst_t数组中的哪个bafhlst_t申请，才能得到足够大的内存&lt;br>
3、从第一个合适的bafhlst_t到最大的bafhlst_t，依次判断链表中有没有可用内存段，一旦有可用的内存段就使用&lt;br>
4、如果内存段大于所需，就要把多出来的内存不断除以2挂载到上一个bafhlst_t，直到达到所需长度&lt;br>
5、设置内存段状态，起始msadsc_t都标记为已占用&lt;br>
6、更新各层结构相关信息，内存申请结束&lt;br>
7、代码中还有各种加锁解锁，各种校验，还有从大到小申请的一种方式，可以看下&lt;br>
&lt;br>
三、释放&lt;br>
1、根据要释放内存段的msadsc_t，获取属于哪个内存区，也就是定位到了memarea_t-&amp;gt;memdivmer_t-&amp;gt;bafhlst_t数组&lt;br>
2、根据释放内存大小，用二进制1的查找，确定最大可以释放到bafhlst_t数组中的哪个bafhlst_t，避免内存碎片化&lt;br>
3、设置内存段状态，起始msadsc_t都标记为未使用&lt;br>
4、从找到的第一个bafhlst_t到最大的bafhlst_t，依次去看链表中有没有内存段是挨着的，如果有就合并，再去下一个bafhlst_t继续合并&lt;br>
一旦某个bafhlst_t中没能合并，就可以退出了，因为我们只存2的n次方大小的内存段&lt;br>
而且每次合并内存段后，都要清理多余的标记，而且开始的msadsc_t要指向最后的msadsc_t&lt;br>
5、把最终合并后的内存段，加入到对应的bafhlst_t中，重新设置内存段的起始msadsc_t标记&lt;br>
6、更新好各层结构相关信息，内存释放结束&lt;br>
7、代码中还有各种加锁解锁，各种校验，可以看下&lt;br>
&lt;br>
四、对于最后的问题&lt;br>
其实无论采用哪种分配方式，内存的碎片化都是难以彻底避免的。无论是操作系统、虚拟机还是应用，都要面对这个问题。业界有多种思路来解决或缓解此问题：&lt;br>
1、把不可移动内存单独管理，系统内存分区其实在一定程度上解决了这些问题&lt;br>
2、linux采用了 buddy system来缓解内存碎片问题，本节已经介绍&lt;br>
3、linux中为了处理特别小的内存请求，引入了slab技术，来辅助buddy system&lt;br>
4、windows有一种LFH技术，在程序启动时，会额外分配一定的连续内存给这个进程备用，从而降低系统层面的内存管理负担&lt;br>
5、windows在进程退出后，不会立即释放dll文件相关内存，一方面提速，一方面也缓解了操作系统内存管理负担。其实，看下你手机的APP，切换到后台时，就是这个效果&lt;br>
6、无论是linux还是windows都有低优先级进程，在后台默默做着内存规整工作，类似于磁盘碎片清理&lt;br>
7、JVM虚拟机，GC时会通过标记-整理（比如CMS）或复制-清除（比如G1）的方法来解决部分碎片问题&lt;br>
8、类似与LFH，可以考虑在内存分配时额外预留一部分，下次分配在预留的地方继续分配&lt;br>
9、为了内存规整方便，可以考虑靠近应用已分配内存区域进行分配&lt;br>
10、还有一种思路，就是将不连续的内存，转换为逻辑上连续的内存，来绕过碎片化问题，但一些情况下性能难以保证&lt;br>
&lt;br>
应用层面也有工作能做：&lt;br>
1、比如redis在处理内存的时候，申请时会额外申请一部分先备着【记得是jemalloc】，释放时也不会立即释放，有单独的线程进行处理，在应用层面去降低系统内存管理负担&lt;br>
2、同时，redis在数据结构上也做了很多努力&lt;br>
3、在写程序的时候，尽量不要零零散散的去申请大量小内存；&lt;br>
4、除了标准库以外，可以试一下 jemalloc或Doug Lea&amp;rsquo;s malloc&lt;br>
5、感兴趣可以看下redis内存管理的代码&lt;br>
额，好像跑题了。。。&lt;/p></description></item><item><title>操作系统中的锁</title><link>https://loloxwg.top/posts/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-%E9%94%81/</link><pubDate>Wed, 17 Jun 2020 12:20:37 +0000</pubDate><guid>https://loloxwg.top/posts/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F-%E9%94%81/</guid><description>&lt;p>两个核心点&lt;/p>
&lt;ul>
&lt;li>原子操作&lt;/li>
&lt;li>中断&lt;/li>
&lt;/ul>
&lt;p>锁是解决并发同步问题的关键，从本文来看，锁有两个核心点，一个是原子操作，另一个则是中断；&lt;br>
通过原子操作来实现临界区标志位的改变，关闭中断来避免CPU中途离开导致数据同步失败问题。&lt;br>
自旋锁(spinlock)是锁的最小原型，其它锁都是以它为基础来实现的，自旋锁的实现也颇为简单，只需一个简单的原子标志位就可以实现了，当然还要妥善管理中断。&lt;br>
在 xv6 中，对锁的实现只有两种，一种是刚才提到的 spinlock，而另外一种则是 sleeplock，spinlock 不会让出 CPU 执行权，而 sleeplock 则是在 spinlock 的基础上，增加 sleep 功能，即如果一个执行体(线程或者进程)加锁失败，就会进入休眠状态，让出 CPU 执行权，让其它的任务也能得以执行。&lt;br>
本文中的信号量(sem)也是 sleeplock 的一种，sem 的实现更为精致，通过等待队列来记录加锁失败的执行体，并后续通过一定的策略来选择唤醒，这也是很多编程语言中信号量的实现方式。&lt;br>
当然不同的语言会有不同的优化，比如 go 的 Mutex 是非公平的唤醒机制，但是针对非公平的场景，又设有饥饿补偿，总之本文中实现的 sem 几乎是任何信号量（锁）实现的基础蓝本。&lt;/p>
&lt;pre tabindex="0">&lt;code> spinlock_t lock;
x86_spin_lock_init(&amp;amp;lock);
// 加锁，如果加锁成功则进入下面代码执行
// 否则，一直自旋，不断检查 lock 值为否为 0
x86_spin_lock_disable_irq(&amp;amp;lock);
// 处理一些数据同步、协同场景
doing_something();
// 解锁
x86_spin_unlock_enabled_irq(&amp;amp;lock);
sem_t sem;
x86_sem_init(&amp;amp;sem);
// 加锁，减少信号量，如果信号量已经为 0
// 则加锁失败，当前线程会改变为 sleeping 状态
// 并让出 CPU 执行权
krlsem_down(&amp;amp;sem);
// 处理一些数据同步、协同场景
doing_something();
// 解锁，增加信号量，唤醒等待队列中的其它线程（若存在）
krlsem_up(&amp;amp;sem);
&lt;/code>&lt;/pre></description></item></channel></rss>