Skip to content

Instantly share code, notes, and snippets.

@zavakid
Created February 7, 2026 05:46
Show Gist options
  • Select an option

  • Save zavakid/c19146ab2902832a34ab316a7cfeaa8e to your computer and use it in GitHub Desktop.

Select an option

Save zavakid/c19146ab2902832a34ab316a7cfeaa8e to your computer and use it in GitHub Desktop.
Thinkd Distributed Systems
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Think Distributed Systems · 第七章 Partitioning 学习笔记</title>
<style>
:root{
--bg0:#0b1220;
--bg1:#0f1b2e;
--panel:#101a2b;
--card:#0f1a2c;
--card2:#0d1727;
--text:#e7eefc;
--muted:#a8b3c7;
--line:rgba(255,255,255,.10);
--accent:#8bd0ff;
--accent2:#9ee6a8;
--warn:#ffcc66;
--danger:#ff7a90;
--codebg:rgba(255,255,255,.06);
--shadow:0 14px 40px rgba(0,0,0,.35);
--radius:16px;
--navW:320px;
--contentW:1100px;
}
*{box-sizing:border-box;}
/* 关键修复:避免滚动时出现“白色蒙版/发白”的合成问题。
一些浏览器在 background-attachment: fixed + blur/backdrop-filter 叠加时会出现发白。
解决:把背景放到一个固定的伪元素图层上(不参与滚动合成),并提供纯色兜底。 */
html{min-height:100%; background-color:var(--bg1);}
body{min-height:100vh; margin:0; color:var(--text);
font:15px/1.7 system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,"PingFang SC","Hiragino Sans GB","Noto Sans CJK SC","Microsoft YaHei",sans-serif;
background:transparent;
position:relative;
overflow-x:hidden;
}
body::before{
content:"";
position:fixed;
inset:0;
z-index:-1;
pointer-events:none;
background:
radial-gradient(1200px 600px at 15% 0%, rgba(139,208,255,.20), transparent 60%),
radial-gradient(900px 500px at 85% 15%, rgba(158,230,168,.14), transparent 55%),
linear-gradient(180deg, var(--bg0), var(--bg1));
}
/* iOS/Safari 等对 fixed 背景/滤镜更敏感,强制关闭滚动回弹带来的露底 */
@supports (-webkit-touch-callout: none){
html,body{overscroll-behavior-y:none;}
}
a{color:var(--accent); text-decoration:none;}
a:hover{text-decoration:underline;}
.app{
width:min(1480px, 96vw);
margin:0 auto;
display:grid;
grid-template-columns: var(--navW) 1fr;
gap:20px;
padding:20px 14px 40px;
min-height:100vh;
}
/* 左侧导航 */
.nav{
position:sticky;
top:16px;
align-self:start;
height:calc(100vh - 32px);
overflow:auto;
border:1px solid var(--line);
background:rgba(16,26,43,.70);
backdrop-filter: blur(10px);
border-radius:var(--radius);
box-shadow:var(--shadow);
padding:14px;
}
.brand{
display:flex;
flex-direction:column;
gap:6px;
padding:10px 10px 12px;
border-bottom:1px solid var(--line);
margin-bottom:10px;
}
.brand .title{font-weight:800; letter-spacing:.2px; font-size:16px;}
.brand .sub{color:var(--muted); font-size:12.5px;}
.toc{
list-style:none;
padding:0;
margin:12px 0 0;
}
.toc li{margin:2px 0;}
.toc a{
display:block;
padding:7px 10px;
border-radius:12px;
color:var(--text);
border:1px solid transparent;
transition:background .15s ease,border-color .15s ease;
font-size:13.5px;
}
.toc a:hover{background:rgba(255,255,255,.06); border-color:rgba(255,255,255,.08); text-decoration:none;}
.toc a.active{background:rgba(139,208,255,.12); border-color:rgba(139,208,255,.30);}
.nav .hint{
margin-top:12px;
padding:10px 10px;
border-radius:12px;
border:1px dashed rgba(255,255,255,.18);
color:var(--muted);
font-size:12.5px;
}
/* 内容区 */
.content{
max-width:var(--contentW);
}
.hero{
border:1px solid var(--line);
background:rgba(16,26,43,.55);
backdrop-filter: blur(10px);
border-radius:22px;
box-shadow:var(--shadow);
padding:22px 22px 18px;
margin-bottom:18px;
}
.hero h1{margin:0 0 6px; font-size:26px; letter-spacing:.2px;}
.hero p{margin:6px 0 0; color:var(--muted);}
.grid2{
display:grid;
grid-template-columns: 1fr 1fr;
gap:14px;
margin-top:14px;
}
.card{
border:1px solid var(--line);
border-radius:var(--radius);
background:rgba(15,26,44,.62);
box-shadow:0 10px 26px rgba(0,0,0,.25);
padding:16px 16px;
}
.card h2{
margin:0 0 10px;
font-size:18px;
}
.section{
margin-top:16px;
border:1px solid var(--line);
border-radius:22px;
background:rgba(13,23,39,.58);
box-shadow:var(--shadow);
padding:18px 18px;
}
.section h2{margin:0 0 12px; font-size:20px;}
.section h3{margin:16px 0 8px; font-size:16.5px;}
.section p{margin:8px 0; color:rgba(231,238,252,.95)}
.muted{color:var(--muted);}
.pill{
display:inline-flex;
gap:6px;
align-items:center;
padding:3px 10px;
border-radius:999px;
border:1px solid rgba(139,208,255,.35);
background:rgba(139,208,255,.10);
font-size:12.5px;
color:var(--text);
margin-right:6px;
}
.kv{
display:grid;
grid-template-columns: 140px 1fr;
gap:8px 12px;
margin:10px 0 0;
padding:12px;
border-radius:14px;
background:rgba(255,255,255,.05);
border:1px solid rgba(255,255,255,.10);
}
.kv div:nth-child(odd){color:var(--muted);}
.callout{
border-radius:16px;
border:1px solid rgba(255,255,255,.14);
background:linear-gradient(180deg, rgba(139,208,255,.10), rgba(255,255,255,.04));
padding:14px 14px;
margin:12px 0;
}
.callout b{color:var(--accent);}
.warn{
background:linear-gradient(180deg, rgba(255,204,102,.16), rgba(255,255,255,.04));
border-color:rgba(255,204,102,.28);
}
.danger{
background:linear-gradient(180deg, rgba(255,122,144,.14), rgba(255,255,255,.04));
border-color:rgba(255,122,144,.28);
}
.figure{
border:1px solid rgba(255,255,255,.12);
background:rgba(255,255,255,.04);
border-radius:16px;
padding:14px;
margin:12px 0;
}
.figure .cap{color:var(--muted); font-size:12.5px; margin-top:6px;}
pre{
margin:10px 0;
padding:12px 12px;
border-radius:14px;
background:var(--codebg);
border:1px solid rgba(255,255,255,.12);
overflow:auto;
}
code{font-family: ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace; font-size:13px;}
.list{
margin:8px 0 0 18px;
color:rgba(231,238,252,.92);
}
.list li{margin:4px 0;}
.table{
width:100%;
border-collapse:separate;
border-spacing:0;
overflow:hidden;
border-radius:14px;
border:1px solid rgba(255,255,255,.12);
margin:10px 0;
background:rgba(255,255,255,.04);
}
.table th,.table td{
padding:10px 10px;
border-bottom:1px solid rgba(255,255,255,.10);
vertical-align:top;
}
.table th{color:var(--muted); text-align:left; font-weight:650; background:rgba(255,255,255,.03)}
.table tr:last-child td{border-bottom:none;}
.footer{
margin-top:16px;
padding:14px 4px 0;
color:var(--muted);
font-size:12.5px;
}
@media (max-width: 1080px){
:root{--navW:280px;}
.app{grid-template-columns: var(--navW) 1fr;}
}
@media (max-width: 920px){
.app{grid-template-columns: 1fr;}
.nav{position:relative; height:auto;}
.content{max-width:100%;}
.grid2{grid-template-columns:1fr;}
}
</style>
</head>
<body>
<div class="app">
<aside class="nav" id="nav">
<div class="brand">
<div class="title">第七章 · Partitioning</div>
<div class="sub">学习笔记(含对话要点 + 章节主线)</div>
</div>
<ul class="toc" id="toc">
<li><a href="#sec-0">章节地图</a></li>
<li><a href="#sec-1">7.1 百科全书隐喻</a></li>
<li><a href="#sec-2">7.2 分区的定义与价值</a></li>
<li><a href="#sec-3">两层映射:item→partition→node</a></li>
<li><a href="#sec-4">7.4(再)分区与类型</a></li>
<li><a href="#sec-5">水平 vs 垂直(可组合)</a></li>
<li><a href="#sec-6">7.4.2 分配策略:item-based vs directory-based</a></li>
<li><a href="#sec-7">衡量指标:variance 与 relocation</a></li>
<li><a href="#sec-8">7.5 常见 item-based:range 与 hash</a></li>
<li><a href="#sec-9">7.6 Repartitioning:为什么痛</a></li>
<li><a href="#sec-10">7.7 Consistent hashing:兼顾均衡与少搬</a></li>
<li><a href="#sec-11">7.8(再)均衡与 overpartitioning</a></li>
<li><a href="#sec-12">工程落地清单</a></li>
</ul>
<div class="hint">
提示:阅读本章时始终抓住两条线:
<br/>① <b>item→partition</b>(分区规则)
<br/>② <b>partition→node</b>(调度/均衡)
</div>
</aside>
<main class="content">
<section class="hero" id="sec-0">
<h1>第七章学习笔记:Partitioning(分区)</h1>
<p>
本章围绕“把一个逻辑对象拆成多个物理对象”展开:为什么要分区、怎么分区、如何评价分区方案、以及系统演进时如何再分区与再均衡。
你会看到:<b>分区解决扩展性</b>,而下一章的 replication 解决可靠性。
</p>
<div class="grid2">
<div class="card">
<h2>本章你需要掌握的“概念接口”</h2>
<div class="kv">
<div>Partitioning</div><div>item → partition 的归属规则(决定数据属于哪片)</div>
<div>Balancing</div><div>partition → node 的落点分配(决定哪台机器承载哪片)</div>
<div>Variance</div><div>分布是否均匀(倾斜/热点程度)</div>
<div>Relocation</div><div>拓扑变化时需要搬迁的 item 数量</div>
<div>Repartitioning</div><div>改变 item→partition(通常更重、更痛)</div>
<div>Rebalancing</div><div>改变 partition→node(更常做、可控)</div>
</div>
</div>
<div class="card">
<h2>章节主线(读完应该形成的心智图)</h2>
<p class="muted" style="margin-top:6px;">
<span class="pill">隐喻</span>百科全书分卷
<span class="pill">定义</span>逻辑对象 vs 物理对象
<span class="pill">两层映射</span>item→partition→node
<span class="pill">策略</span>range/hash/consistent hashing/目录
<span class="pill">演进</span>repartitioning vs rebalancing
<span class="pill">技巧</span>overpartitioning
</p>
<div class="callout warn" style="margin-top:12px;">
<b>关键提醒:</b>“静态”不一定意味着永不改变,也可能是“慢变化/低频变化”。只要改分区数属于管理动作(需要窗口期、迁移、回滚预案),它就可以被视为 static。
</div>
</div>
</div>
</section>
<section class="section" id="sec-1">
<h2>7.1 百科全书与分卷:一个非常好用的隐喻</h2>
<p>
百科全书是<b>逻辑对象</b>,每一本分卷是<b>物理对象</b>。逻辑上全书包含所有条目,物理上每卷只存互不重叠的一部分条目。
分卷必须有规则(例如按首字母分配),否则查找会退化为“扫全书”。
</p>
<div class="figure">
<div><b>图 7.1 / 7.2 的核心信息</b></div>
<ul class="list">
<li>分区的本质:一个逻辑对象由多个互斥的物理对象共同表示。</li>
<li>分区的目的:把需求与容量分散到多个资源上,避免单点资源瓶颈。</li>
<li>分区的挑战:分布不均(uneven distribution)、需求不均(uneven demand)、跨分区引用(cross-reference)。</li>
</ul>
<div class="cap">你可以把“条目↔分卷”直接映射到“数据项↔分区”。</div>
</div>
<div class="callout">
<b>Aha:</b>分布式系统从定义上就已经“分区”了——每个组件只拥有自己的本地状态,这本身就是一种天然的 partition。
进一步的“数据分区”则是为了突破单节点在存储/吞吐上的限制。
</div>
</section>
<section class="section" id="sec-2">
<h2>7.2 分区的定义与价值:扩展性优先</h2>
<p>
分区(partitioning)用于提升可扩展性:把数据与请求压力分摊到多节点,从而突破单节点限制。
复制(replication)用于提升可靠性:让物理对象代表“整体”。
本章先讲分区,下一章讲复制。
</p>
<div class="table-wrap">
<table class="table">
<thead>
<tr><th>技术</th><th>解决的问题</th><th>核心代价</th></tr>
</thead>
<tbody>
<tr>
<td><b>Partitioning</b></td>
<td>扩展性(Scalability):突破单资源瓶颈</td>
<td>跨分区访问、再分区/迁移复杂度</td>
</tr>
<tr>
<td><b>Replication</b></td>
<td>可靠性(Reliability):突破单点故障</td>
<td>一致性、复制延迟、冲突与协调</td>
</tr>
</tbody>
</table>
</div>
<div class="callout">
<b>实用定义:</b>“big data 是相对的”——任何放不进一个节点的数据(或处理不过来的请求)都需要分区。
节点越弱,越容易“big data”。
</div>
</section>
<section class="section" id="sec-3">
<h2>两层映射:item→partition→node(整章最重要的结构)</h2>
<p>
本章不断强调两个不同但相关的关系:
</p>
<ul class="list">
<li><b>Partitioning:</b>把数据项(item)分配到分区(partition)。</li>
<li><b>Balancing:</b>把分区(partition)分配到节点(node)。</li>
</ul>
<div class="figure">
<div><b>图 7.4 / 7.5 / 7.9 的统一解释</b></div>
<div class="kv">
<div>第一层</div><div>item → partition:决定“归属”,影响面大;更接近“数据模型/路由规则”。</div>
<div>第二层</div><div>partition → node:决定“落点”,适合动态调整;更接近“调度/运维”。</div>
</div>
<div class="cap">理解这两层映射后,你就能区分 repartitioning 和 rebalancing 的代价差异。</div>
</div>
<div class="callout danger">
<b>经验结论:</b>尽量让 <b>item→partition</b> 稳定,把动态性放在 <b>partition→node</b>。
这样系统扩缩容时更平滑,风险更可控。
</div>
</section>
<section class="section" id="sec-4">
<h2>7.4(再)分区与类型:static vs dynamic</h2>
<p>
<b>Repartitioning</b> 指把已分配过的 item 重新分配到不同 partition(典型触发:分区数量变化)。
对 item-based 这类“把分区数写进函数”的方案来说,这一步通常非常痛。
</p>
<h3>静态分区 vs 动态分区</h3>
<ul class="list">
<li><b>Static partitioning:</b>分区数量固定,不能在线改;更简单,但缺乏弹性。</li>
<li><b>Dynamic partitioning:</b>分区数量可在线改;更弹性,但复杂度高。</li>
</ul>
<div class="figure">
<div><b>表 7.1 的要点</b></div>
<div class="kv">
<div>Elasticity</div><div>Static ✗;Dynamic ✓</div>
<div>Complexity</div><div>Static 低;Dynamic 高</div>
</div>
<div class="cap">Dynamic 的复杂度主要来自“在线迁移 + 元数据一致性 + 故障恢复”。</div>
</div>
<div class="callout">
<b>Aha:No change vs slow change</b>
<div class="muted">只要改分区数不是日常操作,而是需要规划、迁移、回滚、窗口期的管理动作,它就属于 static 的语境。</div>
</div>
</section>
<section class="section" id="sec-5">
<h2>水平 vs 垂直:按“行”切还是按“列”切(可组合)</h2>
<p>
最直观的模型来自关系型表:表由行(rows)与列(columns)组成。
</p>
<h3>水平分区(Horizontal / Sharding)</h3>
<p class="muted">按行切分:每个分区包含完整列集合,但只覆盖部分行/主键范围。</p>
<h3>垂直分区(Vertical)</h3>
<p class="muted">按列切分:每个分区覆盖全部行,但只包含部分列(字段集合)。</p>
<div class="figure">
<div><b>组合策略(图 7.7):先垂直再水平</b></div>
<ul class="list">
<li>先按类型拆:文本(profile data)与图片(BLOB)分开存。</li>
<li>再分别水平分区:文本进多个 DB 分片;图片进多个对象/文件存储分片。</li>
<li>好处:不同子系统可独立扩展(DB 的索引/查询 vs 存储的容量/带宽)。</li>
</ul>
<div class="cap">代价:跨存储的引用一致性、失败重试与“孤儿对象”清理需要系统性方案。</div>
</div>
</section>
<section class="section" id="sec-6">
<h2>7.4.2 item→partition 分配策略:item-based vs directory-based</h2>
<p>
当数据被分区后,第一件事是回答:<b>一个 item 属于哪个 partition?</b>
常见有两类:
</p>
<h3>Item-based assignment(无状态、粗粒度)</h3>
<ul class="list">
<li>只依赖 item 自身特征(key、hash(key)、范围)。</li>
<li>stateless:不需要目录表;实现与运维简单。</li>
<li>缺点:不能把某个 item 指定到某个分区;热点 key 碰撞时难以“手动拆解”。</li>
</ul>
<h3>Directory-based assignment(有状态、细粒度)</h3>
<ul class="list">
<li>依赖额外目录/查找表(assignment table)。</li>
<li>stateful:能按 key 精准放置,便于隔离热点、做渐进迁移。</li>
<li>缺点:目录自身可能成为瓶颈/单点,反而与“消除单点”的分区目标冲突。</li>
</ul>
<div class="callout">
<b>Aha:Coarse-grained vs Fine-grained</b>
<div class="muted">item-based 更像“公式路由”;directory-based 更像“路由表”。粒度不同,意味着你处理热点与迁移时的手段完全不同。</div>
</div>
<table class="table">
<thead>
<tr><th>维度</th><th>Item-based</th><th>Directory-based</th></tr>
</thead>
<tbody>
<tr><td>状态</td><td>无状态</td><td>有状态(目录)</td></tr>
<tr><td>控制粒度</td><td>粗(改规则影响大量 key)</td><td>细(可单 key 调度/迁移)</td></tr>
<tr><td>热点处理</td><td>易碰撞,难手动干预</td><td>可定点隔离/迁移热点</td></tr>
<tr><td>系统依赖</td><td>少(计算即可)</td><td>多(目录一致性/可用性/缓存)</td></tr>
</tbody>
</table>
</section>
<section class="section" id="sec-7">
<h2>衡量指标:variance 与 relocation(让比较可量化)</h2>
<p>
分配策略的适配度常用两个指标衡量:
</p>
<div class="kv">
<div><b>Variance</b></div>
<div>item 在 partitions 之间是否均匀。variance 低意味着倾斜小、热点少。</div>
<div><b>Relocation</b></div>
<div>分区数量变化时需要搬迁多少 item。relocation 小意味着扩缩容更平滑。</div>
</div>
<div class="callout warn">
<b>常见张力:</b>很多简单策略只能二选一:
例如 <code>hash(key) % P</code> 往往 variance 很好,但 P 变化时 relocation 很差。
</div>
</section>
<section class="section" id="sec-8">
<h2>7.5 常见 item-based:range 与 hash(用 words 文件做实验)</h2>
<p>
作者用 <code>/usr/share/dict/words</code>(235,976 个单词)做实验,把每个单词分配到 5 个分区。
理想均匀是每个分区约 47,195 个条目。
</p>
<h3>Range partitioning(按 key 范围)</h3>
<p class="muted">示例:按首字母分组(ABCDE / FGHIJ / ...)。</p>
<div class="figure">
<div><b>实验结果(作者机器)</b></div>
<pre><code>0: 67730
1: 33203
2: 35829
3: 73432
4: 25782</code></pre>
<div class="cap">明显不均衡:分区 3 与 0 是热点。原因是输入分布(首字母频率)天然不均匀。</div>
</div>
<h3>Hash partitioning(按 hash 值)</h3>
<pre><code>def placement(key):
return hash(key) % 5</code></pre>
<div class="figure">
<div><b>实验结果(作者机器)</b></div>
<pre><code>0: 47219
1: 47187
2: 47362
3: 47175
4: 47033</code></pre>
<div class="cap">分布非常均匀(variance 很好)。但范围查询会变成跨分区 scatter-gather。</div>
</div>
<div class="callout">
<b>工程提醒:</b>很多语言的内置 <code>hash()</code> 可能跨进程/版本不稳定;生产系统通常使用稳定哈希(固定种子的 xxHash/Murmur/CRC 等)。
</div>
</section>
<section class="section" id="sec-9">
<h2>7.6 Repartitioning:分区数变化时为什么“搬家”很痛</h2>
<p>
作者用脚本比较从 5 分区变 6 分区时,多少 item 仍在原分区(same),多少需要迁移(diff)。
这就是 relocation 的直接量化。
</p>
<h3>Range 的再分区(整体重划边界)</h3>
<div class="figure">
<pre><code>Same: 114790
Diff: 121186
(48.6% remain, 51.4% relocate)</code></pre>
<div class="cap">整体重划边界导致近一半条目迁移。若只对热点范围“局部拆分”,迁移可更局部化。</div>
</div>
<h3>Hash 的再分区(%P)</h3>
<div class="figure">
<pre><code>Same: 39443
Diff: 196533
(16.7% remain, 83.3% relocate)</code></pre>
<div class="cap">改变取模基数相当于“全体重新分桶”,迁移量巨大。</div>
</div>
<div class="figure">
<div><b>表 7.2:最小 variance vs 最小 relocation</b></div>
<div class="kv">
<div>Key range</div><div>variance ✗;relocation ✗</div>
<div>Hashing (%P)</div><div>variance ✓;relocation ✗</div>
</div>
</div>
<div class="callout danger">
<b>结论:</b>如果你的系统需要频繁扩缩容,单纯的 <code>%P</code> 哈希通常会把你逼向更高级的策略(consistent hashing / 目录 / overpartitioning)。
</div>
</section>
<section class="section" id="sec-10">
<h2>7.7 Consistent hashing:同时减少倾斜与搬迁</h2>
<p>
一致性哈希的目标是同时做到:variance 小 + relocation 小。
其关键效果是:从 m 增加到 m+1 个分区时,平均只需要迁移大约 <b>1/(m+1)</b> 的 items(等价于新增分区获得的份额)。
</p>
<div class="figure">
<div><b>作者实验(5→6)</b></div>
<pre><code>Same: 192243
Diff: 43733
(81.5% remain, 18.5% relocate)</code></pre>
<div class="cap">18.5% 接近 1/6≈16.7% 的量级,说明迁移被局部化了(不再全局洗牌)。</div>
</div>
<h3>用“接口/规格”思维理解一致性哈希(不纠缠实现)</h3>
<div class="callout">
<b>Aha:Thinking about algorithms</b>
<div class="muted">从系统层面关注效果与契约:给定拓扑变化,一致性哈希应该带来什么可观察的性质。</div>
</div>
<div class="kv">
<div>路由接口</div><div><code>place(key, ring) → partition/node</code>(同 key 在同 ring 下稳定落点)</div>
<div>迁移接口</div><div><code>relocation(old, new) ≈ Δcapacity_share</code>(新增一个分区只搬走一小段)</div>
<div>均衡接口</div><div><code>variance(ring) → skew</code>(通常配合虚拟节点 vnodes 降低抖动)</div>
</div>
<div class="callout warn">
<b>实践提醒:</b>一致性哈希的均衡性往往需要虚拟节点(vnodes)增强;否则各节点区间长度可能有波动。
</div>
</section>
<section class="section" id="sec-11">
<h2>7.8(再)均衡与 overpartitioning:让扩缩容更平滑</h2>
<p>
<b>Balancing</b> 是 partition→node 的分配,<b>Rebalancing</b> 是在需求变化/节点变化后重新分配分区落点。
这通常比 repartitioning(改 item→partition)更常做、更可控。
</p>
<h3>Overpartitioning(过度分区)</h3>
<p>
过度分区的做法是:让分区数显著多于节点数,从而每个节点承载多个分区。
这样你获得更细的调度粒度。
</p>
<div class="figure">
<div><b>图 7.10 的要点</b></div>
<ul class="list">
<li>分区数=节点数:一对一绑定,缺乏弹性,难以细调。</li>
<li>分区数>节点数:一对多承载,可通过迁移分区来平滑扩缩容。</li>
<li>上限:分区数决定可加入的最大节点数;分区都分完后,再加节点无法提升并行度。</li>
</ul>
</div>
<table class="table">
<thead>
<tr><th>做法</th><th>收益</th><th>代价</th></tr>
</thead>
<tbody>
<tr>
<td><b>Rebalancing</b></td>
<td>适配节点增减、需求波动;迁移边界清晰</td>
<td>需要可靠的元数据与迁移机制;控制抖动</td>
</tr>
<tr>
<td><b>Overpartitioning</b></td>
<td>更细粒度调度;扩缩容更平滑;故障恢复更分散</td>
<td>元数据更大;每分区开销(日志/索引/连接/线程)上升</td>
</tr>
</tbody>
</table>
</section>
<section class="section" id="sec-12">
<h2>工程落地清单:如何设计“够用”的分区策略</h2>
<h3>一、先回答“你的系统更怕什么”</h3>
<ul class="list">
<li><b>更怕热点/倾斜?</b>优先控制 variance(哈希/一致性哈希/目录/热点隔离)。</li>
<li><b>更怕扩缩容搬迁成本?</b>优先控制 relocation(一致性哈希/目录式渐进迁移/固定分区+再均衡)。</li>
<li><b>更怕范围查询成本?</b>优先考虑 range 或二级索引/聚合方案。</li>
</ul>
<h3>二、优先选择“把动态性放在 partition→node”</h3>
<div class="callout">
典型稳妥路径:<b>一开始 overpartitioning</b>(分区数显著大于节点数) + <b>后续主要靠 rebalancing</b> 扩缩容。
只有当分区数上限卡住、或热点严重到需要拆分,才进入 repartitioning。
</div>
<h3>三、选择策略时的快速对照</h3>
<table class="table">
<thead>
<tr><th>策略</th><th>variance</th><th>relocation(扩容)</th><th>范围查询</th><th>实现复杂度</th></tr>
</thead>
<tbody>
<tr><td>Range</td><td>受输入分布影响,可能差</td><td>取决于边界变更方式,整体重划会大</td><td>好</td><td>中</td></tr>
<tr><td>Hash (%P)</td><td>好</td><td>很差(接近全局洗牌)</td><td>差</td><td>低</td></tr>
<tr><td>Consistent hashing</td><td>通常好(vnodes 可更好)</td><td>好(≈新增份额)</td><td>一般</td><td>中</td></tr>
<tr><td>Directory-based</td><td>可很好(按负载调度)</td><td>可很好(渐进迁移)</td><td>取决于目录与索引</td><td>高(目录自身要 HA/一致性)</td></tr>
</tbody>
</table>
<h3>四、读者自测题(帮助巩固)</h3>
<ul class="list">
<li>你的系统里,<b>item→partition</b> 与 <b>partition→node</b> 分别由哪些组件负责?它们的元数据如何传播?</li>
<li>系统扩容时,你更倾向于做 <b>repartitioning</b> 还是 <b>rebalancing</b>?为什么?</li>
<li>你能给出一个“热点碰撞”的例子吗?在 item-based 下你会怎么缓解?目录式又会怎么做?</li>
<li>如果你需要范围查询 + 少搬迁,你会选择什么组合策略?</li>
</ul>
<div class="footer">
备注:本页面根据对话内容与《Think Distributed Systems》第七章要点整理。
你可以继续把第七章后续段落贴上来,我也可以把本页扩展成“带练习/带对照示例/带你们系统映射”的版本。
</div>
</section>
</main>
</div>
<script>
// 平滑滚动
document.querySelectorAll('.toc a').forEach(a => {
a.addEventListener('click', (e) => {
const href = a.getAttribute('href');
if (!href || !href.startsWith('#')) return;
e.preventDefault();
const el = document.querySelector(href);
if (!el) return;
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
history.replaceState(null, '', href);
});
});
// 当前章节高亮
const links = Array.from(document.querySelectorAll('#toc a'));
const sections = links
.map(a => document.querySelector(a.getAttribute('href')))
.filter(Boolean);
const io = new IntersectionObserver((entries) => {
const visible = entries
.filter(e => e.isIntersecting)
.sort((a,b) => b.intersectionRatio - a.intersectionRatio)[0];
if (!visible) return;
const id = '#' + visible.target.id;
links.forEach(a => a.classList.toggle('active', a.getAttribute('href') === id));
}, { root: null, threshold: [0.25, 0.5, 0.75] });
sections.forEach(s => io.observe(s));
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment