<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>架构与分布式 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/shelves/%E6%9E%B6%E6%9E%84%E4%B8%8E%E5%88%86%E5%B8%83%E5%BC%8F/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Wed, 12 Feb 2025 10:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/shelves/%E6%9E%B6%E6%9E%84%E4%B8%8E%E5%88%86%E5%B8%83%E5%BC%8F/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>☸️ Kubernetes 学习总结与避坑指南</title>
      <link>https://ftmao.com/blog/bookstore/kubernetes-authoritative-guide-notes/</link>
      <pubDate>Wed, 12 Feb 2025 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/kubernetes-authoritative-guide-notes/</guid>
      <description>&lt;h1 id=&#34;-kubernetes-学习总结与避坑指南&#34;&gt;☸️ Kubernetes 学习总结与避坑指南&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;参考书籍：龚正 等《Kubernetes 权威指南：从 Docker 到 Kubernetes 实践全接触》、Marko Lukša《Kubernetes in Action》
目标读者：会一点 Docker、刚接触 K8s，或者「YAML 会抄但一出问题就懵」的同学。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Kubernetes（简称 K8s，因为 K 和 s 之间有 8 个字母）是 Google 开源的容器编排系统，解决了「成百上千个容器跑在成百上千台机器上，谁来调度、谁来扩容、挂了谁来拉起」的问题。它不是一门「背完概念就会」的技术——新人最常见的状态是：每个名词都听过，但它们之间怎么串起来的说不清楚，Pod 一挂就不知道该看哪里。&lt;/p&gt;
&lt;p&gt;这篇文章帮你建立&lt;strong&gt;主干知识框架&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;K8s 集群整体长什么样？Master 和 Node 各自干什么？&lt;/li&gt;
&lt;li&gt;Pod、Node、Service、Ingress、ConfigMap 这些核心对象到底是什么关系？&lt;/li&gt;
&lt;li&gt;Istio 服务网格在解决什么问题？&lt;/li&gt;
&lt;li&gt;共享存储（PV/PVC/NAS）怎么用，有哪些坑？&lt;/li&gt;
&lt;li&gt;排查 K8s 问题需要补哪些 Unix 网络编程基础知识？&lt;/li&gt;
&lt;li&gt;生产环境如何做内外网访问隔离（私网端点 / 内网 LB / NetworkPolicy）？&lt;/li&gt;
&lt;li&gt;在阿里云 ACK（以及 AWS EKS）上落地时要注意什么？&lt;/li&gt;
&lt;li&gt;Jenkins 如何与 K8s 打通，实现自动化部署、验证与失败回滚？&lt;/li&gt;
&lt;li&gt;遇到 CrashLoopBackOff、OOMKilled、502、DNS 慢这些经典问题，该按什么思路排查？&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一k8s-整体架构一个中心化大脑--一群干活的节点&#34;&gt;一、K8s 整体架构：一个「中心化大脑 + 一群干活的节点」&lt;/h2&gt;
&lt;p&gt;K8s 集群在逻辑上分为两大部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;控制平面（Control Plane，俗称 Master）&lt;/strong&gt;：集群的大脑，负责「决策」——调度、扩缩容、故障自愈。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作节点（Node）&lt;/strong&gt;：真正跑容器的机器，负责「执行」。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    subgraph 控制平面[&amp;#34;控制平面 Control Plane（阿里云 ACK 托管，用户不可见）&amp;#34;]
        APIServer[kube-apiserver&amp;lt;br/&amp;gt;集群唯一入口，所有操作都经过它]
        ETCD[(etcd&amp;lt;br/&amp;gt;分布式 KV 存储&amp;lt;br/&amp;gt;集群所有状态都在这里)]
        Scheduler[kube-scheduler&amp;lt;br/&amp;gt;决定 Pod 调度到哪个 Node]
        CM[kube-controller-manager&amp;lt;br/&amp;gt;各种控制器：副本数维持、节点监控等]
        CCM[cloud-controller-manager&amp;lt;br/&amp;gt;对接云厂商：SLB/EBS/路由]
    end

    subgraph 节点1[&amp;#34;Worker Node 1&amp;#34;]
        K1[kubelet&amp;lt;br/&amp;gt;节点上的「包工头」]
        P1[kube-proxy&amp;lt;br/&amp;gt;维护 Service 网络规则]
        C1[容器运行时&amp;lt;br/&amp;gt;containerd]
        Pod1[Pod A] ~~~ Pod2[Pod B]
    end

    subgraph 节点2[&amp;#34;Worker Node 2&amp;#34;]
        K2[kubelet]
        P2[kube-proxy]
        C2[containerd]
        Pod3[Pod C]
    end

    APIServer --&amp;gt; ETCD
    Scheduler --&amp;gt; APIServer
    CM --&amp;gt; APIServer
    CCM --&amp;gt; APIServer
    K1 --&amp;gt; APIServer
    K2 --&amp;gt; APIServer
    User[kubectl / CI/CD] --&amp;gt; APIServer&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;记住这条主线：&lt;strong&gt;所有组件只跟 apiserver 说话，apiserver 把状态写进 etcd&lt;/strong&gt;。你敲的每一条 &lt;code&gt;kubectl&lt;/code&gt; 命令、每一个控制器的决策，都是「读/写 apiserver」这个动作。理解了这一点，后面所有概念都好串了。&lt;/p&gt;
&lt;p&gt;K8s 的核心设计思想是&lt;strong&gt;声明式 API + 控制器循环&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[你提交 YAML&amp;lt;br/&amp;gt;期望状态：3 个副本] --&amp;gt; B[apiserver 存进 etcd]
    B --&amp;gt; C{控制器不断对比&amp;lt;br/&amp;gt;期望状态 vs 实际状态}
    C --&amp;gt;|实际只有 2 个| D[再创建 1 个 Pod]
    D --&amp;gt; C
    C --&amp;gt;|一致| E[什么都不做]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这就是「自愈」的本质：不是有人盯着修，而是控制器永远在循环纠偏。所以 K8s 里的对象是「期望状态」的描述，不是一条条命令。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二node干活的机器&#34;&gt;二、Node：干活的机器&lt;/h2&gt;
&lt;h3 id=&#34;21-node-上有什么&#34;&gt;2.1 Node 上有什么&lt;/h3&gt;
&lt;p&gt;每个 Node 上必装三个组件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;kubelet&lt;/strong&gt;：节点代理。接收 apiserver 下发的 Pod 定义（PodSpec），调用容器运行时把容器拉起来，并持续上报节点和 Pod 状态。Pod 挂了重启、探针检测，都是 kubelet 干的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容器运行时&lt;/strong&gt;：真正拉镜像、跑容器的软件。Docker 已在 K8s 1.24 后被移除，现在主流是 &lt;strong&gt;containerd&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;kube-proxy&lt;/strong&gt;：维护节点上的网络规则（iptables/IPVS），让 Service 的虚拟 IP 能转发到真实 Pod。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;22-节点相关的避坑点&#34;&gt;2.2 节点相关的避坑点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;节点 NotReady&lt;/strong&gt;：最常见的原因是 kubelet 挂了、节点磁盘满（DiskPressure）、内存不足（MemoryPressure）或网络不通。排查顺序：先 &lt;code&gt;kubectl describe node &amp;lt;名字&amp;gt;&lt;/code&gt; 看 Conditions，再登节点看 &lt;code&gt;systemctl status kubelet&lt;/code&gt; 和日志 &lt;code&gt;journalctl -u kubelet&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;磁盘压力&lt;/strong&gt;：容器日志、镜像、emptyDir 都在吃节点磁盘。务必给应用日志配轮转（logrotate 或运行时自带的日志上限 &lt;code&gt;containerLogMaxSize&lt;/code&gt;），否则磁盘写满会把节点上所有 Pod 驱逐（Evicted）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要随意登节点手工改东西&lt;/strong&gt;：K8s 期望节点是「牛」不是「宠物」——可替换、不特殊。手工装的 Agent、改的 iptables 规则，节点一重建全没了。需要节点级能力请用 DaemonSet。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三podk8s-的最小调度单元&#34;&gt;三、Pod：K8s 的最小调度单元&lt;/h2&gt;
&lt;h3 id=&#34;31-为什么不是直接调度容器&#34;&gt;3.1 为什么不是直接调度容器&lt;/h3&gt;
&lt;p&gt;Pod 是一组&lt;strong&gt;共享网络和存储&lt;/strong&gt;的容器集合。同一个 Pod 里的容器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;共享同一个 IP 和端口空间（通过 &lt;code&gt;localhost&lt;/code&gt; 直接互通）&lt;/li&gt;
&lt;li&gt;可以共享 Volume&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    subgraph Pod[&amp;#34;Pod（10.0.1.15）&amp;#34;]
        direction LR
        C1[主容器&amp;lt;br/&amp;gt;app:8080]
        C2[边车容器&amp;lt;br/&amp;gt;日志采集]
        V[(共享 Volume&amp;lt;br/&amp;gt;日志目录)]
        C1 --&amp;gt; V
        C2 --&amp;gt; V
    end
    C1 -.-&amp;gt;|localhost:8080| C2&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这个设计的经典用途就是 &lt;strong&gt;Sidecar（边车）模式&lt;/strong&gt;：日志采集、流量代理（Istio 的 Envoy）都以边车容器形式注入 Pod，主容器无感知。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;新人直觉「一个 Pod 跑一个容器」，这对 90% 的场景是对的。只有容器之间必须同生共死、共享 localhost 或共享文件时，才放进同一个 Pod。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;32-pod-生命周期与探针&#34;&gt;3.2 Pod 生命周期与探针&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    [*] --&amp;gt; Pending: 创建，等调度/拉镜像
    Pending --&amp;gt; Running: 调度成功，容器启动
    Running --&amp;gt; Succeeded: 正常退出（Job 类）
    Running --&amp;gt; Failed: 异常退出
    Running --&amp;gt; Running: 探针失败→kubelet 重启容器&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三个探针，新人人手必配：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;探针&lt;/th&gt;
					&lt;th&gt;作用&lt;/th&gt;
					&lt;th&gt;配错的后果&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;livenessProbe&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;判断容器是否活着，失败则&lt;strong&gt;重启容器&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;太灵敏 → 无限重启循环&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;readinessProbe&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;判断是否能接流量，失败则从 Service 摘除&lt;/td&gt;
					&lt;td&gt;不配 → 启动期请求打到没 ready 的 Pod，报 502&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;startupProbe&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;启动探针，成功前禁用另外两个&lt;/td&gt;
					&lt;td&gt;慢启动应用（如老 Java 服务）不配 → 被 liveness 误杀&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;33-资源-requestslimits-与-qos最容易被忽视的重灾区&#34;&gt;3.3 资源 requests/limits 与 QoS：最容易被忽视的重灾区&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;requests&lt;/span&gt;:        &lt;span style=&#34;color:#75715e&#34;&gt;# 调度依据：保证分到这么多&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;cpu&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;250m&amp;#34;&lt;/span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 0.25 核&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;memory&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;512Mi&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;limits&lt;/span&gt;:          &lt;span style=&#34;color:#75715e&#34;&gt;# 使用上限&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;cpu&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;500m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;memory&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1Gi&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;requests 是调度的唯一依据&lt;/strong&gt;：节点剩余可分配资源 ≥ Pod 的 requests 总和，Pod 才调度得上去。不配 requests，调度就是瞎排，节点超载。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU 超 limit → 被限流（throttle），容器变慢但不死；内存超 limit → OOMKilled，直接杀&lt;/strong&gt;。这是两种完全不同的失败模式，排查时先分清。&lt;/li&gt;
&lt;li&gt;QoS 等级：requests=limits 为 &lt;code&gt;Guaranteed&lt;/code&gt;（最后被驱逐）；都不配为 &lt;code&gt;BestEffort&lt;/code&gt;（节点资源紧张时最先被杀）。生产核心服务至少配 &lt;code&gt;Burstable&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四工作负载不要直接创建裸-pod&#34;&gt;四、工作负载：不要直接创建裸 Pod&lt;/h2&gt;
&lt;p&gt;实际工作中你几乎不会手写 Pod，而是使用「管理 Pod 的控制器」：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;对象&lt;/th&gt;
					&lt;th&gt;适用场景&lt;/th&gt;
					&lt;th&gt;一句话&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Deployment&lt;/td&gt;
					&lt;td&gt;无状态服务（Web/API）&lt;/td&gt;
					&lt;td&gt;副本数 + 滚动更新 + 回滚&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;StatefulSet&lt;/td&gt;
					&lt;td&gt;有状态服务（MySQL/Kafka）&lt;/td&gt;
					&lt;td&gt;稳定的名字和网络标识、按序启停&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DaemonSet&lt;/td&gt;
					&lt;td&gt;节点级 Agent（日志/监控）&lt;/td&gt;
					&lt;td&gt;每个 Node 跑一个&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Job / CronJob&lt;/td&gt;
					&lt;td&gt;一次性/定时任务&lt;/td&gt;
					&lt;td&gt;跑完即退&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Deployment 滚动更新过程：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant U as 用户
    participant D as Deployment
    participant RS1 as ReplicaSet v1
    participant RS2 as ReplicaSet v2

    U-&amp;gt;&amp;gt;D: kubectl set image 更新镜像
    D-&amp;gt;&amp;gt;RS2: 创建新 RS，先拉起 1 个新 Pod
    RS2--&amp;gt;&amp;gt;D: 新 Pod ready
    D-&amp;gt;&amp;gt;RS1: 缩掉 1 个旧 Pod
    Note over D,RS2: 循环：新 +1 → 旧 -1
    D-&amp;gt;&amp;gt;RS1: 缩到 0（旧 RS 保留，可回滚）
    U-&amp;gt;&amp;gt;D: kubectl rollout undo（随时回滚）&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;避坑点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;maxSurge / maxUnavailable&lt;/strong&gt; 控制滚动节奏。默认 25%/25%，意味着更新中可能瞬间少 25% 的可用副本——容量紧张的服务要调小 maxUnavailable。&lt;/li&gt;
&lt;li&gt;回滚靠旧 ReplicaSet 保留的 Pod 模板，&lt;code&gt;revisionHistoryLimit&lt;/code&gt; 默认 10，别设成 0 否则无法回滚。&lt;/li&gt;
&lt;li&gt;镜像 tag 不要用 &lt;code&gt;latest&lt;/code&gt;：无法回滚、无法审计，且 &lt;code&gt;imagePullPolicy&lt;/code&gt; 行为反直觉。用 git commit 号或语义化版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五k8s-网络模型与必备的-unix-网络编程知识&#34;&gt;五、K8s 网络模型与必备的 Unix 网络编程知识&lt;/h2&gt;
&lt;p&gt;K8s 的网络问题占了线上故障的一半以上。而很多「K8s 网络问题」本质上是 &lt;strong&gt;Unix 网络编程/TCP 协议问题&lt;/strong&gt;，只是发生在容器环境里。这一节把两边打通。&lt;/p&gt;
&lt;h3 id=&#34;51-k8s-网络的三条铁律&#34;&gt;5.1 K8s 网络的三条铁律&lt;/h3&gt;
&lt;p&gt;K8s 对网络模型只有三个基本要求（具体实现交给 CNI 插件，如 Flannel、Calico、阿里云 Terway）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每个 Pod 有自己的独立 IP&lt;/li&gt;
&lt;li&gt;任意两个 Pod 之间可以直接通信（不经过 NAT）&lt;/li&gt;
&lt;li&gt;Node 和 Pod 之间可以直接通信&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph 节点A[&amp;#34;Node A&amp;#34;]
        PA[&amp;#34;Pod A&amp;lt;br/&amp;gt;10.244.1.5&amp;#34;]
    end
    subgraph 节点B[&amp;#34;Node B&amp;#34;]
        PB[&amp;#34;Pod B&amp;lt;br/&amp;gt;10.244.2.8&amp;#34;]
    end
    PA &amp;lt;--&amp;gt;|&amp;#34;Pod 网络（CNI：Terway/Flannel/Calico）&amp;lt;br/&amp;gt;直连，无 NAT&amp;#34;| PB&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;52-service一组-pod-的稳定入口&#34;&gt;5.2 Service：一组 Pod 的稳定入口&lt;/h3&gt;
&lt;p&gt;Pod 会漂移（重建后 IP 就变了），所以不能在代码里写死 Pod IP。Service 提供&lt;strong&gt;一个固定虚拟 IP（ClusterIP）+ DNS 名字&lt;/strong&gt;，负载均衡到后端 Pod。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    Client[客户端 Pod] --&amp;gt;|&amp;#34;访问 my-svc:80&amp;lt;br/&amp;gt;DNS 解析为 10.96.0.10&amp;#34;| SVC[&amp;#34;Service: my-svc&amp;lt;br/&amp;gt;ClusterIP 10.96.0.10&amp;#34;]
    SVC --&amp;gt;|kube-proxy 的 iptables/IPVS 规则转发| P1[Pod 1]
    SVC --&amp;gt; P2[Pod 2]
    SVC --&amp;gt; P3[Pod 3]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三种 Service 类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt;（默认）：仅集群内可达&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NodePort&lt;/strong&gt;：在每个节点开一个高端口（30000-32767），可从集群外访问&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt;：对接云厂商负载均衡器（阿里云 CLB/SLB、AWS NLB/ALB），对外暴露的标准姿势&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;53-为什么排查-k8s-网络要懂-unix-网络编程&#34;&gt;5.3 为什么排查 K8s 网络要懂 Unix 网络编程&lt;/h3&gt;
&lt;p&gt;下面这几组知识，是在 K8s 环境里排查连接问题的「元技能」：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;（1）TCP 连接状态与 TIME_WAIT&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;TCP 挥手后主动关闭方会进入 TIME_WAIT 状态，默认保持 2MSL（Linux 上约 60s）。在高并发短连接场景（比如服务间大量 HTTP/1.0 式短调用），客户端端口会被 TIME_WAIT 大量占用，最终 &lt;code&gt;Cannot assign requested address&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    ESTABLISHED --&amp;gt; FIN_WAIT1: 主动方 close()
    FIN_WAIT1 --&amp;gt; FIN_WAIT2: 收到对端 ACK
    FIN_WAIT2 --&amp;gt; TIME_WAIT: 收到对端 FIN
    TIME_WAIT --&amp;gt; CLOSED: 2MSL 超时（约60s）
    ESTABLISHED --&amp;gt; CLOSE_WAIT: 对端先 close()
    CLOSE_WAIT --&amp;gt; LAST_ACK: 我方 close()
    LAST_ACK --&amp;gt; CLOSED&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;K8s 里的对应症状与解法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;症状：服务间调用偶发超时，&lt;code&gt;ss -s&lt;/code&gt; 看到 timewait 几万。&lt;/li&gt;
&lt;li&gt;正解：&lt;strong&gt;应用层用长连接 + 连接池&lt;/strong&gt;（HTTP keep-alive、gRPC），别依赖 &lt;code&gt;net.ipv4.tcp_tw_reuse&lt;/code&gt; 这类内核参数硬扛。&lt;/li&gt;
&lt;li&gt;另一个常见坑是 &lt;strong&gt;CLOSE_WAIT 堆积&lt;/strong&gt;：对端关了连接但你的代码没 close()（连接泄漏），&lt;code&gt;ss -ant | grep CLOSE_WAIT&lt;/code&gt; 一查便知。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;（2）socket、backlog 与「连接被 Reset」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;服务端 &lt;code&gt;listen(fd, backlog)&lt;/code&gt; 的 backlog 是「已完成三次握手但应用还没 accept」的队列长度。K8s 场景下突发流量打到刚重启的 Pod，如果应用 backlog 太小（或迟迟不 accept），内核会直接 RST，客户端看到的是 &lt;code&gt;Connection reset by peer&lt;/code&gt;。这类问题在 Pod 扩容瞬间、探针刚通过时高发——&lt;strong&gt;readinessProbe 通过 ≠ 应用真的能扛流量&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;（3）epoll 与 I/O 多路复用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个进程通过 &lt;code&gt;epoll&lt;/code&gt; 可以管理数万连接（C10K 问题的解法）。Nginx Ingress、Envoy 都是 epoll 模型。要懂的点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程能开的连接数受 &lt;strong&gt;文件描述符上限&lt;/strong&gt;限制：&lt;code&gt;ulimit -n&lt;/code&gt;。容器里同样生效，高并发服务记得确认 limit（&lt;code&gt;cat /proc/&amp;lt;pid&amp;gt;/limits&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;每个 TCP 连接 = 一个四元组（源IP:源端口 → 目标IP:目标端口）。客户端对单一目标的并发连接数理论上受源端口数（约 2.8 万个可用）限制——这又回到「必须用连接池复用连接」。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;（4）conntrack：K8s 特有的网络坑&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;kube-proxy 的 iptables 模式依赖内核 conntrack（连接跟踪表）。节点上连接数巨大时 conntrack 表被打满（默认 &lt;code&gt;nf_conntrack_max&lt;/code&gt; 往往不够），表现为&lt;strong&gt;新建连接随机丢包、无日志&lt;/strong&gt;，极难排查。解法：调大 &lt;code&gt;net.netfilter.nf_conntrack_max&lt;/code&gt;，或干脆把 kube-proxy 切到 IPVS 模式。阿里云 ACK 的 Terway 模式在这方面压力小很多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;（5）DNS：K8s 里的「隐形依赖」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;集群内所有服务名解析都靠 CoreDNS。两个经典坑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;glibc 的 DNS 解析是串行的&lt;/strong&gt;，且默认 &lt;code&gt;ndots:5&lt;/code&gt; 导致集群内短域名先查 5 次后缀。症状：应用偶发解析慢 5s（一次超时重传）。解法：FQDN 写法 &lt;code&gt;my-svc.my-ns.svc.cluster.local.&lt;/code&gt;（结尾加点）或调整 Pod 的 &lt;code&gt;dnsConfig&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;Java 应用默认&lt;strong&gt;永久缓存 DNS 结果&lt;/strong&gt;（&lt;code&gt;networkaddress.cache.ttl=-1&lt;/code&gt;），Service 后端 IP 变了 JVM 不知道。容器里务必显式设置 DNS 缓存 TTL（如 30s）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;54-内外网访问隔离哪些服务能暴露哪些必须藏住&#34;&gt;5.4 内外网访问隔离：哪些服务能暴露，哪些必须藏住&lt;/h3&gt;
&lt;p&gt;生产集群的基本安全原则：&lt;strong&gt;默认所有东西都不出集群，只开必要的口子&lt;/strong&gt;。K8s 默认网络是「全通」的（任何 Pod 可以访问任何 Pod，LoadBalancer 一不小心就开到公网），隔离要靠自己显式配置。隔离分三个层面，从外到内：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    Internet[公网用户] --&amp;gt;|只放行 443| WAF[WAF / 安全组&amp;lt;br/&amp;gt;云平台层]
    WAF --&amp;gt; PubLB[公网 ALB/CLB&amp;lt;br/&amp;gt;仅 Web 入口服务]
    PubLB --&amp;gt; PubIng[Ingress Controller&amp;lt;br/&amp;gt;公网命名空间]
    PubIng --&amp;gt; WebSvc[Web Service&amp;lt;br/&amp;gt;ClusterIP]

    Office[办公网/运维] --&amp;gt;|VPN/跳板机| IntLB[内网 LoadBalancer&amp;lt;br/&amp;gt;intranet 类型]
    IntLB --&amp;gt; AdminSvc[管理后台 / 监控&amp;lt;br/&amp;gt;Grafana / Jenkins]

    subgraph 集群内[&amp;#34;集群内部（东西向流量）&amp;#34;]
        WebSvc --&amp;gt;|NetworkPolicy 允许| AppSvc[业务 Service]
        AppSvc --&amp;gt;|NetworkPolicy 允许| DBSvc[数据库 Proxy Service]
        AppSvc -.-&amp;gt;|NetworkPolicy 拒绝| Other[其他 namespace]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;第一层：云网络边界（VPC / 安全组）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Server 用私网端点&lt;/strong&gt;：阿里云 ACK 创建集群时选择「专有网络（私网）」API Server 访问，或只绑定内网 EIP，运维通过 VPN/跳板机访问；公网端点即便有证书认证也是攻击面。AWS EKS 对应 endpoint private access。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;节点安全组收紧入方向&lt;/strong&gt;：只放行 NodePort 范围给内网、放行 LB 健康检查来源，其余一律拒绝。ACK 的节点安全组默认规则要审一遍。&lt;/li&gt;
&lt;li&gt;数据库、Redis 这类云产品走 VPC 内网地址，安全组只对集群节点网段开放。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;第二层：Service 暴露方式——内外网分流的关键&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;LoadBalancer 类型 Service 默认创建的是&lt;strong&gt;公网 LB&lt;/strong&gt;，这是最常见的「误暴露」事故。面向内部的服务要用注解显式声明内网 LB：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 阿里云：内网 CLB（VPC 内可达，公网不可达）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;internal-admin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;annotations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;intranet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;LoadBalancer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;port&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;AWS EKS 对应注解：&lt;code&gt;service.beta.kubernetes.io/aws-load-balancer-internal: &amp;quot;true&amp;quot;&lt;/code&gt;（NLB）或 &lt;code&gt;aws-load-balancer-scheme: internal&lt;/code&gt;（ALB Controller）。&lt;/p&gt;
&lt;p&gt;经验法则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对外服务：公网 ALB/CLB + Ingress，前面最好再挂 WAF。&lt;/li&gt;
&lt;li&gt;对内服务（管理后台、监控、Jenkins）：内网 LB 或纯 ClusterIP，配合 VPN/跳板机访问。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认用 ClusterIP&lt;/strong&gt;，需要出集群时再显式升级类型——把「暴露」当成一个需要评审的动作。&lt;/li&gt;
&lt;li&gt;Ingress 上还可以用注解做来源 IP 白名单（如 &lt;code&gt;nginx.ingress.kubernetes.io/whitelist-source-range: 10.0.0.0/8&lt;/code&gt;），给内部接口再加一道闸。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;第三层：NetworkPolicy——集群内部的东西向隔离&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;即使流量不出集群，「测试 namespace 的 Pod 误连生产数据库」这类事故也常见。NetworkPolicy 是 Pod 级的防火墙（需要 CNI 支持，ACK 的 Terway、Calico 都支持，Flannel 不支持）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 第一步：namespace 默认全拒（白名单模式的起点）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;NetworkPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;default-deny&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;podSelector&lt;/span&gt;: {}          &lt;span style=&#34;color:#75715e&#34;&gt;# 选中 namespace 内所有 Pod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;policyTypes&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Ingress&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Egress&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 第二步：只放行需要的流量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;NetworkPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;allow-web-to-app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;podSelector&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;matchLabels&lt;/span&gt;: {&lt;span style=&#34;color:#f92672&#34;&gt;app&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;order-svc}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;policyTypes&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Ingress&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ingress&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;podSelector&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;matchLabels&lt;/span&gt;: {&lt;span style=&#34;color:#f92672&#34;&gt;app&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;web-frontend}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;port&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;避坑点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;NetworkPolicy 是&lt;strong&gt;白名单累加&lt;/strong&gt;模型：任何一条 policy 选中某 Pod，该 Pod 未被放行的方向就被默认拒绝。写漏了 DNS（Egress 到 kube-dns 的 53 端口）会导致全 namespace 解析失败——默认拒绝后记得显式放行 DNS。&lt;/li&gt;
&lt;li&gt;策略&lt;strong&gt;只对选中后新建立的连接生效&lt;/strong&gt;，且 Flannel 下写了也不生效（静默失效，最坑的一种）。&lt;/li&gt;
&lt;li&gt;云安全组管的是「节点/LB 维度」，NetworkPolicy 管的是「Pod 维度」，两者互补不替代。&lt;/li&gt;
&lt;li&gt;上了 Istio 的话，还可以用 AuthorizationPolicy 做带身份（mTLS 服务身份）的七层鉴权，比纯 IP/端口的 NetworkPolicy 更细。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六ingresshttp-流量的统一入口&#34;&gt;六、Ingress：HTTP 流量的统一入口&lt;/h2&gt;
&lt;p&gt;Service 解决的是四层（TCP）负载均衡；Ingress 解决的是七层（HTTP/HTTPS）路由：按域名、按路径把外部流量分发到不同 Service。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    User[外部用户] --&amp;gt;|HTTPS| LB[云负载均衡&amp;lt;br/&amp;gt;阿里云 ALB/CLB]
    LB --&amp;gt; IC[Ingress Controller&amp;lt;br/&amp;gt;Nginx / ALB Ingress]
    IC --&amp;gt;|&amp;#34;a.example.com/api/*&amp;#34;| S1[Service: api]
    IC --&amp;gt;|&amp;#34;a.example.com/web/*&amp;#34;| S2[Service: web]
    IC --&amp;gt;|&amp;#34;b.example.com/*&amp;#34;| S3[Service: admin]
    S1 --&amp;gt; P1[api Pod]
    S2 --&amp;gt; P2[web Pod]
    S3 --&amp;gt; P3[admin Pod]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;注意分清两层：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ingress 资源&lt;/strong&gt;（YAML 对象）：只是路由规则的声明，本身不做任何事。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress Controller&lt;/strong&gt;（真正干活的组件）：如 ingress-nginx、阿里云 ALB Ingress Controller。它 watch Ingress 资源，把规则翻译成 Nginx 配置并热加载。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;避坑点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pathType: Prefix&lt;/code&gt; 和 &lt;code&gt;Exact&lt;/code&gt; 的区别，&lt;code&gt;/api&lt;/code&gt; 配 Prefix 会把 &lt;code&gt;/apiserver&lt;/code&gt; 也匹配进去。&lt;/li&gt;
&lt;li&gt;后端重写（&lt;code&gt;rewrite-target&lt;/code&gt;）是最容易写错的注解，先想清楚「客户端路径 → 后端真实路径」的映射。&lt;/li&gt;
&lt;li&gt;HTTPS 证书放在 Secret 里，过期忘续是经典事故——建议上 cert-manager 自动签发续期。&lt;/li&gt;
&lt;li&gt;502 的第一排查动作：看 Ingress Controller 日志里 upstream 是「连接拒绝」（Pod 没 ready）还是「超时」（应用卡死）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七configmap-与-secret配置和代码分离&#34;&gt;七、ConfigMap 与 Secret：配置和代码分离&lt;/h2&gt;
&lt;p&gt;十二要素应用的第一条就是「配置存环境」。K8s 里对应两个对象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ConfigMap&lt;/strong&gt;：普通配置（应用参数、配置文件、特性开关）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secret&lt;/strong&gt;：敏感配置（密码、密钥、证书），etcd 中 Base64 编码（注意：&lt;strong&gt;Base64 不是加密&lt;/strong&gt;，生产环境要开 etcd 静态加密或对接 KMS）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两种注入方式：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 方式一：环境变量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;DB_HOST&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;valueFrom&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;configMapKeyRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;app-config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;db_host&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 方式二：挂载为文件（推荐，支持热更新）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;volumeMounts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;mountPath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/etc/app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;configMap&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;app-config&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;两个必踩的坑，提前告诉你：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;环境变量注入的配置，改了 ConfigMap 不会生效&lt;/strong&gt;，必须重启 Pod。文件挂载方式会自动更新（kubelet 定期同步，有分钟级延迟），但应用要支持 reload。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;subPath&lt;/code&gt; 挂载的 ConfigMap 不会自动更新&lt;/strong&gt;。这是用了 subPath 挂单个配置文件的同学必踩的坑——改完 ConfigMap 发现容器里还是旧的。解法：挂载整个目录，或接受重启 Pod 的事实。&lt;/li&gt;
&lt;li&gt;ConfigMap 有 1MB 大小限制，别把大文件塞进去。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八共享存储pv--pvc--storageclass&#34;&gt;八、共享存储：PV / PVC / StorageClass&lt;/h2&gt;
&lt;p&gt;容器是临时的，数据要持久化就必须落到外部存储。K8s 的存储抽象分三层：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    subgraph 管理员/云平台侧
        SC[StorageClass&amp;lt;br/&amp;gt;定义「用什么盘」&amp;lt;br/&amp;gt;如 alicloud-disk-essd / nas]
        PV[PersistentVolume&amp;lt;br/&amp;gt;一块真实的存储&amp;lt;br/&amp;gt;云盘/NAS 文件系统]
    end
    subgraph 应用侧
        PVC[PersistentVolumeClaim&amp;lt;br/&amp;gt;「我要 100Gi 存储」的申请单]
        Pod[Pod 挂载 PVC]
    end
    PVC --&amp;gt;|绑定| PV
    SC --&amp;gt;|动态供给：PVC 一创建&amp;lt;br/&amp;gt;自动开出对应 PV| PV
    Pod --&amp;gt; PVC&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;现代用法几乎全是&lt;strong&gt;动态供给&lt;/strong&gt;：你只需要写 PVC 声明要多少空间，StorageClass 自动调用云厂商 API 创建云盘/NAS，不用手工建 PV。&lt;/p&gt;
&lt;p&gt;三种访问模式（Access Modes）是高频考点：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;模式&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
					&lt;th&gt;典型存储&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;ReadWriteOnce (RWO)&lt;/td&gt;
					&lt;td&gt;只能挂到一个节点&lt;/td&gt;
					&lt;td&gt;云盘（阿里云 ESSD、AWS EBS）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ReadWriteMany (RWX)&lt;/td&gt;
					&lt;td&gt;多节点共享读写&lt;/td&gt;
					&lt;td&gt;文件存储（阿里云 NAS、AWS EFS）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ReadOnlyMany (ROX)&lt;/td&gt;
					&lt;td&gt;多节点只读&lt;/td&gt;
					&lt;td&gt;对象存储挂载、只读数据卷&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;共享存储的避坑清单：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;云盘（RWO）不能跨可用区挂载&lt;/strong&gt;。PV 创建在可用区 A，Pod 被调度到可用区 B 就永远 Pending。解法：用 &lt;code&gt;WaitForFirstConsumer&lt;/code&gt; 模式的 StorageClass（等 Pod 定好节点再开盘），并用拓扑约束调度。&lt;/li&gt;
&lt;li&gt;要&lt;strong&gt;多副本共享文件&lt;/strong&gt;（如多个 Web Pod 共享上传目录），必须用 NAS/EFS 这类 RWX 存储，云盘做不到。&lt;/li&gt;
&lt;li&gt;NAS 挂载注意&lt;strong&gt;权限问题&lt;/strong&gt;：容器内进程 UID 和 NAS 目录属主不匹配会 Permission denied，用 &lt;code&gt;securityContext.fsGroup&lt;/code&gt; 或挂载参数解决。&lt;/li&gt;
&lt;li&gt;删除 PVC 会不会丢数据，取决于 PV 的 &lt;code&gt;persistentVolumeReclaimPolicy&lt;/code&gt;（Retain 保留 / Delete 连盘一起删）。&lt;strong&gt;生产数据盘务必确认是 Retain&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;StatefulSet 用 &lt;code&gt;volumeClaimTemplates&lt;/code&gt; 给每个副本独立开盘，扩缩容时数据卷跟着 Pod 名字走，这是有状态服务的标准姿势。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九istio-服务网格把网络能力从业务代码里剥出来&#34;&gt;九、Istio 服务网格：把「网络能力」从业务代码里剥出来&lt;/h2&gt;
&lt;p&gt;微服务多了之后，每个服务都要自己实现超时重试、熔断、灰度、流量监控——这些和业务无关。Istio 的思路：给每个 Pod 注入一个 &lt;strong&gt;Envoy 边车代理&lt;/strong&gt;，接管所有进出流量，上述能力全部在代理层实现，业务代码零侵入。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    subgraph 控制平面[&amp;#34;Istio 控制平面（istiod）&amp;#34;]
        Pilot[配置下发&amp;lt;br/&amp;gt;流量规则]
        Citadel[证书管理&amp;lt;br/&amp;gt;mTLS]
    end
    subgraph 数据平面
        subgraph PodA[&amp;#34;Pod A&amp;#34;]
            App1[业务容器] --- E1[Envoy 边车]
        end
        subgraph PodB[&amp;#34;Pod B&amp;#34;]
            App2[业务容器] --- E2[Envoy 边车]
        end
    end
    Pilot -.-&amp;gt;|xDS 配置下发| E1
    Pilot -.-&amp;gt;|xDS 配置下发| E2
    E1 &amp;lt;--&amp;gt;|&amp;#34;mTLS 加密流量&amp;lt;br/&amp;gt;（应用无感知）&amp;#34;| E2&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;核心能力速览：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;流量管理&lt;/strong&gt;：VirtualService + DestinationRule 实现按权重灰度（v1 90% / v2 10%）、按 Header 路由、超时重试、熔断&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全&lt;/strong&gt;：服务间自动 mTLS 加密 + 身份认证（AuthorizationPolicy）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可观测&lt;/strong&gt;：不用改代码就拿到全链路指标、调用拓扑、访问日志&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;入口&lt;/strong&gt;：Istio Gateway 替代/补充原生 Ingress，作为网格的南北向入口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;避坑点（Istio 坑不少，挑最重要的）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;边车有成本&lt;/strong&gt;：每个 Pod 多一份 Envoy 的 CPU/内存（默认 requests 约 100m/128Mi），上千 Pod 规模要算这笔账，必要时调小。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sidecar 启动顺序&lt;/strong&gt;：业务容器可能比 Envoy 先起来导致启动瞬间网络不通（现在 &lt;code&gt;holdApplicationUntilProxyStarts&lt;/code&gt; 可解）；Job 类任务跑完 Envoy 不退导致 Job 永不结束，需要显式退出或关掉注入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协议识别&lt;/strong&gt;：Service 端口名要带协议前缀（&lt;code&gt;name: http-xxx&lt;/code&gt;），否则 Istio 按 TCP 处理，七层能力全部失效。这是网格内路由不生效的第一嫌疑。&lt;/li&gt;
&lt;li&gt;排障三板斧：&lt;code&gt;istioctl analyze&lt;/code&gt;（静态检查配置）、&lt;code&gt;istioctl proxy-config&lt;/code&gt;（看 Envoy 实际生效配置）、Envoy 的 &lt;code&gt;:15000/stats&lt;/code&gt; 和 &lt;code&gt;:15000/clusters&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;新人别一上来就上全量 Istio。先在非核心 namespace 开注入练手，理解 VirtualService 再说。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十云上落地阿里云-ack-为主aws-eks-对照&#34;&gt;十、云上落地：阿里云 ACK 为主，AWS EKS 对照&lt;/h2&gt;
&lt;p&gt;自己从零搭一套生产级 K8s（etcd 高可用、证书、网络插件）成本极高，生产环境几乎都用云厂商托管服务。&lt;/p&gt;
&lt;h3 id=&#34;101-阿里云-ack-关键点&#34;&gt;10.1 阿里云 ACK 关键点&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    subgraph 阿里云托管[&amp;#34;ACK 托管层（免费/Pro）&amp;#34;]
        MA[控制平面：apiserver/etcd/scheduler&amp;lt;br/&amp;gt;阿里云托管，SLA 保障]
    end
    subgraph 你的账号[&amp;#34;你的 VPC 内&amp;#34;]
        N1[ECS 节点池 1&amp;lt;br/&amp;gt;按量/抢占式]
        N2[ECS 节点池 2&amp;lt;br/&amp;gt;弹性伸缩 ESS]
    end
    MA --&amp;gt; N1
    MA --&amp;gt; N2
    N1 -.对接.-&amp;gt; CLB[CLB/ALB&amp;lt;br/&amp;gt;负载均衡]
    N1 -.对接.-&amp;gt; NAS[云盘 ESSD / NAS&amp;lt;br/&amp;gt;持久存储]
    N1 -.对接.-&amp;gt; SLS[SLS 日志 / ARMS 监控&amp;lt;br/&amp;gt;可观测]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;实践经验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;网络插件选 Terway&lt;/strong&gt;：Pod 直接用 VPC 弹性网卡 IP，无 overlay 开销、conntrack 压力小，还能配 NetworkPolicy；Flannel 适合小规模简单场景。Terway 模式下&lt;strong&gt;规划 VPC 网段要留够&lt;/strong&gt;，每个 Pod 占一个真实 IP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;节点池 + 弹性伸缩&lt;/strong&gt;：按业务波峰波谷自动扩缩节点；抢占式实例（Spot）跑无状态任务能省 70% 成本，但要容忍随时被回收。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress 用 ALB Ingress Controller&lt;/strong&gt;：比自建 Nginx Ingress 省心，ALB 本身按量弹性，且和 ACK 深度集成。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日志和监控开箱即用&lt;/strong&gt;：日志服务 SLS（采集容器 stdout 和文件日志）、ARMS Prometheus 监控，别再自己搭 ELK 了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;镜像仓库 ACR&lt;/strong&gt;：用 ACR EE（企业版）+ ACK 免密拉取插件，避免在 YAML 里配 imagePullSecret。&lt;/li&gt;
&lt;li&gt;存储选 &lt;strong&gt;CNFS/云盘 ESSD（有状态单副本）+ NAS（共享文件）+ OSS（静态资源、备份）&lt;/strong&gt; 的组合。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;102-aws-eks-速查对照&#34;&gt;10.2 AWS EKS 速查对照&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;需求&lt;/th&gt;
					&lt;th&gt;阿里云 ACK&lt;/th&gt;
					&lt;th&gt;AWS EKS&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;网络插件&lt;/td&gt;
					&lt;td&gt;Terway（VPC 网卡直连）&lt;/td&gt;
					&lt;td&gt;aws-vpc-cni（同理）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;七层入口&lt;/td&gt;
					&lt;td&gt;ALB Ingress&lt;/td&gt;
					&lt;td&gt;AWS Load Balancer Controller (ALB/NLB)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;块存储&lt;/td&gt;
					&lt;td&gt;云盘 ESSD&lt;/td&gt;
					&lt;td&gt;EBS (gp3)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;共享文件存储&lt;/td&gt;
					&lt;td&gt;NAS&lt;/td&gt;
					&lt;td&gt;EFS&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;日志&lt;/td&gt;
					&lt;td&gt;SLS&lt;/td&gt;
					&lt;td&gt;CloudWatch&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;监控&lt;/td&gt;
					&lt;td&gt;ARMS Prometheus&lt;/td&gt;
					&lt;td&gt;Managed Prometheus / Grafana&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;镜像仓库&lt;/td&gt;
					&lt;td&gt;ACR&lt;/td&gt;
					&lt;td&gt;ECR&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;无服务器节点&lt;/td&gt;
					&lt;td&gt;虚拟节点（ECI）&lt;/td&gt;
					&lt;td&gt;Fargate&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;概念完全互通，换云只是换 Controller 和注解。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十一典型问题排查手册&#34;&gt;十一、典型问题排查手册&lt;/h2&gt;
&lt;h3 id=&#34;111-通用排查流程&#34;&gt;11.1 通用排查流程&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[发现问题] --&amp;gt; B{Pod 状态？&amp;lt;br/&amp;gt;kubectl get pods}
    B --&amp;gt;|Pending| C[kubectl describe pod&amp;lt;br/&amp;gt;看 Events]
    C --&amp;gt; C1{原因}
    C1 --&amp;gt;|资源不足| C2[加节点/降 requests&amp;lt;br/&amp;gt;或开弹性伸缩]
    C1 --&amp;gt;|存储挂载失败| C3[检查 PVC 绑定/可用区]
    C1 --&amp;gt;|镜像拉取失败| C4[ImagePullBackOff：查镜像名/凭证/网络]
    B --&amp;gt;|CrashLoopBackOff| D[kubectl logs --previous&amp;lt;br/&amp;gt;看上一个容器的临终日志]
    D --&amp;gt; D1{原因}
    D1 --&amp;gt;|应用自身报错| D2[修代码/配置]
    D1 --&amp;gt;|Exit 137| D3[OOMKilled：调大 memory limit&amp;lt;br/&amp;gt;或查内存泄漏]
    D1 --&amp;gt;|Exit 143| D4[被优雅终止：查探针/滚动更新]
    B --&amp;gt;|Running 但访问异常| E[分层验证]
    E --&amp;gt; E1[1. 进 Pod 内 curl localhost 通吗？]
    E1 --&amp;gt; E2[2. curl Pod IP 通吗？]
    E2 --&amp;gt; E3[3. curl Service ClusterIP 通吗？&amp;lt;br/&amp;gt;查 Endpoints 是否有后端]
    E3 --&amp;gt; E4[4. Ingress 域名通吗？&amp;lt;br/&amp;gt;查 Controller 日志]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;112-高频问题速查表&#34;&gt;11.2 高频问题速查表&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;症状&lt;/th&gt;
					&lt;th&gt;最常见原因&lt;/th&gt;
					&lt;th&gt;第一动作&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;ImagePullBackOff&lt;/td&gt;
					&lt;td&gt;镜像名/tag 错、私有仓库无凭证、节点无公网&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;describe pod&lt;/code&gt; 看 Events 报错原文&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CrashLoopBackOff&lt;/td&gt;
					&lt;td&gt;配置错、依赖没起来、探针误杀&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;kubectl logs --previous&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;OOMKilled (137)&lt;/td&gt;
					&lt;td&gt;内存 limit 太小或泄漏&lt;/td&gt;
					&lt;td&gt;看监控曲线，调 limit；Java 服务注意堆外内存&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Pod 一直 Pending&lt;/td&gt;
					&lt;td&gt;资源不够、PVC 未绑定、污点/亲和性&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;describe pod&lt;/code&gt; 看调度失败原因&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;服务间偶发超时&lt;/td&gt;
					&lt;td&gt;conntrack 满、DNS 慢、TIME_WAIT 耗尽&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;ss -s&lt;/code&gt;、CoreDNS 日志、查连接池配置&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;滚动更新后 502&lt;/td&gt;
					&lt;td&gt;没配 readinessProbe / 没处理 SIGTERM 优雅退出&lt;/td&gt;
					&lt;td&gt;配探针 + preStop + 应用优雅关闭&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ConfigMap 改了不生效&lt;/td&gt;
					&lt;td&gt;环境变量注入 / subPath 挂载&lt;/td&gt;
					&lt;td&gt;重启 Pod 或改为目录挂载&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;跨可用区挂载失败&lt;/td&gt;
					&lt;td&gt;云盘和节点不在同一 AZ&lt;/td&gt;
					&lt;td&gt;WaitForFirstConsumer + 拓扑约束&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;113-优雅退出滚动更新不断流的完整姿势&#34;&gt;11.3 优雅退出：滚动更新不断流的完整姿势&lt;/h3&gt;
&lt;p&gt;Pod 被删除时的标准流程（很多人只知道一半）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant K as kubelet
    participant P as Pod
    participant EP as Endpoints

    K-&amp;gt;&amp;gt;EP: 把 Pod 从 Service 摘除（不再接新流量）
    K-&amp;gt;&amp;gt;P: 执行 preStop 钩子（如 sleep 5，等摘流量生效）
    K-&amp;gt;&amp;gt;P: 发送 SIGTERM
    P-&amp;gt;&amp;gt;P: 应用应：停止接新请求，处理完存量请求后退出
    Note over K,P: 超过 terminationGracePeriodSeconds（默认30s）
    K-&amp;gt;&amp;gt;P: SIGKILL 强杀&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三个缺一不可：readinessProbe 保证新 Pod ready 才切流量；preStop/优雅关闭保证老 Pod 处理完存量请求；&lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; 给够时间。Java/Go 服务请确认框架注册了 SIGTERM 处理器（Spring Boot 的 &lt;code&gt;server.shutdown=graceful&lt;/code&gt;、Go 的 &lt;code&gt;signal.Notify&lt;/code&gt;）。&lt;/p&gt;
&lt;h3 id=&#34;114-kubectl-排障速查&#34;&gt;11.4 kubectl 排障速查&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl get pods -o wide -n &amp;lt;ns&amp;gt;          &lt;span style=&#34;color:#75715e&#34;&gt;# 看状态和所在节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl describe pod &amp;lt;pod&amp;gt; -n &amp;lt;ns&amp;gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# 看 Events（排障第一步）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl logs &amp;lt;pod&amp;gt; -c &amp;lt;container&amp;gt; --previous   &lt;span style=&#34;color:#75715e&#34;&gt;# 崩溃前的日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl get endpoints &amp;lt;svc&amp;gt; -n &amp;lt;ns&amp;gt;       &lt;span style=&#34;color:#75715e&#34;&gt;# Service 后面到底有没有 Pod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl exec -it &amp;lt;pod&amp;gt; -- sh              &lt;span style=&#34;color:#75715e&#34;&gt;# 进容器内验证网络&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl run debug --rm -it --image&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;busybox:1.36 -- sh   &lt;span style=&#34;color:#75715e&#34;&gt;# 临时网络调试 Pod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl top pod / kubectl top node        &lt;span style=&#34;color:#75715e&#34;&gt;# 资源占用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl get events --sort-by&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;.lastTimestamp -n &amp;lt;ns&amp;gt;     &lt;span style=&#34;color:#75715e&#34;&gt;# 按时间看事件&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;十二cicdjenkins-与-k8s-打通实现自动化部署与验证&#34;&gt;十二、CI/CD：Jenkins 与 K8s 打通，实现自动化部署与验证&lt;/h2&gt;
&lt;p&gt;手工 &lt;code&gt;kubectl apply&lt;/code&gt; 只适合学习。生产环境的标准做法是把「构建 → 推镜像 → 部署 → 验证 → 失败回滚」串成流水线，代码合并后自动完成发布。&lt;/p&gt;
&lt;h3 id=&#34;121-整体流水线长什么样&#34;&gt;12.1 整体流水线长什么样&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    Dev[开发者 git push] --&amp;gt; Hook[Webhook 触发 Jenkins]
    Hook --&amp;gt; Build[阶段1: 构建&amp;lt;br/&amp;gt;单测 + Docker 镜像]
    Build --&amp;gt; Push[阶段2: 推送镜像&amp;lt;br/&amp;gt;到 ACR/ECR&amp;lt;br/&amp;gt;tag = git commit]
    Push --&amp;gt; Deploy[阶段3: 部署&amp;lt;br/&amp;gt;kubectl set image&amp;lt;br/&amp;gt;更新 K8s]
    Deploy --&amp;gt; Verify[阶段4: 验证&amp;lt;br/&amp;gt;rollout status + 冒烟测试]
    Verify --&amp;gt;|成功| Done[发布完成&amp;lt;br/&amp;gt;钉钉/邮件通知]
    Verify --&amp;gt;|失败| RB[自动回滚&amp;lt;br/&amp;gt;rollout undo] --&amp;gt; Alarm[告警通知]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;关键认知：&lt;strong&gt;K8s 侧不需要装任何 Jenkins 插件或 Agent 也能被部署&lt;/strong&gt;——Jenkins 只要有目标集群的 kubeconfig（访问凭证），用 &lt;code&gt;kubectl&lt;/code&gt; 就能驱动 apiserver 完成一切。打通的本质就两件事：凭证怎么管、流水线脚本怎么写。&lt;/p&gt;
&lt;h3 id=&#34;122-jenkins-的两种部署形态&#34;&gt;12.2 Jenkins 的两种部署形态&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;形态&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
					&lt;th&gt;适用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Jenkins 在集群外（独立服务器/另一集群）&lt;/td&gt;
					&lt;td&gt;通过 kubeconfig 远程操作业务集群&lt;/td&gt;
					&lt;td&gt;一套 Jenkins 管多个集群，最常见&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Jenkins 跑在 K8s 集群内&lt;/td&gt;
					&lt;td&gt;配合 Kubernetes 插件，每次构建动态拉起一个 Pod 作为构建 agent，用完即销毁&lt;/td&gt;
					&lt;td&gt;构建任务多、想弹性利用集群资源&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    subgraph Jenkins侧[&amp;#34;Jenkins（集群外或集群内）&amp;#34;]
        J[Jenkins Master]
        C1[凭据1: kubeconfig&amp;lt;br/&amp;gt;Secret file 类型]
        C2[凭据2: ACR 账号&amp;lt;br/&amp;gt;Username/Password 类型]
        J --&amp;gt; C1
        J --&amp;gt; C2
    end
    subgraph 阿里云[&amp;#34;阿里云&amp;#34;]
        J --&amp;gt;|docker push| ACR[ACR 镜像仓库]
        J --&amp;gt;|&amp;#34;kubectl（走内网 API Server 端点）&amp;#34;| API[ACK apiserver]
        API --&amp;gt; Pod[滚动更新业务 Pod]
        Pod --&amp;gt;|免密拉取| ACR
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;推荐做法：Jenkins 与 ACK 在&lt;strong&gt;同一 VPC 内&lt;/strong&gt;，kubectl 直接走 API Server 的&lt;strong&gt;内网端点&lt;/strong&gt;（安全且免公网流量）；镜像仓库用 ACR 企业版 + ACK 免密拉取，业务侧零凭证。&lt;/p&gt;
&lt;h3 id=&#34;123-最小权限的部署凭证别把-admin-kubeconfig-给-jenkins&#34;&gt;12.3 最小权限的部署凭证：别把 admin kubeconfig 给 Jenkins&lt;/h3&gt;
&lt;p&gt;这是 CI/CD 安全的第一课。给 Jenkins 一个&lt;strong&gt;只能操作特定 namespace、只能改 Deployment&lt;/strong&gt; 的 ServiceAccount：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ServiceAccount&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;jenkins-deployer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Role 只在 prod namespace 内生效&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deployer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;apiGroups&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;apps&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;resources&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;deployments&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;verbs&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;get&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;list&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;watch&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;patch&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;update&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;RoleBinding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;jenkins-deployer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;subjects&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ServiceAccount&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;jenkins-deployer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;roleRef&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Role&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deployer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;apiGroup&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rbac.authorization.k8s.io&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;然后为这个 SA 生成 token，做成只含该 token 的 kubeconfig，存进 Jenkins 的 &lt;strong&gt;Secret file 凭据&lt;/strong&gt;。好处：凭证泄露也只影响一个 namespace 的 Deployment，且可以随时 &lt;code&gt;kubectl delete rolebinding&lt;/code&gt; 吊销。&lt;/p&gt;
&lt;h3 id=&#34;124-完整-jenkinsfile-示例&#34;&gt;12.4 完整 Jenkinsfile 示例&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipeline &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    agent any
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    environment &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        IMAGE     &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;registry.cn-hangzhou.aliyuncs.com/mycompany/order-svc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        TAG       &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${env.GIT_COMMIT.take(8)}&amp;#34;&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// 用 git commit 做 tag，可回滚可追溯
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        KUBECONFIG &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; credentials&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ack-prod-kubeconfig&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// Secret file 凭据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stages &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;构建&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;mvn -q test&amp;#39;&lt;/span&gt;                       &lt;span style=&#34;color:#75715e&#34;&gt;// 先跑单测，挂了直接终止
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;docker build -t ${IMAGE}:${TAG} .&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;推送镜像&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                withCredentials&lt;span style=&#34;color:#f92672&#34;&gt;([&lt;/span&gt;usernamePassword&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    credentialsId: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;acr-account&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    usernameVariable: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;U&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; passwordVariable: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;P&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)])&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;echo $P | docker login registry.cn-hangzhou.aliyuncs.com -u $U --password-stdin&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;docker push ${IMAGE}:${TAG}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;部署到 ACK&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubectl --kubeconfig=$KUBECONFIG -n prod \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    set image deployment/order-svc order-svc=${IMAGE}:${TAG}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;验证&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;// 阻塞等待滚动更新完成；超时/失败会非零退出
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubectl --kubeconfig=$KUBECONFIG -n prod \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    rollout status deployment/order-svc --timeout=300s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#75715e&#34;&gt;// 冒烟测试：走内网 SLB 打健康检查和关键接口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;curl -fsS --retry 5 --retry-delay 3 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    http://order-svc-internal.prod.svc.cluster.local/healthz || exit 1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    post &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        failure &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// 部署验证失败自动回滚到上一个版本
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubectl --kubeconfig=$KUBECONFIG -n prod \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                rollout undo deployment/order-svc || true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;125-验证环节的正确姿势&#34;&gt;12.5 「验证」环节的正确姿势&lt;/h3&gt;
&lt;p&gt;很多流水线只做到 &lt;code&gt;kubectl apply&lt;/code&gt; 就算「发布成功」，这是自欺欺人。&lt;code&gt;apply&lt;/code&gt; 返回成功只代表 apiserver 收下了 YAML，Pod 起不起得来是另一回事。可靠的验证链：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;rollout status 阻塞等待&lt;/strong&gt;：确认所有新副本 ready（背后靠的就是 readinessProbe——再次体现探针的重要性）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;冒烟测试&lt;/strong&gt;：对真实入口打 &lt;code&gt;/healthz&lt;/code&gt; 和一两个核心业务接口，确认「能起来」且「能干活」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;失败自动回滚&lt;/strong&gt;：&lt;code&gt;rollout undo&lt;/code&gt; 依赖旧 ReplicaSet 还在（&lt;code&gt;revisionHistoryLimit&lt;/code&gt; 别设 0）且镜像 tag 可追溯——这就是前面说「不要用 latest」的根本原因。&lt;/li&gt;
&lt;li&gt;更严谨的做法是&lt;strong&gt;灰度验证&lt;/strong&gt;：先用 Istio 把 5% 流量切到新版本观察指标，确认无误再全量（见第九章）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;126-cicd-避坑清单&#34;&gt;12.6 CI/CD 避坑清单&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;镜像 tag 必须唯一可追溯&lt;/strong&gt;：git commit 短哈希或构建号，杜绝 &lt;code&gt;latest&lt;/code&gt;，否则回滚时根本不知道该回滚到哪个镜像。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;凭据最小化 + 分环境&lt;/strong&gt;：dev/staging/prod 用不同的 SA 和 Jenkins 凭据，prod 的部署动作加人工审批（Jenkins 的 &lt;code&gt;input&lt;/code&gt; 步骤）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;声明式优先&lt;/strong&gt;：服务变多后，把 &lt;code&gt;kubectl set image&lt;/code&gt; 换成 Helm/Kustomize（YAML 也进 Git 仓库，可审计可 diff）；再进一步上 &lt;strong&gt;GitOps（ArgoCD/Flux）&lt;/strong&gt;——Jenkins 只负责构建推镜像，部署由集群内的 ArgoCD 从 Git 仓库自动同步，Jenkins 不再需要生产集群的任何凭证，安全性大幅提升。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jenkins 到集群的网络&lt;/strong&gt;：kubectl 走内网 API Server 端点；如果 Jenkins 在另一个 VPC，用云企业网/对等连接打通，不要把 API Server 暴露公网给 Jenkins 用。&lt;/li&gt;
&lt;li&gt;构建产物（镜像）与部署配置分离管理，避免「同一个 tag 被覆盖推送」导致回滚失效——ACR 里给 prod 仓库开&lt;strong&gt;镜像 tag 不可变&lt;/strong&gt;（immutable）开关。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十三学习路线建议&#34;&gt;十三、学习路线建议&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一周&lt;/strong&gt;：本地装 minikube 或 kind，把 Deployment/Service/ConfigMap 各玩一遍，亲手制造一次 CrashLoopBackOff 并修好它。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第二周&lt;/strong&gt;：搞懂网络——Pod 网络、Service 转发（iptables）、Ingress、NetworkPolicy 内外网隔离，配合 &lt;code&gt;tcpdump&lt;/code&gt;/&lt;code&gt;ss&lt;/code&gt; 在容器里抓包看真实流量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三周&lt;/strong&gt;：上云。开一套阿里云 ACK（按量付费，玩完就删），跑通「ACR 推镜像 → ACK 部署 → ALB Ingress 暴露 → NAS 挂共享盘 → SLS 看日志」全流程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第四周&lt;/strong&gt;：打通 CI/CD。用 Jenkins + 最小权限 SA 实现「推代码 → 自动构建 → 自动部署 → rollout 验证 → 失败回滚」完整流水线。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;进阶&lt;/strong&gt;：Helm 包管理、HPA 自动扩缩容、Istio 灰度发布、GitOps（ArgoCD）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后送一句《Kubernetes 权威指南》反复强调的心法：&lt;strong&gt;K8s 的一切都是声明式的——你描述「我要什么」，控制器负责「做到」&lt;/strong&gt;。排查问题时永远先问：期望状态（YAML/Spec）是什么？实际状态（Status/Events）是什么？两者的差距就是答案所在。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《Kubernetes 权威指南：从 Docker 到 Kubernetes 实践全接触（第 4 版）》龚正 等&lt;/li&gt;
&lt;li&gt;《Kubernetes in Action》Marko Lukša&lt;/li&gt;
&lt;li&gt;《Unix 网络编程 卷1》W. Richard Stevens（网络排障内功）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kubernetes.io/zh-cn/docs/&#34;&gt;Kubernetes 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://help.aliyun.com/product/85222.html&#34;&gt;阿里云 ACK 文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
</description>
    </item>
    <item>
      <title>《设计数据密集型应用》学习总结：存量、流量、回路——每个设计决策都是三者的权衡</title>
      <link>https://ftmao.com/blog/bookstore/ddia-book-notes/</link>
      <pubDate>Tue, 08 Aug 2023 10:40:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/ddia-book-notes/</guid>
      <description>&lt;h1 id=&#34;设计数据密集型应用学习总结存量流量回路每个设计决策都是三者的权衡&#34;&gt;《设计数据密集型应用》学习总结：存量、流量、回路——每个设计决策都是三者的权衡&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;目标读者：写过 CRUD、用过 MySQL/Redis/Kafka，但说不清它们&amp;quot;为什么这么设计&amp;quot;的工程师。&lt;br&gt;
参考书籍：Martin Kleppmann《Designing Data-Intensive Applications》（DDIA，那只野猪书）&lt;br&gt;
阅读目标：不背目录，而是拿到一个能套在任何存储/中间件上的分析框架——存量、流量、回路。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;0-写在前面别背组件背权衡&#34;&gt;0. 写在前面：别背组件，背权衡&lt;/h2&gt;
&lt;p&gt;市面上的&amp;quot;中间件教程&amp;quot;有个通病：教你 MySQL 怎么用、Redis 怎么用、Kafka 怎么用，但从不告诉你&lt;strong&gt;为什么它们长得这么不一样&lt;/strong&gt;。同样是存数据，Redis 敢把数据放内存，MySQL 不敢；同样是日志，Kafka 能当数据库用，而大多数人只拿它当管道。&lt;/p&gt;
&lt;p&gt;DDIA 这本书最值钱的地方，就是把这些&amp;quot;为什么&amp;quot;串成了一条线。读完我把它提炼成三个词：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存量（State）&lt;/strong&gt;：数据怎么&lt;strong&gt;存&lt;/strong&gt;——存储引擎（LSM/B-Tree）、编码格式、行存列存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流量（Flow）&lt;/strong&gt;：数据怎么&lt;strong&gt;动&lt;/strong&gt;——复制、分区、派生数据、流处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回路（Loop）&lt;/strong&gt;：系统怎么&lt;strong&gt;自我纠错&lt;/strong&gt;——事务、隔离级别、共识、线性一致性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话理解：&lt;strong&gt;任何一个数据系统的设计决策，都是在存量成本、流量吞吐、回路强度三者之间做取舍。&lt;/strong&gt; 你要强一致（回路强），就得牺牲可用性或延迟（流量贵）；你要写入快（流量大），就得接受读时合并或后台压缩的代价（存量结构变复杂）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 0&lt;/strong&gt;：读完这本书最大的误区是&amp;quot;上来就选最强的一致性&amp;quot;。Kleppmann 自己说过大意如此的话：一致性模型的选择应该由&lt;strong&gt;业务能不能容忍异常&lt;/strong&gt;决定，而不是由工程师的洁癖决定。能容忍就用最终一致，性能白捡。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;和博客已有的 Redis/MySQL/MQ 笔记是&amp;quot;原理 vs 用法&amp;quot;的关系：那些笔记讲&amp;quot;这个命令怎么用、这个参数怎么调&amp;quot;，这篇讲&amp;quot;这个组件为什么被设计成这样&amp;quot;。建议搭配阅读——看完这篇再回头看 Redis 的 RDB/AOF、MySQL 的 InnoDB redo log、Kafka 的副本协议，会有&amp;quot;原来如此&amp;quot;的感觉。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第一部分-存量数据怎么存&#34;&gt;第一部分 存量：数据怎么存&lt;/h2&gt;
&lt;h3 id=&#34;1-lsm-tree-vs-b-tree写的快和读的快不可兼得&#34;&gt;1. LSM-Tree vs B-Tree：写的快和读的快，不可兼得&lt;/h3&gt;
&lt;p&gt;数据库索引结构就两大流派，区别一句话：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;B-Tree&lt;/strong&gt;：读的时候顺藤摸瓜，树的高度就是磁盘寻道次数。写入是&amp;quot;就地更新&amp;quot;——找到那一页，改掉。MySQL InnoDB 就是这个。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LSM-Tree&lt;/strong&gt;：写入只往内存里的有序结构（Memtable）追加，内存写满了就整体刷到磁盘变成一个 &lt;strong&gt;SSTable&lt;/strong&gt;（Sorted String Table，一段有序的文件）。读取时从最新的 SSTable 往最旧的依次找。LevelDB、RocksDB、Cassandra、HBase 都是这个。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;SSTable 合并（compaction）是 LSM 的灵魂&lt;/strong&gt;。因为同一个 key 的新旧版本散落在多个 SSTable 里，后台必须定期把它们归并排序、去重、丢掉被删除的墓碑标记，否则文件越积越多，读放大失控。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    W[&amp;#34;写入请求&amp;#34;] --&amp;gt; L[&amp;#34;WAL 追加日志&amp;lt;br/&amp;gt;（崩溃恢复用）&amp;#34;]
    L --&amp;gt; M[&amp;#34;Memtable&amp;lt;br/&amp;gt;（内存有序表）&amp;#34;]
    M --&amp;gt;|&amp;#34;写满阈值&amp;#34;| S1[&amp;#34;SSTable 1&amp;lt;br/&amp;gt;（不可变文件）&amp;#34;]
    M --&amp;gt;|&amp;#34;再写满&amp;#34;| S2[&amp;#34;SSTable 2&amp;#34;]
    M --&amp;gt;|&amp;#34;又写满&amp;#34;| S3[&amp;#34;SSTable 3&amp;#34;]
    S1 --&amp;gt; C[&amp;#34;后台 Compaction&amp;lt;br/&amp;gt;归并排序 + 去重 + 清墓碑&amp;#34;]
    S2 --&amp;gt; C
    S3 --&amp;gt; C
    C --&amp;gt; S4[&amp;#34;合并后的大 SSTable&amp;lt;br/&amp;gt;（旧文件丢弃）&amp;#34;]
    R[&amp;#34;读取请求&amp;#34;] --&amp;gt; M
    R --&amp;gt; S3
    R --&amp;gt; S2
    R --&amp;gt; S1
    R --&amp;gt;|&amp;#34;配合布隆过滤器&amp;lt;br/&amp;gt;跳过不含该 key 的文件&amp;#34;| MISS[&amp;#34;快速判不存在&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;小数字例子感受写放大：假设每写 1 GB 数据，经过 3 层 compaction，每层都要重写一遍，实际磁盘写入可能是 5~10 GB。这就是&amp;quot;写放大&amp;quot;，也是 RocksDB 调优的核心战场。&lt;/p&gt;
&lt;p&gt;LSM 读取慢怎么补救？靠&lt;strong&gt;布隆过滤器（Bloom filter）&lt;/strong&gt;：一个很小的内存位图，能告诉你&amp;quot;这个 key 肯定不在这个 SSTable 里&amp;quot;（可能有误报，但绝不漏报）。查一个不存在的 key 时，布隆过滤器直接拦掉，不用真的去翻磁盘文件——这对&amp;quot;查缓存未命中再查库&amp;quot;的场景是救命优化。Cassandra、HBase、RocksDB 全都内置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;怎么选？&lt;/strong&gt; 记住这张对照表：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;维度&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;B-Tree&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;LSM-Tree&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;写入&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;就地更新，写放大相对低但随机 IO&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;顺序追加，吞吐高但有 compaction 写放大&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;读取&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;稳定，一次查找几层树&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;要查多层文件，靠布隆过滤器和缓存补救&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;磁盘碎片&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;页分裂会产生碎片&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;文件整体重写，天然紧凑&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;典型代表&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;InnoDB、PostgreSQL&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;RocksDB、Cassandra、HBase&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一句话理解&lt;/strong&gt;：B-Tree 是&amp;quot;图书馆书架&amp;quot;，书按位置摆好，找书快、插书要挪位置；LSM 是&amp;quot;先记在便签上，攒一批再上架&amp;quot;，记便签飞快，但定期要整理书架，找书得翻几摞便签。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;2-wal所有存储引擎的保命符&#34;&gt;2. WAL：所有存储引擎的&amp;quot;保命符&amp;quot;&lt;/h3&gt;
&lt;p&gt;LSM 写 Memtable、B-Tree 改数据页，都是先动内存。断电怎么办？答案是 &lt;strong&gt;WAL（Write-Ahead Log，预写日志）&lt;/strong&gt;：&lt;strong&gt;任何修改先追加一条日志到磁盘，再改内存结构&lt;/strong&gt;。崩溃后重放日志恢复。&lt;/p&gt;
&lt;p&gt;这个思想无处不在：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;InnoDB 的 redo log 是 WAL；&lt;/li&gt;
&lt;li&gt;PostgreSQL 的 WAL 直接就叫 WAL；&lt;/li&gt;
&lt;li&gt;Redis 的 AOF 本质也是 WAL；&lt;/li&gt;
&lt;li&gt;Kafka 的消息日志、ZooKeeper 的事务日志，全是同一个套路。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一句话理解&lt;/strong&gt;：WAL 就是把&amp;quot;随机写&amp;quot;变成&amp;quot;顺序写&amp;quot;——磁盘顺序追加比随机写快几个数量级，顺便白捡了崩溃恢复能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;3-行存-vs-列存oltp-与-olap-的分水岭&#34;&gt;3. 行存 vs 列存：OLTP 与 OLAP 的分水岭&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;行存&lt;/strong&gt;：一行的所有列连续存放。适合&amp;quot;查一个人的全部信息&amp;quot;（点查、事务），即 OLTP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;列存&lt;/strong&gt;：一列的所有值连续存放。适合&amp;quot;统计一百万人的平均年龄&amp;quot;（聚合、分析），即 OLAP。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为什么列存分析快？小数字例子：一张 100 列的表，分析查询只用 5 列。行存要把 100 列全部从磁盘读出来再丢弃 95 列；列存只读那 5 列，IO 直接省 95%。而且单列数据类型相同，压缩率极高（比如&amp;quot;性别&amp;quot;列去重后就两个值，位图编码一下几乎不占空间）。&lt;/p&gt;
&lt;p&gt;代表：Parquet、ORC、ClickHouse、DuckDB 是列存；MySQL、PostgreSQL 是行存。Snowflake、BigQuery 这类数仓全部站在列存一边。&lt;/p&gt;
&lt;h3 id=&#34;4-编码与演进-schema-变了老数据怎么办&#34;&gt;4. 编码与演进： schema 变了，老数据怎么办&lt;/h3&gt;
&lt;p&gt;存量还有个隐藏维度：&lt;strong&gt;数据格式会随时间演化&lt;/strong&gt;。JSON 灵活但没约束；Protocol Buffers / Thrift / Avro 这类二进制编码通过&lt;strong&gt;字段编号&lt;/strong&gt;实现前后兼容——新增字段给新编号，删掉字段不动旧编号，新老代码就能和平共处。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 1&lt;/strong&gt;：滚动升级时&amp;quot;新代码读老数据&amp;quot;和&amp;quot;老代码读新数据&amp;quot;会同时发生。所以兼容性必须是&lt;strong&gt;双向&lt;/strong&gt;的：向后兼容（新读老）+ 向前兼容（老读新）。只考虑一个方向，升级到一半必出事。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第二部分-流量数据怎么动&#34;&gt;第二部分 流量：数据怎么动&lt;/h2&gt;
&lt;p&gt;单台机器存不下、扛不住、会宕机——这是&amp;quot;流量篇&amp;quot;要解决的三个问题。对应三板斧：&lt;strong&gt;复制&lt;/strong&gt;（同一份数据放多台机器）、&lt;strong&gt;分区&lt;/strong&gt;（不同数据放不同机器）、&lt;strong&gt;派生数据&lt;/strong&gt;（数据从一个系统流向另一个系统）。&lt;/p&gt;
&lt;h3 id=&#34;5-复制主从多主无主三种政治体制&#34;&gt;5. 复制：主从、多主、无主，三种政治体制&lt;/h3&gt;
&lt;p&gt;复制的目标是可用性和读扩展，但引入了全书最核心的矛盾：&lt;strong&gt;副本之间怎么保持一致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;主从复制（单主）&lt;/strong&gt;：所有写请求走 Leader，Leader 把变更日志同步给 Follower。读可以分摊到 Follower。简单、直觉，MySQL、PostgreSQL、MongoDB（副本集）、Kafka 分区副本都是这个思路。&lt;/p&gt;
&lt;p&gt;但&amp;quot;Follower 的数据是旧版本&amp;quot;这件事，会带来一连串用户可感知的诡异现象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;你刚发的朋友圈自己刷新后消失了&lt;/strong&gt; → 需要&lt;strong&gt;读己之写（read-your-writes）&lt;/strong&gt;：用户写完之后，读请求必须走 Leader 或带版本号校验。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;你的余额从 100 变成 80 又变回 100&lt;/strong&gt;（读到了更旧的副本）→ 需要&lt;strong&gt;单调读（monotonic reads）&lt;/strong&gt;：一个用户的连续读请求固定打到同一个副本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聊天里先看到回复再看到原帖&lt;/strong&gt;（分区的数据流向不同副本速度不同）→ 需要&lt;strong&gt;一致前缀读（consistent prefix reads）&lt;/strong&gt;：有因果关系的写入，任何读者都应该按相同顺序看到。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant C as 客户端
    participant L as Leader（主库）
    participant F1 as Follower 1（同步副本）
    participant F2 as Follower 2（异步副本）

    C-&amp;gt;&amp;gt;L: 写 x = 1
    L-&amp;gt;&amp;gt;L: 写本地 WAL 并提交
    L-&amp;gt;&amp;gt;F1: 同步复制：等 F1 确认
    F1--&amp;gt;&amp;gt;L: ACK
    L--&amp;gt;&amp;gt;C: 写入成功
    L-&amp;gt;&amp;gt;F2: 异步复制：后台慢慢发
    Note over F2: 此时 F2 还是旧值 x = 0&amp;lt;br/&amp;gt;复制延迟窗口
    C-&amp;gt;&amp;gt;F2: 读 x
    F2--&amp;gt;&amp;gt;C: x = 0（读到旧数据！）
    Note over C,F2: 这就是&amp;#34;读己之写&amp;#34;被破坏的瞬间&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;同步 vs 异步是个硬权衡&lt;/strong&gt;：全同步，任何一个副本宕机写入就卡死；全异步，Leader 宕机时没同步完的写入直接丢失。实践中的折中是&amp;quot;半同步&amp;quot;——一个副本同步、其余异步（MySQL 的 semi-sync 就是这个）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;多主复制&lt;/strong&gt;：多个节点都能接受写，彼此同步。适合多数据中心就近写入，但&lt;strong&gt;写冲突&lt;/strong&gt;是噩梦——两个机房同时改同一条数据，谁说了算？常见解法：LWW（最后写入者胜，靠时间戳，会丢数据）、CRDT（无冲突数据结构）、应用层解决。CouchDB 走这条路。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;无主复制（Dynamo 风格）&lt;/strong&gt;：没有 Leader，写入时客户端把数据发给 N 个副本中的任意 W 个，读的时候从 R 个里读，只要 &lt;strong&gt;W + R &amp;gt; N&lt;/strong&gt; 就能保证读到最新值（法定人数，quorum）。经典参数 N=3, W=2, R=2。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;经典案例：Amazon Dynamo 论文与购物车&lt;/strong&gt;。2007 年 Amazon 发表 Dynamo 论文，动机极其务实：&lt;strong&gt;购物车绝不能拒绝写入&lt;/strong&gt;——用户加购物车被拒，就是真金白银的损失。所以 Dynamo 选择永远可写（AP），哪怕网络分区时也接受写入，事后再合并冲突版本（著名的&amp;quot;向量时钟&amp;quot;就出自这篇论文）。这篇论文直接催生了 Riak、Cassandra、Voldemort 一整代 NoSQL。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;一句话理解&lt;/strong&gt;：单主是&amp;quot;独裁&amp;quot;，多主是&amp;quot;联邦&amp;quot;，无主是&amp;quot;全民投票&amp;quot;。越民主写入越自由，但事后摆平分歧的成本越高。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;6-分区数据切蛋糕的两种刀法&#34;&gt;6. 分区：数据切蛋糕的两种刀法&lt;/h3&gt;
&lt;p&gt;单机存不下就分区（分片）。两种切法：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;按 key 范围分区&lt;/strong&gt;：字典式切分，A&lt;del&gt;M 一个分区、N&lt;/del&gt;Z 一个分区。范围查询（&amp;ldquo;查 7 月的所有订单&amp;rdquo;）天然高效，但&lt;strong&gt;热点&lt;/strong&gt;问题明显——如果 key 是时间戳，所有新写入全砸在最后一个分区上。Bigtable、HBase 用这套。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;按键的哈希分区&lt;/strong&gt;：key 过一遍哈希函数打散。负载均匀，但范围查询要 scatter-gather 到所有分区，效率骤降。Cassandra、DynamoDB 用这套（Cassandra 实际上是&amp;quot;哈希定分区 + 分区内按 key 排序&amp;quot;的混合体）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;再平衡（rebalancing）&lt;/strong&gt; 是分区系统真正的试金石：加机器、减机器、某分区过热，数据都要搬家。要求搬家期间读写不停、搬迁量最小。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 扩容前[&amp;#34;扩容前：3 节点，每节点 4 个虚拟分区&amp;#34;]
        N1[&amp;#34;节点 A&amp;lt;br/&amp;gt;v0 v1 v2 v3&amp;#34;]
        N2[&amp;#34;节点 B&amp;lt;br/&amp;gt;v4 v5 v6 v7&amp;#34;]
        N3[&amp;#34;节点 C&amp;lt;br/&amp;gt;v8 v9 v10 v11&amp;#34;]
    end
    subgraph 扩容后[&amp;#34;扩容后：新节点 D 加入&amp;lt;br/&amp;gt;只搬走 3 个虚拟分区，其余不动&amp;#34;]
        N1b[&amp;#34;节点 A&amp;lt;br/&amp;gt;v0 v1 v2&amp;#34;]
        N2b[&amp;#34;节点 B&amp;lt;br/&amp;gt;v4 v5 v6&amp;#34;]
        N3b[&amp;#34;节点 C&amp;lt;br/&amp;gt;v8 v9 v10&amp;#34;]
        N4[&amp;#34;节点 D&amp;lt;br/&amp;gt;v3 v7 v11&amp;lt;br/&amp;gt;（从 A/B/C 各搬一个）&amp;#34;]
    end
    扩容前 --&amp;gt;|&amp;#34;固定数量虚拟分区策略&amp;lt;br/&amp;gt;只迁移少量 vNode&amp;#34;| 扩容后&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;主流策略就是图里这个：&lt;strong&gt;固定数量的虚拟分区&lt;/strong&gt;（vNode）——分区数远多于节点数，加节点时从每个老节点&amp;quot;匀&amp;quot;几个虚拟分区过来。Riak、Cassandra 都这么干。反例是取模哈希（hash mod N）：节点数一变，几乎全部 key 都要搬家，这就是一致性哈希要解决的原始问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 2&lt;/strong&gt;：分区键一旦选定，几乎不可更改。用 user_id 做分区键的系统，后来想做&amp;quot;按时间范围全局扫描&amp;quot;，会发现每个查询都要打满全部分区。选分区键时想清楚：&lt;strong&gt;你的主要查询模式是什么，热点在哪&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;7-派生数据一个数据系统的输出是另一个系统的输入&#34;&gt;7. 派生数据：一个数据系统的输出，是另一个系统的输入&lt;/h3&gt;
&lt;p&gt;Kleppmann 有个非常锋利的视角：&lt;strong&gt;缓存、搜索索引、数据仓库，本质上都是派生数据&lt;/strong&gt;——它们都是从某个&amp;quot;真相源&amp;quot;（source of truth）加工出来的冗余副本。&lt;/p&gt;
&lt;p&gt;一旦接受这个设定，很多架构问题豁然开朗：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缓存和数据库不一致？因为派生数据没有和源数据同步更新。&lt;/li&gt;
&lt;li&gt;ES 里的搜索结果和 MySQL 对不上？同理。&lt;/li&gt;
&lt;li&gt;数仓报表延迟一天？因为派生管道是 T+1 的批处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是问题从&amp;quot;怎么让缓存不失效&amp;quot;变成&amp;quot;&lt;strong&gt;派生管道怎么设计&lt;/strong&gt;&amp;quot;，这正是流处理要回答的，下文第 10 节展开。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第三部分-回路系统怎么自我纠错&#34;&gt;第三部分 回路：系统怎么自我纠错&lt;/h2&gt;
&lt;p&gt;存量和流量解决了&amp;quot;存得下、流得动&amp;quot;，剩下的问题是：&lt;strong&gt;当多个客户端并发操作、机器会宕机、网络会分区时，系统凭什么保证数据不出错？&lt;/strong&gt; 这就是回路——一致性保障与反馈机制。&lt;/p&gt;
&lt;h3 id=&#34;8-事务与隔离级别用弱隔离换性能代价是异常&#34;&gt;8. 事务与隔离级别：用&amp;quot;弱隔离&amp;quot;换性能，代价是异常&lt;/h3&gt;
&lt;p&gt;事务（ACID）是数据库给程序员的最大善意：&lt;strong&gt;把一堆操作打包成原子单元，要么全成要么全不成，并发的麻烦我来扛&lt;/strong&gt;。但&amp;quot;我来扛&amp;quot;是有价格的——严格串行执行性能太差，于是数据库发明了隔离级别这个&amp;quot;滑杆&amp;quot;，让你自己选扛多少：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;隔离级别&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;防住的异常&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;防不住的异常&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;谁在用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;读未提交&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;几乎啥都不防&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;脏读、一切&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;基本没人用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;读提交（RC）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;脏读、脏写&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;不可重复读、写倾斜&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Oracle、PostgreSQL 的默认&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;快照隔离（SI）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;上面所有 + 不可重复读&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;写倾斜&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;PostgreSQL 的 RR、MySQL InnoDB 的 RR&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;串行化&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;一切&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;无&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;性能最好时的梦想&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;快照隔离（Snapshot Isolation）&lt;/strong&gt; 值得单独说，因为它是现代数据库的主力。原理是 MVCC（多版本并发控制）：每次写入不覆盖旧值，而是生成新版本，每个事务看到&amp;quot;它开始那一刻&amp;quot;的数据快照。读不阻塞写、写不阻塞读，性能极佳。InnoDB 的 undo log、PostgreSQL 的多版本堆表，都是 MVCC 实现。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;写倾斜（write skew）是快照隔离最大的盲区&lt;/strong&gt;，也是面试和实战最容易踩的坑。小例子：医院排班系统要求&amp;quot;至少有一名医生值班&amp;quot;，当前有 Alice 和 Bob 两人值班。两人同时点&amp;quot;请假&amp;quot;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Alice 的事务读到&amp;quot;2 人值班&amp;quot;，判断&amp;quot;我请假后还剩 1 人，OK&amp;quot;，提交；&lt;/li&gt;
&lt;li&gt;Bob 的事务同样读到&amp;quot;2 人值班&amp;quot;（快照！他看不到 Alice 未提交的修改），同样判断 OK，提交；&lt;/li&gt;
&lt;li&gt;结果：两人都不值班了，约束被破坏。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意这不是脏读也不是不可重复读——每个事务内部看到的数据完全一致，但&lt;strong&gt;两个事务基于同一份过期前提做出了互相冲突的决定&lt;/strong&gt;。解法只有：升级到串行化，或者用 &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; 物化冲突（把隐含的读前提变成显式行锁）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;避坑指南 3&lt;/strong&gt;：凡是有&amp;quot;读取一个约束条件，然后据此决定写入&amp;quot;模式的代码（库存扣减、名额抢占、余额校验），在 RR/快照隔离下都可能写倾斜。别指望 ORM 救你，老老实实 &lt;code&gt;FOR UPDATE&lt;/code&gt; 或上串行化。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;串行化的三条实现路线&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;真的串行执行&lt;/strong&gt;：单线程跑事务（Redis 单线程模型、VoltDB），简单暴力，内存够快时性能惊人；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;两阶段锁（2PL）&lt;/strong&gt;：读写都加锁，性能差但正确，MySQL 的串行化级别就是这么实现的，也是死锁的主要来源；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可串行化快照隔离（SSI）&lt;/strong&gt;：快照隔离之上检测危险读写依赖，有冲突就中止重试。PostgreSQL 9.1+ 的串行化级别用的就是它，是近几十年事务领域最重要的学术成果落地。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;9-共识与线性一致性分布式系统的终极回路&#34;&gt;9. 共识与线性一致性：分布式系统的&amp;quot;终极回路&amp;quot;&lt;/h3&gt;
&lt;p&gt;事务解决的是&amp;quot;单机/单系统内的并发正确性&amp;quot;，共识（consensus）解决的是更难的问题：&lt;strong&gt;一群会宕机、消息会延迟的机器，怎么对&amp;quot;某件事&amp;quot;达成一致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;先纠正一个常见混淆：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;线性一致性（Linearizability）&lt;/strong&gt;：让系统表现得&amp;quot;像只有一份数据&amp;quot;——任何人读到 x=1 之后，不可能再读到 x=0。它是对&lt;strong&gt;单个对象&lt;/strong&gt;的读写一致性承诺。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共识（Consensus）&lt;/strong&gt;：多个节点对&lt;strong&gt;某个值/某条决策&lt;/strong&gt;达成不可逆的一致，典型算法 Paxos、Raft、Zab。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两者的关系：要实现线性一致的存储，背后几乎必然要靠共识算法选主或定序。ZooKeeper（Zab）、etcd（Raft）就是共识系统的工业代表，整个 Kubernetes 的正确性底座就是 etcd 里的那套 Raft。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;经典案例：Google Spanner 与 TrueTime&lt;/strong&gt;。Spanner 要在全球范围提供&lt;strong&gt;外部一致性&lt;/strong&gt;（比线性一致更强，跨事务的全局顺序），最大的拦路虎是：分布式系统里各机器时钟不可靠，你没法靠时间戳判断两个事务谁先谁后。Google 的解法粗暴而硬核——&lt;strong&gt;给数据中心装 GPS 接收机和原子钟&lt;/strong&gt;，把时钟误差压缩到一个已知的小区间（TrueTime API 返回的不是一个时间点，而是&amp;quot;真实时间必然落在的这个区间&amp;quot;）。提交事务时，如果发现误差区间还没过去，就&lt;strong&gt;故意等待&lt;/strong&gt;（commit wait）直到确定。用硬件换确定性，用等待换正确性——这是&amp;quot;回路强度可以花钱买&amp;quot;的最著名示范。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;FLP 不可能定理&lt;/strong&gt;泼一盆冷水：在纯异步网络里，哪怕只有一台机器可能崩溃，也不存在&lt;strong&gt;确定性&lt;/strong&gt;的、保证能终止的共识算法。工程上的出路是引入&amp;quot;部分同步&amp;quot;假设（网络总会恢复）和超时机制——所以 Raft/Paxos 的正确性不依赖时钟，但&lt;strong&gt;活性&lt;/strong&gt;（liveness，能不能往前走）依赖。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;极端案例：比特币与拜占庭容错&lt;/strong&gt;。上面说的共识算法都假设节点&amp;quot;可能宕机但不会撒谎&amp;quot;（崩溃容错 CFT）。比特币面对的是更恶劣的环境：&lt;strong&gt;节点可能作恶&lt;/strong&gt;（拜占庭将军问题）。中本聪的解法是放弃确定性共识，改用工作量证明（PoW）+ 最长链规则做&lt;strong&gt;概率性共识&lt;/strong&gt;——一笔交易等 6 个区块确认，被推翻的概率指数级下降但永不为零。性能极差（比特币全网约 7 TPS，对比 Visa 峰值数万 TPS），换来的是&amp;quot;无许可环境下没人能篡改历史&amp;quot;。这是把回路强度推到极限、把流量吞吐牺牲殆尽的极端案例，也完美印证了我们的框架：&lt;strong&gt;一切都是权衡&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;共识落地前，还有个更现实的问题：故障检测&lt;/strong&gt;。分布式系统判断&amp;quot;某节点死了&amp;quot;的唯一办法是超时——但超时无法区分&amp;quot;节点真死了&amp;quot;和&amp;quot;节点只是 GC 停顿了 30 秒&amp;quot;（Java 应用的 Full GC、虚拟机的 steal time、网络拥塞都会造成这种&amp;quot;假死&amp;quot;）。于是就有了&lt;strong&gt;脑裂（split-brain）&lt;/strong&gt;：老 Leader 以为自己还是 Leader，新 Leader 已经被选出来，两个&amp;quot;主&amp;quot;同时接受写入，数据互相覆盖。&lt;/p&gt;
&lt;p&gt;工程解法是 &lt;strong&gt;fencing token（防护令牌）&lt;/strong&gt;：每次选主，共识系统发一个单调递增的编号，存储层只接受&amp;quot;编号比上次大&amp;quot;的写入。老 Leader 拿着旧编号来写，直接被拒。这个小机制在 ZooKeeper（zxid）、etcd（revision）里都有对应物。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;业界掌故&lt;/strong&gt;：Kleppmann 本人就是分布式锁话题的&amp;quot;圈内名人&amp;quot;——2016 年他发文《How to do distributed locking》批判 Redis 的 Redlock 算法在时钟跳变和进程暂停下不安全，引来 Redis 作者 antirez 的公开反驳，两人来回论战了好几轮，成为分布式领域最著名的公开辩论之一。争论的核心正是上面这些：时钟不可信、暂停不可测，所以任何分布式锁要么上 fencing token，要么干脆用共识系统。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;10-批处理-vs-流处理同一枚硬币的两面&#34;&gt;10. 批处理 vs 流处理：同一枚硬币的两面&lt;/h3&gt;
&lt;p&gt;最后回到流量，看数据处理的两种世界观：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;批处理&lt;/strong&gt;：数据是&lt;strong&gt;有界的&lt;/strong&gt;——一堆文件摆在那，MapReduce/Spark 跑一遍，输出结果。优点是吞吐高、可重跑、容错简单（任务挂了重跑）；缺点是延迟以小时计。Hadoop/MapReduce 的黄金时代（Google 2004 年论文、Yahoo 孵化 Hadoop）就是这套世界观。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流处理&lt;/strong&gt;：数据是&lt;strong&gt;无界的&lt;/strong&gt;——事件源源不断地来，Kafka + Flink/Storm 逐条处理，延迟毫秒级。难点：乱序、恰好一次语义（exactly-once）、容错状态恢复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Lambda 架构&lt;/strong&gt;：流处理快但不准（可能丢、可能重复），批处理准但慢，那就两条都跑——流处理给实时近似结果，批处理定期用精确结果覆盖。维护两套系统、两份代码，运维成本翻倍，是出名的&amp;quot;说起来美、做起来痛&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kappa 架构&lt;/strong&gt;：LinkedIn 的 Jay Kreps（Kafka 作者）提出的简化——&lt;strong&gt;只要流处理一条腿&lt;/strong&gt;。历史数据重算？把 Kafka 的日志保留期拉长，让流处理程序从头重放一遍就行。这个架构能成立的前提，是 Kafka 把&amp;quot;消息队列&amp;quot;做成了&amp;quot;可重放的日志存储&amp;quot;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph 数据源
        DB[(&amp;#34;业务数据库&amp;lt;br/&amp;gt;MySQL&amp;#34;)]
        APP[&amp;#34;应用事件&amp;#34;]
    end
    DB --&amp;gt;|&amp;#34;CDC 变更数据捕获&amp;lt;br/&amp;gt;（Debezium 监听 binlog）&amp;#34;| K[&amp;#34;Kafka&amp;lt;br/&amp;gt;（可重放的日志存储）&amp;#34;]
    APP --&amp;gt; K
    K --&amp;gt; S[&amp;#34;流处理引擎&amp;lt;br/&amp;gt;Flink / Kafka Streams&amp;#34;]
    S --&amp;gt; V1[&amp;#34;派生视图：缓存 Redis&amp;#34;]
    S --&amp;gt; V2[&amp;#34;派生视图：搜索索引 ES&amp;#34;]
    S --&amp;gt; V3[&amp;#34;派生视图：数仓 ClickHouse&amp;#34;]
    K --&amp;gt;|&amp;#34;日志长期保留&amp;lt;br/&amp;gt;需要重算时从头重放&amp;#34;| S
    subgraph KP[&amp;#34;Kappa / 批流一体&amp;#34;]
        S
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这张图里藏着 DDIA 全书最重要的思想之一：&lt;strong&gt;数据库和消息队列正在融合&lt;/strong&gt;。Kafka 是可以当数据库用的日志（有保留期、可重放、有流处理）；反过来，数据库的 binlog/WAL 通过 CDC（Change Data Capture）技术流出来，就变成了消息流。Debezium 监听 MySQL binlog 把每一次行变更变成 Kafka 消息，是当下&amp;quot;派生数据管道&amp;quot;的标准做法。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;经典案例：Kafka 从 LinkedIn 内部管道到行业标准&lt;/strong&gt;。Kafka 诞生时（2010 年前后）只是 LinkedIn 内部的一个活动流（activity stream）和日志管道，目标是扛住&amp;quot;每个用户的每个页面浏览都要记录&amp;quot;的巨大流量。它的关键设计——把消息组织成&lt;strong&gt;分区有序日志&lt;/strong&gt;、消费者自己维护 offset、依赖磁盘顺序写+page cache 而非内存——让它意外地发现：这个东西不只是消息队列，而是&amp;quot;以日志为中心的存储系统&amp;quot;。Confluent 成立后，Kafka 成了流处理事实标准，Kreps 那篇《The Log: What every software engineer should know about real-time data&amp;rsquo;s unifying abstraction》（2013）把&amp;quot;日志是数据系统统一抽象&amp;quot;的思想讲透了——和 DDIA 第 3 章&amp;quot;日志结构存储&amp;quot;一脉相承。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;11-全书串讲用三个词复盘四个案例&#34;&gt;11. 全书串讲：用三个词复盘四个案例&lt;/h2&gt;
&lt;p&gt;现在用&amp;quot;存量、流量、回路&amp;quot;的框架，把本文案例快速过一遍，你会发现它们只是同一个框架的不同参数组合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kafka&lt;/strong&gt;：存量用顺序日志（LSM 的近亲），流量靠分区+副本，回路选择&amp;quot;至少一次/恰好一次可配&amp;quot;的务实路线。→ 流量优先，回路可调。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamo&lt;/strong&gt;：无主复制+永远可写，回路是最终一致+应用层冲突解决。→ 流量（可用性）压倒一切，回路最弱。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spanner&lt;/strong&gt;：全球分区+同步复制，回路用 TrueTime 堆到最强（外部一致性）。→ 回路优先，花钱买。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;比特币&lt;/strong&gt;：回路要求拜占庭容错（最强敌对环境），代价是吞吐 7 TPS。→ 回路极限，流量牺牲殆尽。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;没有银弹，只有取舍。&lt;/strong&gt; 这就是 Kleppmann 反复想说、而无数&amp;quot;中间件选型指南&amp;quot;不会告诉你的事。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;12-带走的清单&#34;&gt;12. 带走的清单&lt;/h2&gt;
&lt;p&gt;读完 DDIA，做系统设计时问自己这几句话：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;存量三问&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;写入多还是读取多？写多读少看 LSM（RocksDB/Cassandra），读多写少看 B-Tree（MySQL），分析查询上列存（ClickHouse/Parquet）。&lt;/li&gt;
&lt;li&gt;崩溃恢复靠什么？找到那个 WAL——它一定存在，可能只是叫别的名字（redo log / AOF / binlog / journal）。&lt;/li&gt;
&lt;li&gt;schema 演进支持双向兼容吗？向前向后都要，滚动升级时缺一不可。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;流量三问&lt;/strong&gt;&lt;/p&gt;
&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;复制延迟用户能感知吗？能，就按需要补读己之写、单调读、一致前缀读，别等用户来报 bug。&lt;/li&gt;
&lt;li&gt;分区键选对了吗？对照主要查询模式和热点，选错分区键≈架构重来。&lt;/li&gt;
&lt;li&gt;缓存/索引/数仓是不是派生数据？是，就把&amp;quot;同步管道&amp;quot;当成一等公民来设计（CDC + 流处理），而不是事后打补丁。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;回路三问&lt;/strong&gt;&lt;/p&gt;
&lt;ol start=&#34;7&#34;&gt;
&lt;li&gt;业务能容忍什么级别的一致性？能用最终一致就别上线性一致，省下的都是真金白银。&lt;/li&gt;
&lt;li&gt;有没有&amp;quot;读约束再写入&amp;quot;的代码？有，检查写倾斜——&lt;code&gt;FOR UPDATE&lt;/code&gt; 或串行化，二选一。&lt;/li&gt;
&lt;li&gt;选主/分布式锁靠什么？别自己造轮子，ZooKeeper/etcd 这类共识系统才是正解（Redis 分布式锁的争议——Martin Kleppmann 本人就是那篇著名批判文章《How to do distributed locking》的作者，还设计了&amp;quot;马丁小花&amp;quot;fencing token 方案）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;最后的避坑清单&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;别把默认隔离级别当成&amp;quot;安全级别&amp;quot;——PostgreSQL/MySQL 的默认都不是串行化，写倾斜在看着你。&lt;/li&gt;
&lt;li&gt;别迷信&amp;quot;分布式 = 高可用&amp;quot;——复制和分区引入的故障模式（脑裂、时钟漂移、复制滞后）比单机复杂一个数量级，Jepsen 测试报告（Kyle Kingsbury 对各大数据库的&amp;quot;虐测&amp;quot;）里全是血泪。&lt;/li&gt;
&lt;li&gt;别在不了解权衡时引用 CAP——CAP 只说&amp;quot;网络分区时一致性和可用性二选一&amp;quot;，而网络不分区时你还有延迟与一致性的权衡（PACELC 才是完整版）。&lt;/li&gt;
&lt;li&gt;时间不可信：进程会 GC 暂停、线程会被调度、时钟会跳变。所有依赖&amp;quot;本地时钟先后关系&amp;quot;的分布式逻辑都值得怀疑——这正是 Spanner 要装原子钟的原因。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;13-结语野猪书为什么是神书&#34;&gt;13. 结语：野猪书为什么是神书&lt;/h2&gt;
&lt;p&gt;市面上讲分布式的书，要么讲理论（一堆证明，读完不会写代码），要么讲产品（一堆配置，换个组件就过时）。DDIA 恰好卡在中间：&lt;strong&gt;它讲的是&amp;quot;不会过时的权衡&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;LSM 和 B-Tree 的取舍，二十年前成立，二十年后还会成立；复制滞后引发的那些&amp;quot;诡异现象&amp;quot;，今天在 MySQL 从库上出现，明天就会在你的新数据库上换个马甲出现。工具会死，权衡永存。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;顺带给个阅读路径建议&lt;/strong&gt;：这本书 12 章不必按顺序啃——&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;日常写业务代码的：先读第 3 章（存储引擎）、第 7 章（事务），性价比最高；&lt;/li&gt;
&lt;li&gt;做中间件/基础架构的：第 5、6 章（复制、分区）和第 9 章（共识）是主菜；&lt;/li&gt;
&lt;li&gt;做数据平台/实时数仓的：第 10、11 章（批处理、流处理）直接对口；&lt;/li&gt;
&lt;li&gt;第 8 章（分布式系统的麻烦）建议所有人都读，它是全书&amp;quot;劝退幻想&amp;quot;的一章，读完你会对时钟和网络产生健康的怀疑。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用一句话收尾，也是这本书给我最大的思维升级：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;不要问&amp;quot;这个系统支持什么特性&amp;quot;，要问&amp;quot;这个系统在存量、流量、回路上各做了什么取舍，这些取舍和我的业务匹配吗&amp;quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;能问出这个问题，这本书就没白读。&lt;/p&gt;
</description>
    </item>
    <item>
      <title>《软件架构：最难的部分》详解：分布式架构的权衡分析实战指南</title>
      <link>https://ftmao.com/blog/bookstore/software-architecture-hard-parts-book-notes/</link>
      <pubDate>Wed, 26 Jul 2023 14:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/software-architecture-hard-parts-book-notes/</guid>
      <description>&lt;h1 id=&#34;软件架构最难的部分详解分布式架构的权衡分析实战指南&#34;&gt;《软件架构：最难的部分》详解：分布式架构的权衡分析实战指南&lt;/h1&gt;
&lt;p&gt;《软件架构：最难的部分》（Software Architecture: The Hard Parts: Modern Trade-Off Analyses for Distributed Architectures）是 O&amp;rsquo;Reilly 在 2021 年出版的一本架构书，四位作者都是业内响当当的人物：Neal Ford（《软件架构 fundamentals》作者之一）、Mark Richards（资深架构师、《软件架构 fundamentals》合著者）、Pramod Sadalage（数据库演进专家）和 Zhamak Dehghani（&amp;ldquo;数据网格&amp;quot;概念的提出者）。&lt;/p&gt;
&lt;p&gt;这本书回答了一个非常现实的问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;当我把一个单体系统拆成微服务时，每一步到底该怎么拆？怎么知道拆得对不对？&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;市面上的架构书大多告诉你&amp;quot;什么是好架构&amp;rdquo;，却很少教你&amp;quot;做决策的过程&amp;quot;。这本书补上了这块空白。它的核心立场是：&lt;strong&gt;架构里不存在&amp;quot;最佳实践&amp;quot;，只存在&amp;quot;权衡分析&amp;quot;（Trade-off Analysis）&lt;/strong&gt;。每一个决策都是在拿一些东西换另一些东西，架构师的工作就是把交换的条件摆清楚。&lt;/p&gt;
&lt;p&gt;本文面向&lt;strong&gt;从未接触过软件架构概念&lt;/strong&gt;的读者，按全书十五章的顺序逐层展开，配合图解和实例，帮你建立完整的知识地图。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-1-章-当没有最佳实践时会发生什么&#34;&gt;第 1 章 当没有&amp;quot;最佳实践&amp;quot;时会发生什么&lt;/h2&gt;
&lt;h3 id=&#34;一切皆权衡&#34;&gt;一切皆权衡&lt;/h3&gt;
&lt;p&gt;作者开篇就泼了一盆冷水：如果有人问你&amp;quot;微服务和单体哪个好&amp;quot;，唯一正确的回答是——&lt;strong&gt;看情况&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;架构决策和买东西很像。假如有人不问预算、不问用途，直接告诉你&amp;quot;买这辆车是最好的&amp;quot;，你肯定会觉得他不靠谱。架构也一样：任何脱离上下文的&amp;quot;最佳实践&amp;quot;都是耍流氓。架构师真正的工作流程是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找到决策涉及的各个维度（性能、成本、耦合度、团队结构……）；&lt;/li&gt;
&lt;li&gt;分析每个维度上的得与失；&lt;/li&gt;
&lt;li&gt;把分析结果呈现给利益相关者，让他们在知情的情况下做选择。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以这本书的副标题才是&amp;quot;现代分布式架构的权衡分析&amp;quot;——&lt;strong&gt;最难的部分不是技术本身，而是权衡&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;贯穿全书的案例sysops-squad-工单系统&#34;&gt;贯穿全书的案例：Sysops Squad 工单系统&lt;/h3&gt;
&lt;p&gt;为了方便讲解，作者虚构了一个贯穿全书的案例。Sysops Squad 是一家 IT 运维支持公司，客户（通常是企业的 IT 部门）遇到设备故障时提交&lt;strong&gt;工单（Ticket）&lt;/strong&gt;，系统把工单分配给合适的运维专家（Sysop），专家上门或远程修复，完成后填写处理报告。&lt;/p&gt;
&lt;p&gt;他们现在用的是一个大单体应用，随着业务增长越来越痛苦：改一个小功能要全量发布、一个模块的 bug 拖垮整个系统、高峰期扩容只能整体扩容。于是团队决定把它拆成微服务——&lt;strong&gt;这本书讲的就是这一路上遇到的每一个&amp;quot;难啃的骨头&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 拆分前[&amp;#34;拆分前：单体应用&amp;#34;]
        M[&amp;#34;Sysops Squad 单体系统&amp;lt;br/&amp;gt;工单 + 用户 + 派单 + 报告 + 计费&amp;lt;br/&amp;gt;共享一个大数据库&amp;#34;]
    end
    subgraph 拆分后[&amp;#34;拆分后：微服务&amp;#34;]
        A[&amp;#34;工单服务&amp;#34;]
        B[&amp;#34;用户服务&amp;#34;]
        C[&amp;#34;派单服务&amp;#34;]
        D[&amp;#34;报告服务&amp;#34;]
        E[&amp;#34;计费服务&amp;#34;]
        DB1[(&amp;#34;工单库&amp;#34;)]
        DB2[(&amp;#34;用户库&amp;#34;)]
        DB3[(&amp;#34;派单库&amp;#34;)]
        DB4[(&amp;#34;报告库&amp;#34;)]
        DB5[(&amp;#34;计费库&amp;#34;)]
        A --- DB1
        B --- DB2
        C --- DB3
        D --- DB4
        E --- DB5
    end
    拆分前 --&amp;gt;|本书要讲的全过程| 拆分后&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;看起来很美好对吧？但魔鬼全在细节里：组件怎么划？数据库怎么拆？服务粒度多细合适？公共代码怎么办？跨服务的事务和数据访问怎么办？——这些就是后面十四章要逐一解决的问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-2-章-识别软件架构中的耦合&#34;&gt;第 2 章 识别软件架构中的耦合&lt;/h2&gt;
&lt;h3 id=&#34;架构量子architecture-quantum&#34;&gt;架构量子（Architecture Quantum）&lt;/h3&gt;
&lt;p&gt;这是全书最重要的概念之一，也是后续所有分析的基本单位。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;架构量子&lt;/strong&gt;：系统中可以&lt;strong&gt;独立部署&lt;/strong&gt;的最小单元，包含它正常运行所必需的一切（代码、数据库、配置等）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;判断一个东西是不是一个架构量子，问三个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;能独立部署吗？&lt;/strong&gt;（改完它不用动别的部分就能上线）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功能内聚性高吗？&lt;/strong&gt;（它内部的代码是围绕同一个业务目标组织的）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;耦合度高吗？&lt;/strong&gt;（量子内部耦合高没关系，量子之间耦合低才是目标）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个单体应用就是一个架构量子；一套拆得干净的微服务，每个服务各自是一个量子；而&amp;quot;服务拆了但数据库还共享&amp;quot;的系统，量子边界其实还卡在数据库上——这是新手最容易掉进去的坑。&lt;/p&gt;
&lt;h3 id=&#34;静态耦合与动态耦合&#34;&gt;静态耦合与动态耦合&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;静态耦合&lt;/strong&gt;：编译期/部署期就能看到的依赖。比如 A 服务调用了共享库里的类，或者两个服务连同一个数据库 schema。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动态耦合&lt;/strong&gt;：运行时才发生的调用关系。比如订单服务运行时调用支付服务的接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;动态耦合由三个维度决定，这是全书反复使用的分析框架：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;维度&lt;/th&gt;
					&lt;th&gt;两个选项&lt;/th&gt;
					&lt;th&gt;权衡点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;通信（Communication）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;同步 / 异步&lt;/td&gt;
					&lt;td&gt;同步简单直观，异步解耦但复杂&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;一致性（Consistency）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;原子 / 最终&lt;/td&gt;
					&lt;td&gt;原子一致安全，最终一致可用性高&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;协调（Coordination）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;编排 / 编制&lt;/td&gt;
					&lt;td&gt;编排好管理，编制无中心瓶颈&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这三个维度像调色盘的三原色，后面讲工作流（第 11 章）和 Saga（第 12 章）时会反复用到。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-3-章-架构模块化&#34;&gt;第 3 章 架构模块化&lt;/h2&gt;
&lt;p&gt;为什么要模块化？作者给出了五个驱动力，每一个都对应一种&amp;quot;不模块化会疼&amp;quot;的场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;可维护性（Maintainability）&lt;/strong&gt;：大泥球代码改一处坏三处，模块化让改动范围可控。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可测试性（Testability）&lt;/strong&gt;：边界清晰的模块可以单独写测试，不用拉起整个系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可部署性（Deployability）&lt;/strong&gt;：小模块发布风险低、频率高；单体发布一次像发射火箭。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性（Scalability）&lt;/strong&gt;：旺季只有&amp;quot;下单&amp;quot;需要扩容，模块化后可以只扩这一个模块。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可用性与容错（Availability/Fault Tolerance）&lt;/strong&gt;：故障被限制在模块内部，不至于全站瘫痪。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;举个生活化的例子：老式组合音响里一个喇叭坏了，整台机器都得拆开修；而现在的家庭影院，功放、音箱、播放器各自独立，坏哪个换哪个——这就是模块化的价值。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-4-章-架构分解&#34;&gt;第 4 章 架构分解&lt;/h2&gt;
&lt;p&gt;拆分之前，先要学会&amp;quot;测量&amp;quot;耦合，而不是凭感觉。&lt;/p&gt;
&lt;h3 id=&#34;传入耦合与传出耦合&#34;&gt;传入耦合与传出耦合&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;传入耦合（Afferent Coupling）&lt;/strong&gt;：有多少别的组件依赖我？——衡量&amp;quot;我有多重要、改动我的风险有多大&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;传出耦合（Efferent Coupling）&lt;/strong&gt;：我依赖多少别的组件？——衡量&amp;quot;我有多容易被别人拖累&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;主序列距离distance-from-the-main-sequence&#34;&gt;主序列距离（Distance from the Main Sequence）&lt;/h3&gt;
&lt;p&gt;这是 Robert C. Martin 提出的一组度量。想象一条坐标轴：横轴是&lt;strong&gt;抽象性&lt;/strong&gt;（组件里抽象类/接口占的比例），纵轴是&lt;strong&gt;不稳定性&lt;/strong&gt;（传出耦合占比）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个组件如果&lt;strong&gt;很具体但很多人依赖它&lt;/strong&gt;（比如工具类被全系统引用），它落在&amp;quot;痛苦区&amp;quot;：难改、一改全崩。&lt;/li&gt;
&lt;li&gt;一个组件如果&lt;strong&gt;很抽象但没人用&lt;/strong&gt;（比如过度设计的框架），它落在&amp;quot;无用区&amp;quot;。&lt;/li&gt;
&lt;li&gt;理想位置在两者之间的对角线（&amp;ldquo;主序列&amp;rdquo;）附近。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些度量不是让你拿计算器天天算，而是提供一个&lt;strong&gt;客观的拆分依据&lt;/strong&gt;：拆分候选的组件，先看它们的耦合度量，优先拆那些传入传出耦合低、边界清晰的——它们是&amp;quot;最容易摘下来的果子&amp;quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-5-章-基于组件的分解模式&#34;&gt;第 5 章 基于组件的分解模式&lt;/h2&gt;
&lt;p&gt;这是全书最&amp;quot;实操&amp;quot;的一章：怎么把一坨单体代码拆成组件？作者给出了一个六步流水线，每一步都有对应的&amp;quot;适应度函数&amp;quot;（自动化检查规则）来保证不跑偏。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    S1[&amp;#34;① 识别并确定组件大小&amp;lt;br/&amp;gt;盘点代码库里有哪些组件、各占多大比例&amp;#34;]
    S2[&amp;#34;② 收集公共组件&amp;lt;br/&amp;gt;把多个组件里重复出现的代码收拢到一处&amp;#34;]
    S3[&amp;#34;③ 扁平化组件&amp;lt;br/&amp;gt;把代码从根命名空间下沉到有明确职责的叶子节点&amp;#34;]
    S4[&amp;#34;④ 确定组件依赖&amp;lt;br/&amp;gt;画出组件依赖图，找出纠缠在一起的循环依赖&amp;#34;]
    S5[&amp;#34;⑤ 创建组件域&amp;lt;br/&amp;gt;把职责相近的组件归入同一个领域分组&amp;#34;]
    S6[&amp;#34;⑥ 创建领域服务&amp;lt;br/&amp;gt;每个领域组对外暴露成一个粗粒度服务&amp;#34;]
    S1 --&amp;gt; S2 --&amp;gt; S3 --&amp;gt; S4 --&amp;gt; S5 --&amp;gt; S6&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;几个步骤的直觉解释：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;第①步&lt;/strong&gt;像搬家前盘点家当：先知道自己有什么。适应度函数可以自动检查&amp;quot;任何组件不得超过代码库总量的 X%&amp;quot;、&amp;ldquo;组件大小不得偏离平均值 N 个标准差&amp;rdquo;，防止拆出巨型组件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第②步&lt;/strong&gt;解决重复：如果&amp;quot;日志工具&amp;quot;&amp;ldquo;日期处理&amp;quot;在十个组件里各抄了一份，先把它们收拢，否则拆开后会十处同步改。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第③步&lt;/strong&gt;针对一种常见病：大量代码堆在根命名空间（比如所有代码都在 &lt;code&gt;com.sysops&lt;/code&gt; 下面不分层），说明系统从来没有真正的结构，必须先给每段代码找到归属。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第④步&lt;/strong&gt;是分水岭：依赖图里纠缠不清的组件（A 调 B、B 调 C、C 又调 A）说明领域边界划分有误，要回到业务层面重新划。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第⑤⑥步&lt;/strong&gt;完成从&amp;quot;代码组件&amp;quot;到&amp;quot;业务服务&amp;quot;的跃迁：同一域内的组件将来部署成一个服务，域与域之间的边界就是服务边界。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;**适应度函数（Fitness Function）**是本书反复强调的治理手段：把架构约束写成自动化测试，放进 CI 流水线。比如&amp;quot;报告域的组件不允许依赖计费域的组件&amp;quot;可以直接写成一条 ArchUnit 测试，谁违反了构建就失败——架构约束从&amp;quot;文档里的愿望&amp;quot;变成&amp;quot;会报警的护栏&amp;rdquo;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-6-章-拆分运营数据&#34;&gt;第 6 章 拆分运营数据&lt;/h2&gt;
&lt;p&gt;代码拆完，真正的硬骨头来了：&lt;strong&gt;数据库&lt;/strong&gt;。很多团队服务拆得很漂亮，底下却还用同一个数据库——等于楼分了户、水电还是一户一表，根本没有真正解耦。&lt;/p&gt;
&lt;h3 id=&#34;拆开数据库的推力与阻力&#34;&gt;拆开数据库的推力与阻力&lt;/h3&gt;
&lt;p&gt;作者在拆数据这件事上建立了一个&amp;quot;天平&amp;quot;模型：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;拆分驱动因素（Data Disintegrators，推着你拆）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;变更控制：一个服务改表结构，不用和别的团队协调。&lt;/li&gt;
&lt;li&gt;连接管理：数据库连接数有限，服务多了必须分库。&lt;/li&gt;
&lt;li&gt;可扩展性：可以按服务单独扩容数据库。&lt;/li&gt;
&lt;li&gt;容错：一个库挂了只影响一个服务。&lt;/li&gt;
&lt;li&gt;架构量子：共享数据库会把量子边界撑破。&lt;/li&gt;
&lt;li&gt;数据库类型优化：不同服务适合不同数据库（工单用关系库，日志用时序库）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;整合因素（Data Integrators，拽着你不让拆）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据关系：外键、JOIN 在跨库之后全部失效。&lt;/li&gt;
&lt;li&gt;数据库事务：跨库没有 ACID 事务，必须面对分布式事务的复杂性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;五步数据库分解法&#34;&gt;五步数据库分解法&lt;/h3&gt;
&lt;p&gt;书里给出了一个务实的渐进式拆分流程，核心思想是&amp;quot;&lt;strong&gt;先逻辑拆，再物理拆&lt;/strong&gt;&amp;quot;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    P1[&amp;#34;① 分析数据库&amp;lt;br/&amp;gt;划分数据域&amp;#34;]
    P2[&amp;#34;② 把表分配到&amp;lt;br/&amp;gt;各数据域&amp;#34;]
    P3[&amp;#34;③ 数据库连接&amp;lt;br/&amp;gt;按域分离&amp;#34;]
    P4[&amp;#34;④ Schema 迁移到&amp;lt;br/&amp;gt;独立数据库服务器&amp;#34;]
    P5[&amp;#34;⑤ 切换到完全独立的&amp;lt;br/&amp;gt;数据库&amp;#34;]
    P1 --&amp;gt; P2 --&amp;gt; P3 --&amp;gt; P4 --&amp;gt; P5&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;关键是第③步：表还没物理分开，就先让各服务只连自己的 schema、只访问自己的表。这一步不动数据，只动代码和连接，风险小、可回滚。等代码层面确认拆分正确了，再做物理迁移（第④⑤步）。这种&amp;quot;小步快跑、每步可退&amp;quot;的思路贯穿全书。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-7-章-服务粒度&#34;&gt;第 7 章 服务粒度&lt;/h2&gt;
&lt;p&gt;拆服务时最纠结的问题：&lt;strong&gt;一个服务到底该多大？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作者又祭出了&amp;quot;天平&amp;quot;分析法——这次叫粒度解耦因素（Granularity Disintegrators，推着你往细拆）与粒度整合因素（Granularity Integrators，拽着你往粗合）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;往细拆的理由（Disintegrators）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务范围与功能&lt;/strong&gt;：一个服务只干一件事。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码波动性&lt;/strong&gt;：如果系统里&amp;quot;促销规则&amp;quot;每周改、&amp;ldquo;订单核心&amp;quot;半年不动，就该把促销拆出去，免得高频改动反复惊扰稳定部分。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性与吞吐量&lt;/strong&gt;：只有搜索服务需要扛流量，拆细了才能单独扩容。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容错&lt;/strong&gt;：非关键功能（比如推荐）挂了不该拖垮下单。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全&lt;/strong&gt;：处理支付的服务需要更高的安全等级和审计，独立出来好管控。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可扩展性（Extensibility）&lt;/strong&gt;：新功能以新服务的形式生长，不改老代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;往粗合的理由（Integrators）：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;数据库事务&lt;/strong&gt;：两个操作必须在同一个 ACID 事务里？那它们最好待在同一个服务里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作流与编制&lt;/strong&gt;：跨服务的编排/编制又慢又难调试，粒度太细会制造大量分布式工作流。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共享代码&lt;/strong&gt;：两个服务共享大量代码，可能说明它们本来就该是一个。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据关系&lt;/strong&gt;：两个服务频繁访问彼此的数据，拆分带来的网络开销和一致性成本可能超过收益。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：Sysops Squad 拆出&amp;quot;工单服务&amp;quot;后，争论要不要把&amp;quot;工单指派&amp;quot;再拆成独立服务。分析后发现：指派逻辑改动频繁（每周调规则）、需要单独的弹性伸缩（早高峰派单量是平时的 5 倍）——两个 disintegrator 都很强；而它与工单共享的表只有一张、没有跨库事务需求——integrator 很弱。于是拆。反过来，&amp;ldquo;工单备注&amp;quot;功能没有任何独立的扩展和发布需求，就留在工单服务里。&lt;/p&gt;
&lt;p&gt;这就是权衡分析的样子：&lt;strong&gt;不是数哪边的理由多，而是掂量哪边的理由重&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-8-章-复用模式&#34;&gt;第 8 章 复用模式&lt;/h2&gt;
&lt;p&gt;多个服务都需要同一段代码（比如审计日志、认证），怎么办？书里给了四种方案：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;模式&lt;/th&gt;
					&lt;th&gt;做法&lt;/th&gt;
					&lt;th&gt;优点&lt;/th&gt;
					&lt;th&gt;缺点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;代码复制&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;每个服务拷一份&lt;/td&gt;
					&lt;td&gt;零耦合、零依赖&lt;/td&gt;
					&lt;td&gt;改 bug 要改 N 处&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;共享库&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;打成 jar/npm 包共用&lt;/td&gt;
					&lt;td&gt;版本可控、无运行时开销&lt;/td&gt;
					&lt;td&gt;版本地狱、异构技术栈难共享&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;共享服务&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;独立部署一个公共服务&lt;/td&gt;
					&lt;td&gt;改动一处全生效、跨语言&lt;/td&gt;
					&lt;td&gt;网络开销、单点风险、变更影响面大&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Sidecar / 服务网格&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;功能放进每个服务的伴生容器&lt;/td&gt;
					&lt;td&gt;跨语言、运维统一&lt;/td&gt;
					&lt;td&gt;需要容器编排基础设施&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;选择思路很直接：&lt;strong&gt;变化越频繁、越偏向业务的代码，越不要共享&lt;/strong&gt;（宁肯复制）；&lt;strong&gt;越稳定、越偏向技术基础设施的能力&lt;/strong&gt;（日志、监控、鉴权），越适合共享或 Sidecar。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：Sysops Squad 的&amp;quot;专家评分算法&amp;quot;每个服务都在用，但它每月都在调优——选了代码复制，各服务按需演进。而&amp;quot;日志采集&amp;quot;用了 Sidecar，统一由运维团队维护。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-9-章-数据所有权与分布式事务&#34;&gt;第 9 章 数据所有权与分布式事务&lt;/h2&gt;
&lt;p&gt;拆库之后立刻撞上两个难题：一张表被两个服务都要写，归谁？一个业务操作要跨两个服务写数据，怎么保证一致性？&lt;/p&gt;
&lt;h3 id=&#34;数据所有权&#34;&gt;数据所有权&lt;/h3&gt;
&lt;p&gt;先看所有权的两种场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;单一所有权&lt;/strong&gt;：一张表只有一个服务写，其他服务最多只读——理想情况。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共同所有权&lt;/strong&gt;：多个服务都要写同一张表——必须拆开归属。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;处理共同所有权有三种技术：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph 表拆分技术[&amp;#34;表拆分：按列拆开&amp;#34;]
        T1[&amp;#34;customer 表&amp;lt;br/&amp;gt;姓名/联系方式 → 档案服务&amp;lt;br/&amp;gt;信用额度/账单地址 → 计费服务&amp;#34;]
    end
    subgraph 数据域技术[&amp;#34;数据域：按行拆开&amp;#34;]
        T2[&amp;#34;ticket 表&amp;lt;br/&amp;gt;进行中的工单 → 工单服务&amp;lt;br/&amp;gt;已归档的历史工单 → 报告服务&amp;#34;]
    end
    subgraph 委派技术[&amp;#34;委派：一主一从&amp;#34;]
        T3[&amp;#34;invoice 表归计费服务所有&amp;lt;br/&amp;gt;工单服务需要时&amp;lt;br/&amp;gt;通过接口委托计费服务写&amp;#34;]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;分布式事务的三种模式&#34;&gt;分布式事务的三种模式&lt;/h3&gt;
&lt;p&gt;跨服务更新数据时，怎么保持一致？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;后台同步模式&lt;/strong&gt;：先各自写，后台进程定时对账、修复不一致。最简单，但不一致窗口最长，适合对实时性无所谓的数据（比如统计报表）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编排请求模式&lt;/strong&gt;：由一个编排者按顺序调用各服务，全部确认后才算完成。一致性好，但慢，且编排者成了耦合中心。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基于事件的模式&lt;/strong&gt;：写本地库后立刻发事件，其他服务异步消费。性能和可用性最好，但要想清楚&amp;quot;事件丢了怎么办&amp;rdquo;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;实例&lt;/strong&gt;：工单完成后要同时更新&amp;quot;工单状态&amp;quot;和&amp;quot;专家积分&amp;rdquo;。如果用编排请求，用户要等两个服务都响应；用事件模式，工单服务先发 &lt;code&gt;TicketCompleted&lt;/code&gt; 事件立即返回，积分服务异步加分——用户快了几百毫秒，代价是积分查询页面可能晚几秒才更新。Sysops Squad 选了后者：&lt;strong&gt;最终一致性换了响应速度&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-10-章-分布式数据访问&#34;&gt;第 10 章 分布式数据访问&lt;/h2&gt;
&lt;p&gt;读操作比写操作更常见：服务 A 需要服务 B 拥有的数据，怎么拿？四种模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务间通信&lt;/strong&gt;：直接调 B 的接口。最简单，但每次查询都是一次网络往返，B 挂了 A 也查不了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;列模式复制&lt;/strong&gt;：把需要的那几列数据复制一份到 A 的数据库（通过消息保持同步）。查询快、无运行时依赖，但数据有延迟、要处理重复治理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复制缓存&lt;/strong&gt;：B 把数据推进共享缓存（如 Redis），A 从缓存读。适合读多写少的参考数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据域&lt;/strong&gt;：把双方都要的数据干脆划给一个专门的&amp;quot;数据域服务&amp;quot;管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选择的关键问题是：&amp;quot;&lt;strong&gt;这份数据 A 需要多新鲜？B 的可用性 A 能承受吗？&lt;/strong&gt;&amp;ldquo;答案不同，模式就不同。比如专家的联系信息（几乎不变）适合复制缓存；工单的实时状态（必须最新）只能走服务间通信。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-11-章-管理分布式工作流&#34;&gt;第 11 章 管理分布式工作流&lt;/h2&gt;
&lt;p&gt;一个业务流程跨多个服务时，谁来当&amp;quot;导演&amp;rdquo;？两种风格：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph 编排[&amp;#34;编排 Orchestration：有个总指挥&amp;#34;]
        O[&amp;#34;编排者&amp;lt;br/&amp;gt;（工单流程服务）&amp;#34;]
        O --&amp;gt; A[&amp;#34;工单服务&amp;#34;]
        O --&amp;gt; B[&amp;#34;派单服务&amp;#34;]
        O --&amp;gt; C[&amp;#34;计费服务&amp;#34;]
        O --&amp;gt; D[&amp;#34;通知服务&amp;#34;]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 编制[&amp;#34;编制 Choreography：舞者跟着音乐各自起舞&amp;#34;]
        A2[&amp;#34;工单服务&amp;#34;] --&amp;gt;|发事件| B2[&amp;#34;派单服务&amp;#34;]
        B2 --&amp;gt;|发事件| C2[&amp;#34;计费服务&amp;#34;]
        C2 --&amp;gt;|发事件| D2[&amp;#34;通知服务&amp;#34;]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;编排（Orchestration）&lt;/strong&gt;：设一个中心编排者，像乐队指挥，告诉每个服务&amp;quot;现在该你了&amp;quot;。流程一目了然、好监控、好报错；但编排者容易变成新的&amp;quot;小单体&amp;quot;，且是所有服务的耦合点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编制（Choreography）&lt;/strong&gt;：没有中心，每个服务监听事件、做完自己的事再发下一个事件，像舞者跟着音乐即兴配合。极度解耦、无单点；但流程分散在各处，&amp;ldquo;现在进行到哪一步了&amp;quot;这种简单问题都很难回答。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有一个关键细节：**工作流状态放哪？**编排模式下状态天然在编排者手里；编制模式下要么设一个专门的&amp;quot;状态所有者&amp;quot;服务，要么忍受状态分散的痛苦。&lt;/p&gt;
&lt;p&gt;经验法则：&lt;strong&gt;流程复杂、分支多、需要人工介入补偿的，用编排；流程线性、追求吞吐量的，用编制&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-12-章-事务性-saga&#34;&gt;第 12 章 事务性 Saga&lt;/h2&gt;
&lt;p&gt;Saga 是分布式世界里替代 ACID 事务的方案：把一个跨服务的大事务拆成一串本地小事务，任何一步失败就按相反顺序执行&lt;strong&gt;补偿操作&lt;/strong&gt;（退款、释放库存……）回滚已完成的步骤。&lt;/p&gt;
&lt;p&gt;书里把第 2 章的三个维度组合起来，给 Saga 模式起了八个别致的名字（括号里的三个字母依次代表：一致性 a=原子/e=最终、通信 s=同步/a=异步、协调 o=编排/c=编制）：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;模式&lt;/th&gt;
					&lt;th&gt;组合&lt;/th&gt;
					&lt;th&gt;适用场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;史诗 Saga&lt;/strong&gt;（a,s,o）&lt;/td&gt;
					&lt;td&gt;原子+同步+编排&lt;/td&gt;
					&lt;td&gt;最接近传统事务，但分布式环境下最难实现&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;电话接龙 Saga&lt;/strong&gt;（a,s,c）&lt;/td&gt;
					&lt;td&gt;原子+同步+编制&lt;/td&gt;
					&lt;td&gt;既要原子性又不想有中心——又慢又脆，慎用&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;童话 Saga&lt;/strong&gt;（e,s,o）&lt;/td&gt;
					&lt;td&gt;最终+同步+编排&lt;/td&gt;
					&lt;td&gt;简单好用的常见选择&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;时间旅行 Saga&lt;/strong&gt;（e,s,c）&lt;/td&gt;
					&lt;td&gt;最终+同步+编制&lt;/td&gt;
					&lt;td&gt;高并发下的妥协方案&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;奇幻小说 Saga&lt;/strong&gt;（a,a,o）&lt;/td&gt;
					&lt;td&gt;原子+异步+编排&lt;/td&gt;
					&lt;td&gt;用异步提高吞吐，靠编排守住一致性&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;恐怖故事 Saga&lt;/strong&gt;（a,a,c）&lt;/td&gt;
					&lt;td&gt;原子+异步+编制&lt;/td&gt;
					&lt;td&gt;作者直言：别这么干&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;平行 Saga&lt;/strong&gt;（e,a,o）&lt;/td&gt;
					&lt;td&gt;最终+异步+编排&lt;/td&gt;
					&lt;td&gt;最实用的组合之一&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;文集 Saga&lt;/strong&gt;（e,a,c）&lt;/td&gt;
					&lt;td&gt;最终+异步+编制&lt;/td&gt;
					&lt;td&gt;最解耦、最高可用，也最难调试&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;起这些&amp;quot;俗气&amp;quot;的名字是故意的：&lt;strong&gt;好记&lt;/strong&gt;。团队讨论时说&amp;quot;这个流程用文集模式&amp;rdquo;，比说&amp;quot;最终一致+异步通信+编制协调的 Saga&amp;quot;高效得多。&lt;/p&gt;
&lt;p&gt;核心结论：&lt;strong&gt;越往左（原子一致），越安全越慢越脆；越往右（最终一致+异步+编制），越快越抗造，但你要为&amp;quot;中间状态&amp;quot;和&amp;quot;补偿逻辑&amp;quot;付出工程成本&lt;/strong&gt;。没有免费的午餐，只有明摆着的账单。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-13-章-契约&#34;&gt;第 13 章 契约&lt;/h2&gt;
&lt;p&gt;服务之间的接口约定（Contract）该多严格？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;严格契约&lt;/strong&gt;：字段类型、格式全部写死并强校验（如带 schema 的 RPC）。好处是错误在编译/调用瞬间就暴露；代价是任何字段变更都要双方同时升级，耦合很紧。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;宽松契约&lt;/strong&gt;：只约定必需字段，其余自由扩展（如 JSON + 约定）。灵活、可独立演进；代价是错误可能潜伏到运行时才爆发。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有两个进阶概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;消费者驱动的契约（Consumer-Driven Contracts）&lt;/strong&gt;：由服务的&lt;strong&gt;调用方&lt;/strong&gt;写下&amp;quot;我需要这些字段、这种格式&amp;quot;，作为测试放进提供方的流水线。提供方一改接口，立刻知道会弄坏谁——契约从&amp;quot;提供方说了算&amp;quot;变成&amp;quot;对消费者负责&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;戳记耦合（Stamp Coupling）&lt;/strong&gt;：调用时把一大坨数据结构整个传过去（比如把 50 个字段的 &lt;code&gt;Customer&lt;/code&gt; 对象传给只需要 &lt;code&gt;email&lt;/code&gt; 的服务）。对方被迫依赖整个结构的形状，改任何一个字段都可能波及无辜。&lt;strong&gt;只传对方真正需要的字段&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-14-章-管理分析型数据&#34;&gt;第 14 章 管理分析型数据&lt;/h2&gt;
&lt;p&gt;运营数据（支撑业务运转）拆给了微服务，那&lt;strong&gt;分析数据&lt;/strong&gt;（做报表、跑 BI、训练模型）怎么办？作者梳理了三代方案：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    DW[&amp;#34;数据仓库&amp;lt;br/&amp;gt;ETL 抽取 → 清洗 → 集中存储&amp;lt;br/&amp;gt;结构化、为报表优化&amp;#34;] --&amp;gt; DL[&amp;#34;数据湖&amp;lt;br/&amp;gt;原始数据先倒进去&amp;lt;br/&amp;gt;用的时候再处理&amp;lt;br/&amp;gt;灵活但容易变成数据沼泽&amp;#34;] --&amp;gt; DM[&amp;#34;数据网格&amp;lt;br/&amp;gt;按领域分布式的数据产品&amp;lt;br/&amp;gt;数据像微服务一样被治理&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;**数据网格（Data Mesh）**是本章的高潮，也是作者之一 Zhamak Dehghani 原创的概念。它的核心思想是把微服务的哲学搬到数据世界：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;领域拥有数据&lt;/strong&gt;：工单团队对自己的工单数据负责，而不是甩给中央数据团队。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据即产品&lt;/strong&gt;：数据要有明确的接口、质量承诺、文档，像 API 一样被消费。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自助式数据平台&lt;/strong&gt;：基础设施团队提供平台，领域团队自助发布数据产品。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;联邦式治理&lt;/strong&gt;：全局规则（隐私、安全）统一，领域内部自治。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;作者也诚实地给出了&amp;quot;什么时候&lt;strong&gt;不要&lt;/strong&gt;用数据网格&amp;quot;：团队小、数据量不大、分析需求简单时，一个数据仓库就够了——&lt;strong&gt;架构量子越少越好，够用就行&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;第-15-章-构建你自己的权衡分析&#34;&gt;第 15 章 构建你自己的权衡分析&lt;/h2&gt;
&lt;p&gt;最后一章是方法论总结：这套&amp;quot;天平分析&amp;quot;手艺怎么用到自己的项目里？&lt;/p&gt;
&lt;p&gt;几条核心建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;找出纠缠的维度&lt;/strong&gt;：性能和安全往往互相拉扯，先把一个决策涉及的所有维度列出来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用 MECE 原则列清单&lt;/strong&gt;：相互独立、完全穷尽，避免漏掉某个维度的代价。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;警惕&amp;quot;脱离上下文&amp;quot;的陷阱&lt;/strong&gt;：网上说&amp;quot;Netflix 用了 X 架构所以 X 好&amp;quot;——Netflix 的上下文（规模、团队、预算）和你完全不同，结论不能直接搬。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定性分析优先于定量&lt;/strong&gt;：很多时候没法拿到精确数字，用&amp;quot;高/中/低&amp;quot;的比较级就够做决策了，别为了伪精确而瘫痪。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;只给结论，别淹没听众&lt;/strong&gt;：给业务方汇报时，先说&amp;quot;推荐方案 B，因为……&amp;quot;，细节放附录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;远离蛇油和布道&lt;/strong&gt;：任何声称&amp;quot;无条件最优&amp;quot;的方案（银弹、框架布道者），默认先怀疑。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;全书总结一张权衡分析的思维导图&#34;&gt;全书总结：一张权衡分析的思维导图&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;mindmap
  root((软件架构&amp;lt;br/&amp;gt;最难的部分))
    核心理念
      没有最佳实践
      一切皆权衡
      架构量子是分析单位
    怎么拆
      组件分解六步法
      数据库五步分解
      粒度天平
        Disintegrators 往细拆
        Integrators 往粗合
    拆完怎么办
      复用四模式
      数据所有权
      分布式数据访问
    跨服务协作
      编排 vs 编制
      八种 Saga
      严格 vs 宽松契约
    数据与分析
      数据仓库/湖
      数据网格
    方法论
      适应度函数
      MECE 分析
      警惕脱离上下文&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这本书最有价值的不是任何一条具体结论，而是它示范的&lt;strong&gt;思考方式&lt;/strong&gt;：面对任何架构决策，先问&amp;quot;这个选择让我得到什么、失去什么&amp;quot;，把天平两端的砝码摆出来，再结合自己的上下文做判断。&lt;/p&gt;
&lt;p&gt;如果你是架构新手，建议阅读顺序是：先读第 1、2 章建立&amp;quot;权衡&amp;quot;和&amp;quot;架构量子&amp;quot;的心智模型，再读第 5、6、7 章学会&amp;quot;拆&amp;quot;，最后按需查阅第 8–14 章解决&amp;quot;拆完之后&amp;quot;的具体问题。第 15 章适合放在手边，每次做重大技术选型前翻一遍——它会让你少交很多学费。&lt;/p&gt;
</description>
    </item>
    <item>
      <title>《Elasticsearch 权威指南》详解：零基础读懂 ES 的核心知识体系</title>
      <link>https://ftmao.com/blog/bookstore/elasticsearch-definitive-guide-notes/</link>
      <pubDate>Sat, 20 Jun 2020 09:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/elasticsearch-definitive-guide-notes/</guid>
      <description>&lt;h1 id=&#34;elasticsearch-权威指南详解零基础读懂-es-的核心知识体系&#34;&gt;《Elasticsearch 权威指南》详解：零基础读懂 ES 的核心知识体系&lt;/h1&gt;
&lt;p&gt;《Elasticsearch 权威指南》（Elasticsearch: The Definitive Guide，Clinton Gormley &amp;amp; Zachary Tong 著）是学习 Elasticsearch 公认的第一本书。中文版由 looly 等人组织社区翻译。它不是 API 手册，而是沿着&amp;quot;&lt;strong&gt;为什么这样设计&lt;/strong&gt;&amp;ldquo;的路线，把 ES 从安装、文档 CRUD 一路讲到分布式内核、分词、相关性算分和集群运维。&lt;/p&gt;
&lt;p&gt;这本书基于 ES 1.x/2.x 时代写成，个别细节（如 type 概念、TF/IDF 默认评分）在新版本有变化，但&lt;strong&gt;核心思想至今不变&lt;/strong&gt;。本文面向从未读过这本书的人，按&amp;quot;基础概念 → 分布式原理 → 搜索 → 语言处理 → 聚合 → 建模 → 运维&amp;quot;的顺序完整梳理，配图解和可直接运行的实例。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一elasticsearch-是什么先建立直觉&#34;&gt;一、Elasticsearch 是什么：先建立直觉&lt;/h2&gt;
&lt;p&gt;书的开篇用一句话定义 ES：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Elasticsearch 是一个&lt;strong&gt;分布式的、实时的文档存储与搜索引擎&lt;/strong&gt;，基于 Apache Lucene 构建，通过 RESTful API 暴露 Lucene 的全部威力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lucene 本身是一个 Java 搜索库，强大但难用——ES 的价值在于把 Lucene 包装成一个可以水平扩展、开箱即用的分布式系统。书中列举的典型用户包括 Wikipedia（全文搜索 + 高亮 + 输入即搜 + 拼写纠错）、GitHub（检索 1300 亿行代码）、StackOverflow（&amp;ldquo;你可能想问&amp;quot;的 more-like-this 推荐）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;与关系型数据库的概念对照&lt;/strong&gt;（这是全书最重要的入门表格）：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;关系型数据库&lt;/th&gt;
					&lt;th&gt;Elasticsearch&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Database（库）&lt;/td&gt;
					&lt;td&gt;Index（索引）&lt;/td&gt;
					&lt;td&gt;逻辑命名空间&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Table（表）&lt;/td&gt;
					&lt;td&gt;Type（类型）&lt;/td&gt;
					&lt;td&gt;⚠️ 新版已废弃，理解旧书时需要&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Row（行）&lt;/td&gt;
					&lt;td&gt;Document（文档）&lt;/td&gt;
					&lt;td&gt;一条 JSON 记录&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Column（列）&lt;/td&gt;
					&lt;td&gt;Field（字段）&lt;/td&gt;
					&lt;td&gt;文档的属性&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Schema（表结构）&lt;/td&gt;
					&lt;td&gt;Mapping（映射）&lt;/td&gt;
					&lt;td&gt;字段类型定义&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;SQL&lt;/td&gt;
					&lt;td&gt;Query DSL&lt;/td&gt;
					&lt;td&gt;查询语言&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;index（索引，动词）&lt;/td&gt;
					&lt;td&gt;index（索引，动词）&lt;/td&gt;
					&lt;td&gt;把文档写入&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键区别&lt;/strong&gt;：数据库假设&amp;quot;每行结构相同&amp;rdquo;，ES 的文档是无模式的 JSON（虽然实际上有 mapping 约束）；数据库为&amp;quot;精确存取&amp;quot;优化，ES 为&amp;rdquo;&lt;strong&gt;相关性检索&lt;/strong&gt;&amp;ldquo;优化——它回答的问题不是&amp;quot;有没有&amp;rdquo;，而是&amp;quot;&lt;strong&gt;有多相关&lt;/strong&gt;&amp;quot;。&lt;/p&gt;
&lt;h3 id=&#34;11-十分钟跑起来书中的第一个例子&#34;&gt;1.1 十分钟跑起来：书中的第一个例子&lt;/h3&gt;
&lt;p&gt;全书第一章用一家虚构公司 megacorp 的员工数据贯穿：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 写入一个员工文档（PUT = 指定 ID 创建/覆盖）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PUT /megacorp/employee/1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;first_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;John&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;last_name&amp;#34;&lt;/span&gt;:  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Smith&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;age&amp;#34;&lt;/span&gt;:        25,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;about&amp;#34;&lt;/span&gt;:      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;I love to go rock climbing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;interests&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sports&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;music&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 取回（GET）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /megacorp/employee/1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 轻量搜索：查询字符串&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /megacorp/employee/_search?q&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;last_name:Smith
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 正式搜索：Query DSL（JSON 请求体）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /megacorp/employee/_search
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;match&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;last_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Smith&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;注意这里&lt;strong&gt;没有建库、没有建表、没有定义字段类型&lt;/strong&gt;——这就是&amp;quot;无模式&amp;quot;体验。但书里马上提醒：无模式是假象，ES 在背后做了&lt;strong&gt;动态映射（dynamic mapping）&lt;/strong&gt;，第一次见 &lt;code&gt;&amp;quot;age&amp;quot;: 25&lt;/code&gt; 就推断 age 是 long。生产环境应该显式定义 mapping（第四章详解）。&lt;/p&gt;
&lt;h3 id=&#34;12-一个展示相关性的例子&#34;&gt;1.2 一个展示&amp;quot;相关性&amp;quot;的例子&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /megacorp/employee/_search
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bool&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;must&amp;#34;&lt;/span&gt;:     &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;match&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;about&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rock climbing&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;filter&amp;#34;&lt;/span&gt;:   &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;range&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;gt&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;返回结果中每个文档带一个 &lt;code&gt;_score&lt;/code&gt;（相关性得分），按得分降序排列。&lt;code&gt;must&lt;/code&gt; 里的 match 参与算分，&lt;code&gt;filter&lt;/code&gt; 里的 range 只过滤不算分——&lt;strong&gt;这是全书反复出现的 query/filter 二分法&lt;/strong&gt;，第六章详解。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二分布式内核es-为什么能水平扩展&#34;&gt;二、分布式内核：ES 为什么能水平扩展&lt;/h2&gt;
&lt;p&gt;这是全书的硬核部分，也是 ES 区别于&amp;quot;单机 Lucene&amp;quot;的根本。&lt;/p&gt;
&lt;h3 id=&#34;21-集群节点与分片&#34;&gt;2.1 集群、节点与分片&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    subgraph CLUSTER[&amp;#34;集群 cluster（一个 cluster.name）&amp;#34;]
        N1[&amp;#34;节点 Node 1&amp;lt;br/&amp;gt;（一个 ES 进程）&amp;#34;]
        N2[&amp;#34;节点 Node 2&amp;#34;]
        N3[&amp;#34;节点 Node 3&amp;#34;]
    end
    subgraph IDX[&amp;#34;索引 blogs（3 主分片 × 1 副本）&amp;#34;]
        P0[&amp;#34;主分片 P0&amp;#34;] --- R0[&amp;#34;副本分片 R0&amp;#34;]
        P1[&amp;#34;主分片 P1&amp;#34;] --- R1[&amp;#34;副本分片 R1&amp;#34;]
        P2[&amp;#34;主分片 P2&amp;#34;] --- R2[&amp;#34;副本分片 R2&amp;#34;]
    end
    N1 -.承载.- P0
    N1 -.承载.- R1
    N2 -.承载.- P1
    N2 -.承载.- R2
    N3 -.承载.- P2
    N3 -.承载.- R0
    style CLUSTER fill:#e8f0fe&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;要点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分片（shard）是 ES 分布式能力的最小单元&lt;/strong&gt;：一个分片就是一个完整的 Lucene 实例。索引只是&amp;quot;一组分片的逻辑命名空间&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主分片数在创建索引时定死&lt;/strong&gt;（路由哈希依赖它），副本分片数可以随时调整。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主副本不允许在同一节点&lt;/strong&gt;——这样任意一台机器宕机，数据不丢、搜索不中断。&lt;/li&gt;
&lt;li&gt;集群健康三态：&lt;code&gt;green&lt;/code&gt;（所有主副本都就位）/ &lt;code&gt;yellow&lt;/code&gt;（主分片齐了，副本缺）/ &lt;code&gt;red&lt;/code&gt;（有主分片丢失，数据不可用）。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /_cluster/health   &lt;span style=&#34;color:#75715e&#34;&gt;# 全书最常用的运维命令&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;22-扩容为什么是免费的&#34;&gt;2.2 扩容为什么是&amp;quot;免费&amp;quot;的&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph 三节点[&amp;#34;3 节点&amp;#34;]
        A1[&amp;#34;P0 R1&amp;#34;] 
        B1[&amp;#34;P1 R2&amp;#34;]
        C1[&amp;#34;P2 R0&amp;#34;]
    end
    subgraph 加一节点[&amp;#34;加入第 4 节点后：自动再平衡&amp;#34;]
        A2[&amp;#34;P0&amp;#34;]
        B2[&amp;#34;P1 R0&amp;#34;]
        C2[&amp;#34;P2&amp;#34;]
        D2[&amp;#34;R1 R2&amp;#34;]
    end
    三节点 --&amp;gt;|&amp;#34;分片自动迁移&amp;lt;br/&amp;gt;无需停机&amp;#34;| 加一节点&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;分片是 Lucene 实例，搬分片 = 搬一个独立的小搜索引擎。ES 自动把分片挪到新节点，流量自动均摊。&lt;strong&gt;垂直扩容（换大机器）之外，ES 天生支持水平扩容（加机器）&lt;/strong&gt;——这是书里&amp;quot;scale up vs scale out&amp;quot;一节的核心论点。&lt;/p&gt;
&lt;h3 id=&#34;23-文档是怎么找到家的路由&#34;&gt;2.3 文档是怎么找到家的：路由&lt;/h3&gt;
&lt;p&gt;写入/读取一个文档时，ES 用它定位分片：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;shard = hash(routing) % number_of_primary_shards&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;code&gt;routing&lt;/code&gt; 默认是文档的 &lt;code&gt;_id&lt;/code&gt;，也可以自定义（比如按用户 ID 路由，让同一用户的数据落在同一分片）。&lt;strong&gt;这解释了主分片数为什么不能改&lt;/strong&gt;：改了模数，所有已有文档的哈希结果全部失效。&lt;/p&gt;
&lt;h3 id=&#34;24-写入与读取的分布式流程&#34;&gt;2.4 写入与读取的分布式流程&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant C as 客户端
    participant N as 协调节点&amp;lt;br/&amp;gt;（收到请求的任意节点）
    participant P as 主分片所在节点
    participant R1 as 副本节点1
    participant R2 as 副本节点2

    Note over C,N: 写入流程
    C-&amp;gt;&amp;gt;N: PUT /blogs/post/123
    N-&amp;gt;&amp;gt;N: 计算路由 hash(_id)%3 → P1
    N-&amp;gt;&amp;gt;P: 转发写入
    P-&amp;gt;&amp;gt;P: 本地写 Lucene + translog
    par 并行同步副本
        P-&amp;gt;&amp;gt;R1: 复制写入
        P-&amp;gt;&amp;gt;R2: 复制写入
    end
    R1--&amp;gt;&amp;gt;P: ack
    R2--&amp;gt;&amp;gt;P: ack
    P--&amp;gt;&amp;gt;N: 成功
    N--&amp;gt;&amp;gt;C: 201 + _version

    Note over C,N: 读取流程
    C-&amp;gt;&amp;gt;N: GET /blogs/post/123
    N-&amp;gt;&amp;gt;N: 路由计算 → 分片 P1
    N-&amp;gt;&amp;gt;R2: 主/副本轮询负载均衡
    R2--&amp;gt;&amp;gt;N: 文档
    N--&amp;gt;&amp;gt;C: 200 + _source&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;两个关键设计：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;任何节点都能当协调节点&lt;/strong&gt;——没有单点入口；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;写走主分片，读可以走主或副本&lt;/strong&gt;——副本不仅容灾，还分摊读流量。加副本 = 加读吞吐。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三近实时的秘密refreshtranslog-与段合并&#34;&gt;三、近实时的秘密：refresh、translog 与段合并&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;文档写入后为什么默认 1 秒才能搜到？&amp;ldquo;回答这个问题需要理解 Lucene 的存储模型。&lt;/p&gt;
&lt;h3 id=&#34;31-倒排索引与不可变段&#34;&gt;3.1 倒排索引与不可变段&lt;/h3&gt;
&lt;p&gt;ES 快的根源是&lt;strong&gt;倒排索引&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph 词典[&amp;#34;Term Dictionary&amp;#34;]
        T1[&amp;#34;climbing&amp;#34;]
        T2[&amp;#34;music&amp;#34;]
        T3[&amp;#34;rock&amp;#34;]
    end
    subgraph 倒排表[&amp;#34;Posting List&amp;#34;]
        L1[&amp;#34;doc 1, doc 5&amp;#34;]
        L2[&amp;#34;doc 1, doc 3&amp;#34;]
        L3[&amp;#34;doc 1&amp;#34;]
    end
    T1 --&amp;gt; L1
    T2 --&amp;gt; L2
    T3 --&amp;gt; L3&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;Lucene 把倒排索引持久化为&lt;strong&gt;段（segment）&lt;/strong&gt;，段一旦写出就&lt;strong&gt;不可变（immutable）&lt;/strong&gt;。不可变性带来一串好处：不需要锁、可以进文件系统缓存、删除只需打标记。代价是：&lt;strong&gt;更新 = 删旧 + 建新&lt;/strong&gt;，新写的文档必须先攒成新段才能被搜索。&lt;/p&gt;
&lt;h3 id=&#34;32-完整链路&#34;&gt;3.2 完整链路&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    W[&amp;#34;文档写入请求&amp;#34;] --&amp;gt; MEM[&amp;#34;内存缓冲区&amp;#34;]
    W --&amp;gt; TL[&amp;#34;translog&amp;lt;br/&amp;gt;（先写 WAL，防宕机丢数据）&amp;#34;]
    MEM --&amp;gt;|&amp;#34;refresh&amp;lt;br/&amp;gt;默认每 1 秒&amp;#34;| SEG[&amp;#34;新 segment&amp;lt;br/&amp;gt;写入文件系统缓存&amp;lt;br/&amp;gt;→ 立即可被搜索&amp;#34;]
    SEG --&amp;gt;|&amp;#34;flush&amp;lt;br/&amp;gt;translog 满或定时&amp;#34;| DISK[&amp;#34;fsync 落盘&amp;lt;br/&amp;gt;清空 translog&amp;#34;]
    SEG -.-&amp;gt;|&amp;#34;后台 merge&amp;#34;| BIG[&amp;#34;小段合并成大段&amp;lt;br/&amp;gt;物理清除已删文档&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三个动作的分工：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;refresh&lt;/strong&gt;：内存 → 新的可搜索 segment（在文件系统缓存里，不落盘）。这就是&amp;quot;近实时（NRT）&amp;ldquo;的来源——默认 1 秒延迟。批量导入时可以调大 &lt;code&gt;refresh_interval&lt;/code&gt; 换吞吐。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;translog&lt;/strong&gt;：预写日志。每个写入先记 translog，节点宕机后用它重放还没 flush 的数据。这是&amp;quot;ES 不丢数据&amp;quot;的底气。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;flush&lt;/strong&gt;：真正 fsync 落盘 + 清空 translog。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;merge&lt;/strong&gt;：小段太多会拖慢搜索（每个查询都要遍历所有段），后台线程持续把小段合并成大段，并顺带物理删除被标记删除的文档。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;给初学者的记忆法&lt;/strong&gt;：refresh 管&amp;quot;搜得到&amp;rdquo;，flush 管&amp;quot;丢不了&amp;rdquo;，merge 管&amp;quot;搜得快&amp;rdquo;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四映射与分析数据进-es-之前发生了什么&#34;&gt;四、映射与分析：数据进 ES 之前发生了什么&lt;/h2&gt;
&lt;h3 id=&#34;41-mapping字段的类型决定它能怎么被查&#34;&gt;4.1 Mapping：字段的类型决定它能怎么被查&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;PUT&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/blogs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;mappings&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;:    { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;analyzer&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ik_max_word&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;:   { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;keyword&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;views&amp;#34;&lt;/span&gt;:    { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;integer&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;created&amp;#34;&lt;/span&gt;:  { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;date&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;最核心的类型二分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;text&lt;/code&gt;&lt;/strong&gt;：全文字段，会被&lt;strong&gt;分词&lt;/strong&gt;后存入倒排索引，用于 &lt;code&gt;match&lt;/code&gt; 全文检索；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;keyword&lt;/code&gt;&lt;/strong&gt;：精确值字段，&lt;strong&gt;原样&lt;/strong&gt;入索引，用于过滤、排序、聚合。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;书中金句：&amp;quot;&lt;strong&gt;exact value 和 full text 的区别，是 SQL 世界和搜索世界的分界&lt;/strong&gt;&amp;quot;。用 keyword 字段做 match 全文搜索（或反过来）是新手最常见的错误。&lt;/p&gt;
&lt;p&gt;⚠️ 版本提示：原书写作时还是 &lt;code&gt;string&lt;/code&gt; + &lt;code&gt;analyzed/not_analyzed&lt;/code&gt; 的写法，ES 5.x 起拆分为 &lt;code&gt;text&lt;/code&gt;/&lt;code&gt;keyword&lt;/code&gt;，思想完全一致。&lt;/p&gt;
&lt;h3 id=&#34;42-分析链分词是怎么发生的&#34;&gt;4.2 分析链：分词是怎么发生的&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[&amp;#34;原始文本&amp;lt;br/&amp;gt;&amp;#39;The Quick Brown Fox!&amp;#39;&amp;#34;] --&amp;gt; CF[&amp;#34;Character Filter&amp;lt;br/&amp;gt;字符过滤&amp;lt;br/&amp;gt;（去 HTML、字符映射）&amp;#34;]
    CF --&amp;gt; TK[&amp;#34;Tokenizer&amp;lt;br/&amp;gt;分词器&amp;lt;br/&amp;gt;&amp;#39;The&amp;#39;,&amp;#39;Quick&amp;#39;,&amp;#39;Brown&amp;#39;,&amp;#39;Fox&amp;#39;&amp;#34;]
    TK --&amp;gt; TF[&amp;#34;Token Filter&amp;lt;br/&amp;gt;词项过滤器&amp;lt;br/&amp;gt;lowercase → 停用词 → 词干提取&amp;#34;]
    TF --&amp;gt; R[&amp;#34;最终词项&amp;lt;br/&amp;gt;&amp;#39;quick&amp;#39;,&amp;#39;brown&amp;#39;,&amp;#39;fox&amp;#39;&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Character Filter&lt;/strong&gt;：分词前做字符级预处理；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tokenizer&lt;/strong&gt;：把文本切成词项（一个分析器&lt;strong&gt;只能有一个&lt;/strong&gt; tokenizer）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Token Filter&lt;/strong&gt;：对词项再加工，可串联多个（lowercase、停用词、同义词、词干提取）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;内置的 &lt;code&gt;standard&lt;/code&gt; 分析器 = standard tokenizer + lowercase + stop。可以用 &lt;code&gt;_analyze&lt;/code&gt; API 眼见为实：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;POST /_analyze
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;analyzer&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;standard&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The Quick Brown Fox!&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# → the, quick, brown, fox（&amp;#39;The&amp;#39; 被小写化）&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;中文场景&lt;/strong&gt;：标准分词器会把中文逐字切开，效果很差。社区方案是 IK 分词器（&lt;code&gt;ik_max_word&lt;/code&gt; 细粒度建索引、&lt;code&gt;ik_smart&lt;/code&gt; 粗粒度搜索），这是中文 ES 项目的标配。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;索引时与搜索时的分析必须配套&lt;/strong&gt;：文档里的 &lt;code&gt;&amp;quot;Fox&amp;quot;&lt;/code&gt; 入库时被分析成 &lt;code&gt;fox&lt;/code&gt;；搜索 &lt;code&gt;&amp;quot;FOX&amp;quot;&lt;/code&gt; 时查询词也会被同一个分析器处理成 &lt;code&gt;fox&lt;/code&gt; 去匹配——&lt;strong&gt;这就是为什么 ES 搜索&amp;quot;不区分大小写&amp;quot;&lt;/strong&gt;：不是查询忽略了大小写，而是两边都被 lowercased 了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五搜索query-dsl-的全景&#34;&gt;五、搜索：Query DSL 的全景&lt;/h2&gt;
&lt;h3 id=&#34;51-query-与-filter全书最重要的性能概念&#34;&gt;5.1 Query 与 Filter：全书最重要的性能概念&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    Q[&amp;#34;Query 上下文&amp;lt;br/&amp;gt;&amp;#39;这个文档和查询有多相关？&amp;#39;&amp;#34;]
    F[&amp;#34;Filter 上下文&amp;lt;br/&amp;gt;&amp;#39;这个文档是否匹配？&amp;#39;&amp;#34;]
    Q --&amp;gt; A1[&amp;#34;计算 _score&amp;lt;br/&amp;gt;结果按相关性排序&amp;#34;]
    F --&amp;gt; A2[&amp;#34;只答 yes/no&amp;lt;br/&amp;gt;不算分、结果可被缓存&amp;#34;]
    style Q fill:#fef3e8
    style F fill:#e1f5e1&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;经验法则：能用 filter 就不用 query&lt;/strong&gt;。过滤条件（状态、时间范围、ID 列表）放 filter，既省算分开销又能命中 bitset 缓存。&lt;/p&gt;
&lt;h3 id=&#34;52-常用查询速查&#34;&gt;5.2 常用查询速查&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;查询&lt;/th&gt;
					&lt;th&gt;用途&lt;/th&gt;
					&lt;th&gt;例子&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;match&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;全文搜索（会分词）&lt;/td&gt;
					&lt;td&gt;标题含&amp;quot;登山 装备&amp;quot;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;term&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;精确匹配（不分词）&lt;/td&gt;
					&lt;td&gt;status 等于 &amp;ldquo;published&amp;rdquo;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;terms&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;多值精确匹配&lt;/td&gt;
					&lt;td&gt;标签 in [&amp;ldquo;科幻&amp;rdquo;,&amp;ldquo;动作&amp;rdquo;]&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;range&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;范围&lt;/td&gt;
					&lt;td&gt;age ≥ 30&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;exists&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;字段非空&lt;/td&gt;
					&lt;td&gt;必须有 poster 字段&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;multi_match&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;跨多字段全文&lt;/td&gt;
					&lt;td&gt;同时在标题和简介里搜&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;match_phrase&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;短语匹配（词序相邻）&lt;/td&gt;
					&lt;td&gt;搜 &amp;ldquo;rock climbing&amp;rdquo; 不拆词&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;组合一切&lt;/td&gt;
					&lt;td&gt;见下&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;53-bool-查询乐高积木&#34;&gt;5.3 bool 查询：乐高积木&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;GET&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;/megacorp/employee/_search&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;query&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bool&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;must&amp;#34;&lt;/span&gt;:     [ { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;match&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;about&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;rock climbing&amp;#34;&lt;/span&gt; } } ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;should&amp;#34;&lt;/span&gt;:   [ { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;match&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;interests&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;music&amp;#34;&lt;/span&gt; } } ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;must_not&amp;#34;&lt;/span&gt;: [ { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;term&amp;#34;&lt;/span&gt;:  { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;last_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;smith&amp;#34;&lt;/span&gt; } } ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;filter&amp;#34;&lt;/span&gt;:   [ { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;range&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;gte&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt; } } } ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;must&lt;/code&gt;：必须匹配，参与算分；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;should&lt;/code&gt;：最好匹配，匹配上加分（没有 must/filter 时至少命中一个）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;must_not&lt;/code&gt;：必须不匹配，不参与算分（在 filter 上下文执行）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filter&lt;/code&gt;：必须匹配，不参与算分。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;所有复杂查询都是这四块积木的嵌套&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;54-相关性算分_score-是怎么来的&#34;&gt;5.4 相关性算分：_score 是怎么来的&lt;/h3&gt;
&lt;p&gt;书里讲了经典的 &lt;strong&gt;TF/IDF&lt;/strong&gt; 模型（新版本默认换成 BM25，思想相同，多了文档长度归一化）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;词频 TF&lt;/strong&gt;：词在本文档出现越多，越相关；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;逆文档频率 IDF&lt;/strong&gt;：词在所有文档中越罕见，区分度越高（&amp;ldquo;的&amp;quot;不值钱，&amp;ldquo;攀岩&amp;quot;值钱）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;字段长度&lt;/strong&gt;：字段越短，匹配越&amp;quot;精悍&amp;rdquo;，得分越高。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对结果有疑问时用 &lt;code&gt;_explain&lt;/code&gt; API 看算分过程——这是调搜索体验的第一件工具。&lt;/p&gt;
&lt;h3 id=&#34;55-深分页陷阱&#34;&gt;5.5 深分页陷阱&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /_search?from&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;10000&amp;amp;size&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;# ⚠️ 危险&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;分布式搜索是 &lt;strong&gt;scatter-gather&lt;/strong&gt; 模式：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant C as 客户端
    participant CO as 协调节点
    participant S0 as 分片0
    participant S1 as 分片1
    participant S2 as 分片2
    C-&amp;gt;&amp;gt;CO: from=10000, size=10
    par scatter
        CO-&amp;gt;&amp;gt;S0: 各分片先取 Top 10010
        CO-&amp;gt;&amp;gt;S1: 各分片先取 Top 10010
        CO-&amp;gt;&amp;gt;S2: 各分片先取 Top 10010
    end
    S0--&amp;gt;&amp;gt;CO: 10010 条
    S1--&amp;gt;&amp;gt;CO: 10010 条
    S2--&amp;gt;&amp;gt;CO: 10010 条
    CO-&amp;gt;&amp;gt;CO: 归并 30030 条，取 10001~10010
    CO--&amp;gt;&amp;gt;C: 10 条&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;取第 1001 页，要在每个分片各取 10010 条再归并。&lt;strong&gt;深度翻页成本线性爆炸&lt;/strong&gt;，书里的忠告是&amp;quot;不要在搜索场景做深翻页&amp;rdquo;；确实需要遍历全量时，用 &lt;code&gt;scroll&lt;/code&gt;（游标快照）或新版本的 &lt;code&gt;search_after&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六处理人类语言&#34;&gt;六、处理人类语言&lt;/h2&gt;
&lt;p&gt;这一部分是本书区别于普通教程的精华：搜索的本质对手是&lt;strong&gt;自然语言的模糊性&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;语言分析器&lt;/strong&gt;：英语词干提取（&lt;code&gt;foxes&lt;/code&gt;→&lt;code&gt;fox&lt;/code&gt;）、德语的复合词拆分、中文/日文的无空格分词。每种语言的正确处理方式完全不同——&lt;strong&gt;选对语言分析器比调任何参数都重要&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拼写纠错（did-you-mean）&lt;/strong&gt;：&lt;code&gt;term suggester&lt;/code&gt; / &lt;code&gt;phrase suggester&lt;/code&gt;，&amp;ldquo;你搜的是不是 elasticsearch？&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;输入即搜（search-as-you-type）&lt;/strong&gt;：用 &lt;code&gt;edge ngram&lt;/code&gt;（前缀 ngram）在索引时把 &lt;code&gt;&amp;quot;elasticsearch&amp;quot;&lt;/code&gt; 切成 &lt;code&gt;e, el, ela, ...&lt;/code&gt;，用户每敲一个字母就能命中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;近似匹配&lt;/strong&gt;：&lt;code&gt;match&lt;/code&gt; 的 &lt;code&gt;fuzziness&lt;/code&gt; 参数基于编辑距离，容忍拼写错误（&lt;code&gt;searching&lt;/code&gt; 打成 &lt;code&gt;serching&lt;/code&gt; 也能命中）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;近似短语&lt;/strong&gt;：&lt;code&gt;match_phrase&lt;/code&gt; 的 &lt;code&gt;slop&lt;/code&gt; 参数允许词与词之间隔着几个词仍算匹配。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这一部分的共同心法：&lt;strong&gt;召回率与准确率的权衡&lt;/strong&gt;。ngram 开得越大、fuzziness 越高，召回越好但噪音越多；工程上常用&amp;quot;多层子字段&amp;quot;（精确字段 + 分词字段 + ngram 字段）分层兜底。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七聚合从搜索到分析&#34;&gt;七、聚合：从搜索到分析&lt;/h2&gt;
&lt;p&gt;搜索回答&amp;quot;哪些文档匹配&amp;quot;，&lt;strong&gt;聚合（aggregations）&lt;/strong&gt; 回答&amp;quot;数据整体长什么样&amp;quot;。聚合分两大类，可以无限嵌套：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;桶（Bucket）聚合&lt;/strong&gt;：把文档分组——&lt;code&gt;terms&lt;/code&gt;（按字段值）、&lt;code&gt;date_histogram&lt;/code&gt;（按时间）、&lt;code&gt;range&lt;/code&gt;（按区间）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;度量（Metric）聚合&lt;/strong&gt;：对组内文档算统计——&lt;code&gt;avg/sum/min/max/stats/cardinality&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;71-实例员工数据统计&#34;&gt;7.1 实例：员工数据统计&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GET /megacorp/employee/_search
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;: 0,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aggs&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;by_interest&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;              &lt;span style=&#34;color:#75715e&#34;&gt;# 桶：按兴趣分组&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;terms&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;field&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;interests&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;aggs&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;avg_age&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;              &lt;span style=&#34;color:#75715e&#34;&gt;# 嵌套度量：每组平均年龄&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;avg&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;field&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;age&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[&amp;#34;全部员工文档&amp;#34;] --&amp;gt;|&amp;#34;terms 桶 by_interest&amp;#34;| B1[&amp;#34;sports 组&amp;#34;]
    A --&amp;gt; B2[&amp;#34;music 组&amp;#34;]
    A --&amp;gt; B3[&amp;#34;forestry 组&amp;#34;]
    B1 --&amp;gt; C1[&amp;#34;avg_age = 28.5&amp;#34;]
    B2 --&amp;gt; C2[&amp;#34;avg_age = 31.2&amp;#34;]
    B3 --&amp;gt; C3[&amp;#34;avg_age = 35.0&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;72-要点&#34;&gt;7.2 要点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;size: 0&lt;/code&gt;&lt;/strong&gt;：只要聚合不要文档，省掉无用传输；&lt;/li&gt;
&lt;li&gt;聚合只能跑在 &lt;code&gt;keyword&lt;/code&gt;/数值/日期等&lt;strong&gt;精确值字段&lt;/strong&gt;上——在 &lt;code&gt;text&lt;/code&gt; 字段上聚合会失败或慢到崩溃（需要 fielddata，默认关闭，这是个性能地雷）；&lt;/li&gt;
&lt;li&gt;高基数 &lt;code&gt;terms&lt;/code&gt; 聚合（如按 userId 分桶）吃内存，新版有 circuit breaker 兜底；&lt;/li&gt;
&lt;li&gt;近似聚合是 ES 的特色：&lt;code&gt;cardinality&lt;/code&gt;（去重计数）和 &lt;code&gt;percentiles&lt;/code&gt;（百分位）都是&lt;strong&gt;近似算法&lt;/strong&gt;，用微小误差换海量数据下的可用性。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八关系建模es-不是数据库&#34;&gt;八、关系建模：ES 不是数据库&lt;/h2&gt;
&lt;p&gt;书里最&amp;quot;劝退&amp;quot;也最重要的一章：ES 没有 join。建模关系的四种方式按推荐顺序：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;方式&lt;/th&gt;
					&lt;th&gt;思路&lt;/th&gt;
					&lt;th&gt;代价&lt;/th&gt;
					&lt;th&gt;适用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;反规范化（Denormalize）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;把关联数据冗余进每个文档&lt;/td&gt;
					&lt;td&gt;数据重复、更新要改多处&lt;/td&gt;
					&lt;td&gt;首选，读多写少&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;应用层 join&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;查两次，在代码里拼接&lt;/td&gt;
					&lt;td&gt;多一次网络往返&lt;/td&gt;
					&lt;td&gt;一对多且&amp;quot;多&amp;quot;侧很大&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;nested 对象&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;对象数组整体索引，保持内部关联&lt;/td&gt;
					&lt;td&gt;查询稍慢，更新整文档重建&lt;/td&gt;
					&lt;td&gt;对象数组（如文章的评论列表）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;parent-child（join 字段）&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;父子文档独立存储，同分片关联&lt;/td&gt;
					&lt;td&gt;查询开销最大&lt;/td&gt;
					&lt;td&gt;子文档频繁独立更新&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;默认答案永远是先反规范化&lt;/strong&gt;。搜索系统的哲学是&amp;quot;用写入时的冗余，换查询时的简单&amp;quot;——磁盘便宜，join 昂贵。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九集群管理与运维要点&#34;&gt;九、集群管理与运维要点&lt;/h2&gt;
&lt;p&gt;最后一部分面向生产：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;节点角色分工&lt;/strong&gt;：master 节点管集群元数据（建议 3 个专职 master 防脑裂）、data 节点存数据、协调节点转发请求。大集群应该角色分离。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;脑裂与 minimum_master_nodes&lt;/strong&gt;：网络分区时两个子集群各自选主 = 数据写烂。经典配置是 &lt;code&gt;master 节点数 / 2 + 1&lt;/code&gt;（新版本的集群选举机制已自动化）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分片规划&lt;/strong&gt;：分片不是越多越好——每个分片都有固定开销（文件句柄、内存、段管理）。单分片控制在几十 GB 量级、节点堆内存 32GB 以内（压指针上限）、堆内存不超过物理内存一半（另一半留给文件系统缓存）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;监控集群健康&lt;/strong&gt;：&lt;code&gt;green/yellow/red&lt;/code&gt; + &lt;code&gt;hot_threads&lt;/code&gt; + &lt;code&gt;cat&lt;/code&gt; API 家族。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;升级与备份&lt;/strong&gt;：滚动升级逐个节点来；快照（snapshot）到 S3/HDFS 做备份。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十全书知识地图&#34;&gt;十、全书知识地图&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    CORE[&amp;#34;倒排索引&amp;lt;br/&amp;gt;（一切的根基）&amp;#34;]
    CORE --&amp;gt; WRITE[&amp;#34;写入链路&amp;lt;br/&amp;gt;refresh/translog/merge&amp;lt;br/&amp;gt;→ 近实时 + 不丢数据&amp;#34;]
    CORE --&amp;gt; SEARCH[&amp;#34;搜索&amp;lt;br/&amp;gt;Query/Filter + bool 组合&amp;lt;br/&amp;gt;TF-IDF 相关性算分&amp;#34;]
    CORE --&amp;gt; AGG[&amp;#34;聚合&amp;lt;br/&amp;gt;桶 × 度量嵌套&amp;#34;]
    WRITE --&amp;gt; DIST[&amp;#34;分布式&amp;lt;br/&amp;gt;分片路由 scatter-gather&amp;lt;br/&amp;gt;主副本容灾&amp;#34;]
    SEARCH --&amp;gt; LANG[&amp;#34;语言处理&amp;lt;br/&amp;gt;分析器/纠错/输入即搜&amp;#34;]
    SEARCH --&amp;gt; MODEL[&amp;#34;建模&amp;lt;br/&amp;gt;反规范化优先&amp;#34;]
    DIST --&amp;gt; OPS[&amp;#34;运维&amp;lt;br/&amp;gt;健康监控/分片规划/防脑裂&amp;#34;]
    style CORE fill:#e1f5e1&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;如果只能记住三句话：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;一切性能与行为的答案都在倒排索引里&lt;/strong&gt;：不可变段决定了 NRT，分词决定了查不查得到，keyword/text 之分决定了能不能聚合；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;query 算分、filter 缓存&lt;/strong&gt;——写任何查询前先想这一条；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分片是分布式的原子&lt;/strong&gt;：路由公式决定了主分片数不能改，副本决定了读吞吐和容灾。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;参考资料：Clinton Gormley, Zachary Tong. 《Elasticsearch: The Definitive Guide》. O&amp;rsquo;Reilly, 2015. 中文版：&lt;a href=&#34;https://github.com/looly/elasticsearch-definitive-guide-cn&#34;&gt;elasticsearch-definitive-guide-cn&lt;/a&gt;（looly 等译）。&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
