feat: add comprehensive backend topics and fix build issues
## 新增内容 ### 附录文档扩展 - 扩展前端项目架构文档 (frontend-project-architecture.md) - 扩展后端项目架构文档 (backend-project-architecture.md) - 扩展数据治理文档 (data-governance.md) - 扩展数据可视化文档 (data-visualization.md) - 扩展分布式系统文档 (distributed-systems.md) - 扩展高可用文档 (high-availability.md) - 扩展单体到微服务文档 (monolith-to-microservices.md) - 扩展系统设计方法论文档 (system-design-methodology.md) - 扩展 Docker 容器文档 (docker-containers.md) - 扩展 Kubernetes 文档 (kubernetes.md) - 扩展 Linux 基础文档 (linux-basics.md) - 扩展神经网络文档 (neural-networks.md) ### 新增交互式组件 - 数据治理组件: DataQualityDemo, DataGovernanceFrameworkDemo, DataLineageDemo - 数据可视化组件: ChartTypeSelectorDemo, DashboardLayoutDemo - 分布式系统组件: CAPTheoremDemo, ConsistencyModelsDemo, DistributedChallengesDemo - 高可用组件: AvailabilityCalculatorDemo, FailoverStrategyDemo - 系统设计组件: SystemDesignStepsDemo, CapacityEstimationDemo - Docker 容器组件: DockerArchitectureDemo, DockerLifecycleDemo - Kubernetes 组件: K8sArchitectureDemo, K8sWorkloadsDemo - Linux 基础组件: LinuxFileSystemDemo, LinuxCommandDemo, LinuxPermissionsDemo - 神经网络组件: NeuronDemo, NetworkLayersDemo, NetworkArchitectureDemo - 单体到微服务组件: ArchEvolutionDemo - 项目架构组件: ProjectArchitectureComparisonDemo - 附录导航组件: AppendixFlowMap ### 英文版重构 - 将 en-us 目录重命名为 en - 更新相关配置和组件中的语言代码 ## Bug 修复 - 修复 index.js 中重复的组件导入语句导致的 build 失败 - 恢复被注释的 InvertedIndexDemo 和 SearchRelevanceDemo 导入 - 修复 HomeFeatures.vue 中 en-us 与 config.mjs 中 en 不一致导致的语言切换问题 ## 其他改进 - 添加构建脚本 (scripts/build.mjs) - 更新依赖版本
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,214 @@
|
||||
# 数据治理与数据质量
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**你有没有遇到过这种情况:报表上的数字和实际业务对不上,两个系统里同一个用户的信息不一样,或者分析结果因为脏数据完全不可信?** 数据治理就是解决这些问题的系统性方法。在"数据驱动决策"的时代,数据质量直接决定了决策质量——垃圾进,垃圾出(Garbage In, Garbage Out)。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **数据质量维度**:理解完整性、准确性、一致性等六大质量维度
|
||||
- **数据治理体系**:了解从组织、流程到技术的治理框架
|
||||
- **数据血缘**:掌握数据从源头到消费的全链路追踪
|
||||
- **元数据管理**:理解"描述数据的数据"的重要性
|
||||
- **数据分层架构**:掌握 ODS → DWD → DWS → ADS 的数仓分层模型
|
||||
- **实战能力**:知道如何在项目中落地数据治理
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 数据质量维度 | 完整性、准确性、一致性、时效性 |
|
||||
| **第 2 章** | 数据治理框架 | 组织、流程、技术、文化 |
|
||||
| **第 3 章** | 数据血缘追踪 | 影响分析、问题排查、合规审计 |
|
||||
| **第 4 章** | 元数据管理 | 技术元数据、业务元数据、操作元数据 |
|
||||
| **第 5 章** | 数据分层架构 | ODS、DWD、DWS、ADS |
|
||||
| **第 6 章** | 治理工具与实践 | Great Expectations、dbt、DataHub |
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:为什么需要数据治理?
|
||||
|
||||
数据治理不是一个技术问题,而是一个**管理问题**。它回答的核心问题是:**谁对数据负责?数据的标准是什么?如何保证数据持续可信?**
|
||||
|
||||
想象一个公司有 100 个数据表,每个表由不同团队维护,没有统一的命名规范、没有数据字典、没有质量检查。结果就是:同一个"月活用户"指标,市场部算出来 500 万,产品部算出来 300 万——因为定义不一样。
|
||||
|
||||
::: tip 数据治理的四个支柱
|
||||
1. **组织**:明确数据 Owner、数据管家(Data Steward)的角色和职责
|
||||
2. **流程**:建立数据接入、变更、下线的标准流程
|
||||
3. **技术**:部署数据质量监控、元数据管理、血缘追踪等工具
|
||||
4. **文化**:让全公司认同"数据是资产",而不是"数据是副产品"
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据质量的六个维度
|
||||
|
||||
数据质量不是一个模糊的概念,而是可以从六个具体维度来衡量的。每个维度都有明确的定义和检测方法。
|
||||
|
||||
<DataQualityDemo />
|
||||
|
||||
| 维度 | 定义 | 检测方法 | 常见问题 |
|
||||
|------|------|---------|---------|
|
||||
| 完整性 | 数据是否存在缺失 | 空值率检查 | 必填字段为空、关联数据缺失 |
|
||||
| 准确性 | 数据是否正确 | 规则校验、抽样核对 | 金额为负、日期不合法 |
|
||||
| 一致性 | 多源数据是否一致 | 跨系统比对 | CRM 和订单系统用户名不同 |
|
||||
| 时效性 | 数据是否及时更新 | 更新时间检查 | 库存数据滞后、价格未同步 |
|
||||
| 唯一性 | 是否存在重复记录 | 去重检查 | 同一用户注册两次 |
|
||||
| 有效性 | 是否符合格式规则 | 正则/范围校验 | 邮箱格式错误、年龄为负数 |
|
||||
|
||||
::: tip 数据质量的 1-10-100 法则
|
||||
- **1 元**:在数据入口做校验,预防脏数据进入
|
||||
- **10 元**:在数据仓库中清洗已有的脏数据
|
||||
- **100 元**:因为脏数据导致错误决策的损失
|
||||
|
||||
越早发现和修复数据质量问题,成本越低。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 数据治理框架:全生命周期管理
|
||||
|
||||
数据治理不是一次性项目,而是贯穿数据全生命周期的持续过程。从数据的产生到销毁,每个阶段都需要明确的规范和责任人。
|
||||
|
||||
<DataGovernanceFrameworkDemo />
|
||||
|
||||
| 阶段 | 核心产出 | 关键角色 |
|
||||
|------|---------|---------|
|
||||
| 定义标准 | 数据字典、命名规范、分类分级标准 | 数据架构师 |
|
||||
| 采集接入 | 接入规范、校验规则、血缘记录 | 数据工程师 |
|
||||
| 存储管理 | 分层模型、权限矩阵、生命周期策略 | DBA / 平台工程师 |
|
||||
| 使用消费 | 数据目录、脱敏规则、质量报告 | 数据分析师 / 业务方 |
|
||||
| 归档销毁 | 归档策略、删除记录、审计日志 | 安全合规团队 |
|
||||
|
||||
## 2. 数据治理框架
|
||||
|
||||
数据治理不是买一个工具就能解决的,它需要一套完整的框架来支撑。业界最常用的参考框架是 DAMA-DMBOK(数据管理知识体系)。
|
||||
|
||||
| 治理领域 | 核心内容 | 关键产出 |
|
||||
|---------|---------|---------|
|
||||
| 数据架构 | 定义数据模型、数据流、存储策略 | 数据架构图、ER 图 |
|
||||
| 数据标准 | 统一命名规范、编码规范、指标定义 | 数据字典、指标库 |
|
||||
| 数据质量 | 建立质量规则、监控告警、修复流程 | 质量报告、SLA 仪表盘 |
|
||||
| 数据安全 | 分级分类、访问控制、脱敏加密 | 安全策略、审计日志 |
|
||||
| 主数据管理 | 统一客户、商品等核心实体的"黄金记录" | 主数据中心 |
|
||||
| 数据生命周期 | 管理数据从创建到归档到销毁的全过程 | 保留策略、归档规则 |
|
||||
|
||||
::: tip 数据治理的成熟度模型
|
||||
- **Level 1 - 初始级**:没有统一标准,各团队各自为政
|
||||
- **Level 2 - 可重复级**:有基本的规范文档,但执行不一致
|
||||
- **Level 3 - 已定义级**:有统一的治理流程和工具,大部分团队遵守
|
||||
- **Level 4 - 已管理级**:有量化的质量指标和自动化监控
|
||||
- **Level 5 - 优化级**:持续改进,数据治理融入日常开发流程
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据血缘:从哪来,到哪去
|
||||
|
||||
数据血缘(Data Lineage)记录了数据从源头到最终消费的完整流转路径。它就像数据的"族谱",让你能追溯任何一个数据的来龙去脉。
|
||||
|
||||
<DataLineageDemo />
|
||||
|
||||
数据血缘在实际工作中有三个核心应用场景:
|
||||
|
||||
| 场景 | 问题 | 血缘如何帮助 |
|
||||
|------|------|------------|
|
||||
| 影响分析 | 要修改用户表的字段,会影响哪些下游报表? | 沿血缘向下追踪所有依赖 |
|
||||
| 根因定位 | 今天的 GMV 报表数据异常,问题出在哪一步? | 沿血缘向上回溯每个环节 |
|
||||
| 合规审计 | 用户的手机号经过了哪些系统?是否都做了脱敏? | 追踪敏感字段的全链路流转 |
|
||||
|
||||
::: tip 血缘采集的两种方式
|
||||
- **主动采集**:解析 SQL 语句、ETL 配置,自动提取表级/字段级血缘关系
|
||||
- **被动采集**:通过 Hook 拦截查询引擎(如 Hive、Spark)的执行计划,实时记录血缘
|
||||
|
||||
主流工具如 Apache Atlas、DataHub、OpenLineage 都支持自动化血缘采集。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 元数据管理:"描述数据的数据"
|
||||
|
||||
元数据(Metadata)是关于数据的数据。如果数据是一本书的内容,元数据就是书的目录、作者、出版日期、ISBN 号。没有元数据,数据就是一堆无法理解的数字和字符串。
|
||||
|
||||
| 元数据类型 | 描述 | 示例 |
|
||||
|-----------|------|------|
|
||||
| 技术元数据 | 数据的物理存储信息 | 表名、字段类型、分区方式、存储位置 |
|
||||
| 业务元数据 | 数据的业务含义 | 字段中文名、业务定义、计算口径 |
|
||||
| 操作元数据 | 数据的运行状态 | ETL 执行时间、数据量、更新频率 |
|
||||
|
||||
::: tip 数据字典的重要性
|
||||
数据字典是元数据管理最基础的产出。一个好的数据字典应该包含:
|
||||
- **字段名**:英文名和中文名
|
||||
- **数据类型**:VARCHAR(50)、INT、DATETIME 等
|
||||
- **业务定义**:这个字段代表什么?怎么计算的?
|
||||
- **取值范围**:有效值是什么?空值是否允许?
|
||||
- **负责人**:谁维护这个字段?有问题找谁?
|
||||
|
||||
没有数据字典的团队,新人入职后理解一张表的含义可能需要一周;有数据字典的团队,10 分钟就够了。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据分层架构:ODS → DWD → DWS → ADS
|
||||
|
||||
数据仓库不是把所有数据堆在一起,而是按照**加工程度**分层存储。每一层有明确的职责,上层依赖下层,逐步从原始数据提炼为业务可用的数据。
|
||||
|
||||
| 层级 | 全称 | 职责 | 数据特点 |
|
||||
|------|------|------|---------|
|
||||
| ODS | 操作数据层 | 原样同步业务数据库 | 最原始,未经处理 |
|
||||
| DWD | 明细数据层 | 清洗、标准化、去重 | 干净的明细记录 |
|
||||
| DWS | 汇总数据层 | 按主题聚合(日/周/月) | 预计算的聚合指标 |
|
||||
| ADS | 应用数据层 | 面向具体报表/接口 | 直接可用的结果数据 |
|
||||
|
||||
::: tip 为什么要分层?
|
||||
- **复用**:DWD 层清洗一次,所有上层共享,避免重复清洗
|
||||
- **解耦**:业务库表结构变更只影响 ODS 层,不会波及报表
|
||||
- **性能**:DWS 层预聚合,报表查询直接读取,不需要实时计算
|
||||
- **可追溯**:每一层都保留,出问题时可以逐层排查
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 6. 治理工具与实践
|
||||
|
||||
| 工具 | 定位 | 核心能力 | 适用场景 |
|
||||
|------|------|---------|---------|
|
||||
| Great Expectations | 数据质量 | 声明式数据校验规则,自动生成质量报告 | Python 数据管道 |
|
||||
| dbt | 数据转换 | SQL 模型化开发,内置测试和文档生成 | 数仓建模 |
|
||||
| DataHub | 元数据管理 | 数据目录、血缘追踪、数据发现 | 企业级数据治理 |
|
||||
| Apache Atlas | 元数据管理 | Hadoop 生态血缘追踪 | 大数据平台 |
|
||||
| OpenMetadata | 元数据管理 | 开源数据目录,支持多种数据源 | 中小团队 |
|
||||
| Amundsen | 数据发现 | 搜索式数据发现平台 | 数据民主化 |
|
||||
|
||||
::: tip 从零开始的治理路径
|
||||
如果你的团队还没有数据治理,建议按这个顺序推进:
|
||||
1. **先建数据字典**:把现有的表和字段含义记录下来(哪怕用 Excel)
|
||||
2. **加质量检查**:在关键数据管道中加入基本的空值、范围校验
|
||||
3. **统一指标定义**:把"日活""月活""GMV"等核心指标的计算口径统一
|
||||
4. **引入工具**:当手动管理成本太高时,引入 DataHub 或 dbt 等工具
|
||||
5. **建立流程**:数据变更需要评审,质量问题有 SLA 和告警
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
数据治理是让数据从"能用"变成"好用、可信、可追溯"的系统性工程。它不是一次性项目,而是持续运营的过程。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **六大质量维度**:完整性、准确性、一致性、时效性、唯一性、有效性
|
||||
2. **治理四支柱**:组织、流程、技术、文化缺一不可
|
||||
3. **数据血缘**:追踪数据的来龙去脉,支撑影响分析和问题排查
|
||||
4. **元数据管理**:数据字典是最基础也最重要的治理产出
|
||||
5. **分层架构**:ODS → DWD → DWS → ADS,逐层提炼数据价值
|
||||
6. **渐进式落地**:从数据字典开始,逐步引入工具和流程
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [DAMA-DMBOK](https://www.dama.org/cpages/body-of-knowledge) - 数据管理知识体系,数据治理的"圣经"
|
||||
- [DataHub](https://datahubproject.io/) - LinkedIn 开源的元数据管理平台
|
||||
- [Great Expectations](https://greatexpectations.io/) - Python 数据质量框架
|
||||
- [dbt](https://www.getdbt.com/) - 数据转换工具,内置测试和文档
|
||||
- [Apache Atlas](https://atlas.apache.org/) - Hadoop 生态的元数据治理框架
|
||||
- [The Data Warehouse Toolkit](https://www.kimballgroup.com/data-warehouse-business-intelligence-resources/books/) - Kimball 数仓建模经典
|
||||
|
||||
@@ -1,3 +1,299 @@
|
||||
# 数据可视化与仪表盘
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**一张好的图表胜过一千行数据。** 数据可视化是将抽象的数字转化为直观的视觉表达,让人能在几秒内理解数据背后的故事。从 Excel 图表到 Grafana 监控大屏,可视化无处不在。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **图表选择**:根据数据目的选择最合适的图表类型
|
||||
- **可视化原则**:掌握数据可视化的核心设计原则
|
||||
- **仪表盘设计**:了解不同类型仪表盘的布局模式
|
||||
- **工具生态**:熟悉主流可视化工具的定位和选型
|
||||
- **常见陷阱**:避免误导性图表和常见的可视化错误
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 图表类型选择 | 比较、趋势、占比、分布、关系 |
|
||||
| **第 2 章** | 可视化设计原则 | 数据墨水比、一致性、可读性 |
|
||||
| **第 3 章** | 仪表盘布局 | 概览型、对比型、下钻型、实时型 |
|
||||
| **第 4 章** | 工具选型 | ECharts、D3、Grafana、Metabase |
|
||||
| **第 5 章** | 常见陷阱 | 截断坐标轴、3D 饼图、颜色滥用 |
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:为什么需要可视化?
|
||||
|
||||
人类大脑处理视觉信息的速度比处理文字快 6 万倍。一张折线图能让你在 1 秒内看出"上个月销售额在下降",而同样的信息如果用表格呈现,你可能需要 30 秒才能得出结论。
|
||||
|
||||
可视化的核心价值:
|
||||
|
||||
- **发现模式**:趋势、周期、异常值在图表中一目了然
|
||||
- **辅助决策**:让非技术人员也能理解数据,参与决策
|
||||
- **沟通效率**:一图胜千言,减少数据解读的歧义
|
||||
|
||||
::: tip 可视化 ≠ 好看
|
||||
可视化的目标是**传达信息**,不是炫技。一个朴素但准确的柱状图,远比一个花哨但难以理解的 3D 图表更有价值。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 图表类型选择:用对图表讲对故事
|
||||
|
||||
选择图表的第一步不是"我喜欢什么图表",而是"我想传达什么信息"。不同的数据目的对应不同的最佳图表类型。
|
||||
|
||||
<ChartTypeSelectorDemo />
|
||||
|
||||
### 图表选择速查表
|
||||
|
||||
| 数据目的 | 推荐图表 | 不推荐 | 原因 |
|
||||
|---------|---------|--------|------|
|
||||
| 比较大小 | 柱状图、条形图 | 饼图 | 人眼对长度差异比角度差异更敏感 |
|
||||
| 展示趋势 | 折线图、面积图 | 柱状图 | 折线的连续性暗示时间的连续性 |
|
||||
| 展示占比 | 饼图(≤5 类)、堆叠柱状图 | 3D 饼图 | 3D 透视会扭曲面积比例 |
|
||||
| 展示分布 | 直方图、箱线图 | 折线图 | 分布需要看频率,不是趋势 |
|
||||
| 展示关系 | 散点图、气泡图 | 柱状图 | 两个连续变量的关系需要二维空间 |
|
||||
|
||||
::: tip 一个简单的决策规则
|
||||
- **一个变量** → 直方图(分布)或数字卡片(KPI)
|
||||
- **两个变量** → 折线图(时间 vs 数值)或散点图(数值 vs 数值)
|
||||
- **多个类别** → 柱状图(比较)或饼图(占比,≤5 类)
|
||||
- **多维度** → 雷达图或平行坐标图
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 可视化设计原则
|
||||
|
||||
Edward Tufte 在《The Visual Display of Quantitative Information》中提出了数据可视化的核心原则,至今仍是行业标准。
|
||||
|
||||
| 原则 | 说明 | 反面案例 |
|
||||
|------|------|---------|
|
||||
| 数据墨水比 | 图表中用于展示数据的"墨水"占比应尽量高 | 过多的网格线、装饰性元素 |
|
||||
| 最小化非数据元素 | 去除不传达信息的视觉元素 | 3D 效果、阴影、渐变背景 |
|
||||
| 一致的比例尺 | 坐标轴从零开始,刻度均匀 | Y 轴从 95 开始(夸大差异) |
|
||||
| 合理的颜色使用 | 用颜色编码信息,而非装饰 | 彩虹色(无序)表示有序数据 |
|
||||
| 清晰的标注 | 标题、轴标签、图例缺一不可 | 没有单位、没有时间范围 |
|
||||
|
||||
::: tip 颜色使用的三条规则
|
||||
1. **同一指标用同一颜色**:收入在所有图表中都用蓝色,不要一会蓝一会绿
|
||||
2. **有序数据用渐变色**:温度从低到高用蓝→红渐变,不要用离散色
|
||||
3. **考虑色盲友好**:约 8% 的男性有红绿色盲,避免仅用红绿区分关键信息
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 可视化设计原则:让数据说话
|
||||
|
||||
好的可视化不是"好看",而是"好懂"。Edward Tufte 在《The Visual Display of Quantitative Information》中提出了几个经典原则,至今仍是可视化设计的黄金标准。
|
||||
|
||||
### 2.1 数据墨水比(Data-Ink Ratio)
|
||||
|
||||
> 图表中用于表达数据的"墨水"占总"墨水"的比例应该尽可能高。
|
||||
|
||||
简单说:**删掉一切不传达信息的元素**。
|
||||
|
||||
| 应该删掉的 | 应该保留的 |
|
||||
|-----------|-----------|
|
||||
| 3D 效果、阴影、渐变 | 数据点、坐标轴标签 |
|
||||
| 多余的网格线 | 关键参考线(如目标值) |
|
||||
| 装饰性图标 | 图例(当有多系列时) |
|
||||
| 花哨的背景色 | 清晰的标题和单位 |
|
||||
|
||||
### 2.2 一致性原则
|
||||
|
||||
- **颜色一致**:同一个维度在不同图表中用同一种颜色(如"收入"始终用蓝色)
|
||||
- **比例一致**:坐标轴从 0 开始(除非有充分理由),避免误导
|
||||
- **时间一致**:时间轴的间隔应该均匀,不要跳跃
|
||||
|
||||
### 2.3 可读性原则
|
||||
|
||||
- **标题要说结论**:不是"月度销售额",而是"销售额连续 3 个月下降"
|
||||
- **标注关键点**:在异常值、拐点处加标注,引导读者注意力
|
||||
- **控制信息密度**:一张图表传达 1-2 个核心信息,不要塞太多
|
||||
|
||||
::: tip Tufte 的名言
|
||||
"Excellence in statistical graphics consists of complex ideas communicated with clarity, precision, and efficiency."(优秀的统计图形,是用清晰、精确、高效的方式传达复杂的想法。)
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 仪表盘布局:不同场景,不同模式
|
||||
|
||||
仪表盘(Dashboard)是多个图表的有机组合。好的仪表盘不是把图表堆在一起,而是根据使用场景选择合适的布局模式。
|
||||
|
||||
<DashboardLayoutDemo />
|
||||
|
||||
### 四种常见布局模式
|
||||
|
||||
| 布局模式 | 核心结构 | 适用场景 | 设计要点 |
|
||||
|---------|---------|---------|---------|
|
||||
| 全局概览型 | KPI 卡片 + 趋势图 + 明细表 | 管理层日报、运营大盘 | 核心指标放最上方,一眼看到关键数字 |
|
||||
| 对比分析型 | 左右对称布局 | A/B 测试、同环比分析 | 保持对比维度一致,突出差异 |
|
||||
| 下钻分析型 | 从汇总到明细逐层展开 | 销售分析、用户行为分析 | 支持点击交互,逐层深入 |
|
||||
| 实时监控型 | 大数字 + 实时曲线 + 告警状态 | 双十一大屏、服务器监控 | 自动刷新,深色背景,适合投屏 |
|
||||
|
||||
### 仪表盘设计的 5 个原则
|
||||
|
||||
1. **先问"谁在看"**:CEO 看战略指标,运营看过程指标,工程师看技术指标
|
||||
2. **5 秒规则**:用户应该在 5 秒内理解仪表盘的核心信息
|
||||
3. **信息层次**:最重要的放左上角(F 型阅读模式),次要的放下方
|
||||
4. **减少滚动**:一屏展示核心内容,避免用户需要滚动才能看到关键数据
|
||||
5. **留白**:不要塞满每一寸空间,适当留白让视觉更舒适
|
||||
|
||||
::: tip 仪表盘 vs 报表
|
||||
- **仪表盘**:实时/准实时,交互式,面向监控和快速决策
|
||||
- **报表**:定期生成(日/周/月),静态,面向详细分析和存档
|
||||
|
||||
两者不是替代关系,而是互补关系。仪表盘发现问题,报表深入分析。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 工具选型:从代码到拖拽
|
||||
|
||||
| 工具 | 类型 | 特点 | 适用场景 |
|
||||
|------|------|------|---------|
|
||||
| ECharts | JS 图表库 | 百度开源,图表类型丰富,中文文档完善 | 前端项目内嵌图表 |
|
||||
| D3.js | JS 可视化库 | 底层灵活,可定制任意可视化效果 | 高度定制化需求 |
|
||||
| Chart.js | JS 图表库 | 轻量简单,上手快 | 简单图表需求 |
|
||||
| Grafana | 监控仪表盘 | 支持多数据源,实时刷新,告警集成 | 服务器/应用监控 |
|
||||
| Metabase | BI 工具 | 开源,SQL 查询 + 拖拽建图 | 业务数据分析 |
|
||||
| Superset | BI 工具 | Apache 开源,支持大数据源 | 企业级数据探索 |
|
||||
| Tableau | 商业 BI | 拖拽式,交互能力强 | 企业级报表 |
|
||||
|
||||
::: tip 怎么选?
|
||||
- **开发者做项目内嵌图表** → ECharts(功能全)或 Chart.js(轻量)
|
||||
- **需要高度定制的可视化** → D3.js(学习曲线陡但无限可能)
|
||||
- **运维监控大屏** → Grafana(行业标准)
|
||||
- **业务团队自助分析** → Metabase(简单)或 Superset(功能强)
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 工具选型:从代码库到 BI 平台
|
||||
|
||||
可视化工具可以分为三个层次:底层绑定库、高层图表库、BI 平台。选择哪个取决于你的需求复杂度和团队技术能力。
|
||||
|
||||
### 4.1 代码级图表库
|
||||
|
||||
| 工具 | 语言/平台 | 特点 | 适用场景 |
|
||||
|------|----------|------|---------|
|
||||
| ECharts | JavaScript | 开箱即用,图表类型丰富,中文文档完善 | 业务系统内嵌图表 |
|
||||
| D3.js | JavaScript | 底层灵活,可定制任何可视化效果 | 高度定制化的数据可视化 |
|
||||
| Chart.js | JavaScript | 轻量简单,上手快 | 简单的图表需求 |
|
||||
| Matplotlib | Python | 科学计算标准库,静态图表 | 数据分析、论文图表 |
|
||||
| Plotly | Python/JS | 交互式图表,支持 3D | 数据探索、Jupyter Notebook |
|
||||
|
||||
### 4.2 BI 平台(无代码/低代码)
|
||||
|
||||
| 工具 | 定位 | 核心优势 | 适用团队 |
|
||||
|------|------|---------|---------|
|
||||
| Grafana | 监控可视化 | 时序数据支持好,告警集成 | 运维/SRE 团队 |
|
||||
| Metabase | 轻量 BI | 开源免费,SQL 即可出图 | 中小团队快速搭建 |
|
||||
| Apache Superset | 企业 BI | 开源,支持大数据源 | 有数据团队的公司 |
|
||||
| Tableau | 商业 BI | 拖拽式操作,可视化效果好 | 业务分析师 |
|
||||
| Power BI | 商业 BI | 与微软生态集成好 | 使用微软技术栈的企业 |
|
||||
|
||||
::: tip 选型建议
|
||||
- **开发者做产品内嵌图表** → ECharts(中文生态好)或 Chart.js(简单场景)
|
||||
- **数据分析师做探索分析** → Plotly + Jupyter 或 Metabase
|
||||
- **运维监控大屏** → Grafana(事实标准)
|
||||
- **业务团队自助分析** → Metabase(开源)或 Tableau(商业)
|
||||
- **需要高度定制** → D3.js(学习曲线陡峭,但无所不能)
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 常见陷阱:这些图表在骗你
|
||||
|
||||
数据可视化是一把双刃剑——用得好能揭示真相,用得不好会制造谎言。以下是最常见的可视化陷阱,每个数据从业者都应该能识别。
|
||||
|
||||
### 5.1 截断坐标轴
|
||||
|
||||
把 Y 轴的起点从 0 改成一个较大的数字,会让微小的差异看起来像巨大的变化。
|
||||
|
||||
| 场景 | 真实差异 | 视觉感受 |
|
||||
|------|---------|---------|
|
||||
| Y 轴从 0 开始 | A 产品 98 分,B 产品 95 分 | 差距很小 |
|
||||
| Y 轴从 90 开始 | 同样的数据 | A 看起来是 B 的好几倍 |
|
||||
|
||||
**什么时候可以截断?** 当数据的绝对值很大但变化很小时(如股价从 100 到 105),截断是合理的——但必须明确标注。
|
||||
|
||||
### 5.2 3D 饼图的透视陷阱
|
||||
|
||||
3D 透视会让靠近观察者的扇区看起来更大。一个 25% 的扇区在 3D 视角下可能看起来像 35%。
|
||||
|
||||
**解决方案**:永远不要用 3D 饼图。用普通饼图或环形图,或者干脆用柱状图。
|
||||
|
||||
### 5.3 颜色滥用
|
||||
|
||||
| 错误做法 | 正确做法 |
|
||||
|---------|---------|
|
||||
| 用红绿表示数据(色盲不友好) | 用蓝橙等色盲安全配色 |
|
||||
| 每个类别用不同颜色(彩虹图) | 同一系列用同色系深浅变化 |
|
||||
| 用颜色编码连续数据但不加图例 | 始终提供颜色图例和数值标注 |
|
||||
| 背景色和数据色对比度不够 | 确保 WCAG AA 级对比度 |
|
||||
|
||||
### 5.4 其他常见错误
|
||||
|
||||
| 陷阱 | 问题 | 修复 |
|
||||
|------|------|------|
|
||||
| 双 Y 轴 | 两个不相关的指标共享 X 轴,暗示因果关系 | 拆成两张图,或明确说明无因果 |
|
||||
| 面积误导 | 用圆的半径而非面积表示数值 | 数值翻倍时面积翻倍,不是半径翻倍 |
|
||||
| 时间轴不均匀 | 1月、3月、12月的间距一样 | 按实际时间比例排列 |
|
||||
| 过多类别 | 饼图有 15 个扇区 | 超过 5 个类别就用柱状图或合并"其他" |
|
||||
|
||||
::: tip 可视化的道德准则
|
||||
可视化的目的是**帮助理解**,不是**操纵认知**。每次做图表时问自己:
|
||||
- 如果我是读者,这张图会不会让我产生错误的结论?
|
||||
- 我是否隐藏了不利的数据?
|
||||
- 坐标轴、比例、颜色是否公正地呈现了数据?
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
数据可视化是数据价值传递的"最后一公里"。选对图表、遵循设计原则、避免常见陷阱,就能让数据真正"说话"。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **先问目的再选图表**:比较用柱状图、趋势用折线图、占比用饼图
|
||||
2. **数据墨水比**:删掉一切不传达信息的视觉元素
|
||||
3. **仪表盘有模式**:概览型、对比型、下钻型、实时型各有适用场景
|
||||
4. **工具按需选**:ECharts 做产品、Grafana 做监控、Metabase 做分析
|
||||
5. **警惕视觉陷阱**:截断坐标轴、3D 饼图、颜色滥用都会误导读者
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [ECharts 官方文档](https://echarts.apache.org/zh/index.html) - 最流行的中文图表库
|
||||
- [D3.js](https://d3js.org/) - 数据驱动的可视化库
|
||||
- [Grafana](https://grafana.com/) - 开源监控可视化平台
|
||||
- [The Visual Display of Quantitative Information](https://www.edwardtufte.com/tufte/books_vdqi) - Tufte 的可视化经典
|
||||
- [From Data to Viz](https://www.data-to-viz.com/) - 图表类型选择指南
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
数据可视化是数据价值传递的"最后一公里"。再好的分析,如果不能被正确理解,就等于没有分析。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **选对图表**:根据数据目的(比较、趋势、占比、分布、关系)选择图表类型
|
||||
2. **设计原则**:高数据墨水比、一致性、可读性是三大核心原则
|
||||
3. **仪表盘布局**:概览型、对比型、下钻型、实时型四种模式覆盖大部分场景
|
||||
4. **工具选型**:从 ECharts 到 Grafana,根据团队能力和需求复杂度选择
|
||||
5. **避免陷阱**:截断坐标轴、3D 饼图、颜色滥用是最常见的误导手段
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [The Visual Display of Quantitative Information](https://www.edwardtufte.com/tufte/books_vdqi) - Edward Tufte 的可视化经典
|
||||
- [ECharts 官方文档](https://echarts.apache.org/zh/index.html) - 最流行的中文图表库
|
||||
- [D3.js](https://d3js.org/) - 最强大的底层可视化库
|
||||
- [Grafana](https://grafana.com/) - 监控可视化事实标准
|
||||
- [From Data to Viz](https://www.data-to-viz.com/) - 图表类型选择决策树
|
||||
- [ColorBrewer](https://colorbrewer2.org/) - 色盲安全的配色方案工具
|
||||
|
||||
@@ -1,3 +1,253 @@
|
||||
# 分布式系统的挑战
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**当一台机器不够用时,问题才真正开始。** 分布式系统是现代互联网的基石——从微信消息到淘宝下单,背后都是成百上千台机器协同工作。但"分布式"不是免费的午餐,它带来了一系列单机系统从未遇到的挑战。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **核心定理**:理解 CAP 定理及其对系统设计的影响
|
||||
- **一致性模型**:区分强一致性、最终一致性、因果一致性
|
||||
- **八大挑战**:掌握分布式系统面临的核心难题
|
||||
- **共识算法**:了解 Paxos、Raft 等分布式共识的基本思想
|
||||
- **实战模式**:熟悉 2PC、Saga、CRDT 等常用解决方案
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 为什么需要分布式 | 扩展性、可用性、地理分布 |
|
||||
| **第 2 章** | CAP 定理 | 一致性、可用性、分区容错 |
|
||||
| **第 3 章** | 一致性模型 | 强一致、最终一致、因果一致 |
|
||||
| **第 4 章** | 八大挑战 | 网络、时钟、分区、脑裂等 |
|
||||
| **第 5 章** | 共识算法 | Paxos、Raft、ZAB |
|
||||
| **第 6 章** | 分布式事务 | 2PC、Saga、TCC |
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:为什么需要分布式系统?
|
||||
|
||||
单机系统简单可靠,但有三个无法逾越的瓶颈:
|
||||
|
||||
| 瓶颈 | 说明 | 分布式的解法 |
|
||||
|------|------|-------------|
|
||||
| 性能上限 | 单机 CPU、内存、磁盘有物理极限 | 水平扩展:加更多机器分担负载 |
|
||||
| 单点故障 | 一台机器挂了,整个服务就挂了 | 冗余副本:多台机器互为备份 |
|
||||
| 地理延迟 | 用户在全球各地,单机只能在一个地方 | 多地部署:就近服务用户 |
|
||||
|
||||
::: tip 分布式的代价
|
||||
分布式系统解决了上面的问题,但引入了新的复杂性:网络不可靠、时钟不同步、部分失败、数据一致性……这些就是本文要讨论的"挑战"。
|
||||
|
||||
**Peter Deutsch 的分布式计算八大谬误**告诉我们,以下假设在分布式环境中都是错的:
|
||||
1. 网络是可靠的
|
||||
2. 延迟是零
|
||||
3. 带宽是无限的
|
||||
4. 网络是安全的
|
||||
5. 拓扑不会变化
|
||||
6. 只有一个管理员
|
||||
7. 传输成本是零
|
||||
8. 网络是同构的
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. CAP 定理:分布式系统的"不可能三角"
|
||||
|
||||
2000 年,Eric Brewer 提出了 CAP 猜想(后被证明为定理):一个分布式系统最多只能同时满足以下三个属性中的两个。
|
||||
|
||||
| 属性 | 含义 | 通俗理解 |
|
||||
|------|------|---------|
|
||||
| **C**onsistency(一致性) | 所有节点在同一时刻看到相同的数据 | 你在任何 ATM 查余额,结果都一样 |
|
||||
| **A**vailability(可用性) | 每个请求都能收到非错误的响应 | 系统永远能回应你,不会说"服务不可用" |
|
||||
| **P**artition tolerance(分区容错) | 网络分区时系统仍能继续运行 | 即使部分网线断了,系统还能工作 |
|
||||
|
||||
<CAPTheoremDemo />
|
||||
|
||||
### 为什么只能选两个?
|
||||
|
||||
在分布式环境中,网络分区(P)是不可避免的——光纤会被挖断、交换机会故障、数据中心会断网。所以 P 是必选项,实际的选择是在 C 和 A 之间权衡:
|
||||
|
||||
- **选 CP**:分区时拒绝不确定的请求,保证数据正确 → 适合金融、库存
|
||||
- **选 AP**:分区时继续服务,但数据可能暂时不一致 → 适合社交、内容
|
||||
|
||||
::: tip CAP 不是非黑即白
|
||||
现实中的系统不是简单的"CP 或 AP"。很多系统在不同操作上做不同的选择——比如同一个数据库,读操作可以是 AP(允许读旧数据),写操作可以是 CP(要求多数确认)。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 一致性模型:数据同步的"严格程度"
|
||||
|
||||
一致性不是一个开关(有或没有),而是一个光谱。不同的一致性模型在"正确性"和"性能"之间做不同的权衡。
|
||||
|
||||
<ConsistencyModelsDemo />
|
||||
|
||||
### 一致性模型对比
|
||||
|
||||
| 模型 | 保证 | 延迟 | 适用场景 |
|
||||
|------|------|------|---------|
|
||||
| 强一致性 | 读到的一定是最新写入的值 | 高(需等待同步) | 银行转账、库存扣减 |
|
||||
| 最终一致性 | 最终所有副本会一致,但中间可能读到旧值 | 低(写入立即返回) | 社交动态、DNS |
|
||||
| 因果一致性 | 有因果关系的操作保证顺序 | 中等 | 评论回复、协作编辑 |
|
||||
| 线性一致性 | 所有操作看起来像在单机上按顺序执行 | 最高 | 分布式锁、选主 |
|
||||
| 会话一致性 | 同一会话内保证读到自己的写入 | 低-中 | 用户个人数据 |
|
||||
|
||||
::: tip "读己之写"一致性
|
||||
最常见的实际需求是:用户修改了自己的数据后,自己能立即看到更新(但其他用户可以稍后看到)。这叫"Read Your Own Writes"一致性,是最终一致性的一个实用增强。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 八大挑战:分布式系统的"地雷阵"
|
||||
|
||||
分布式系统的复杂性不是来自某一个问题,而是多个问题交织在一起。以下是最核心的八大挑战。
|
||||
|
||||
<DistributedChallengesDemo />
|
||||
|
||||
### 挑战之间的关联
|
||||
|
||||
这八大挑战不是孤立的,它们相互关联:
|
||||
|
||||
- **网络不可靠** → 导致 **网络分区** → 触发 **CAP 权衡**
|
||||
- **时钟不同步** → 导致 **事件排序困难** → 影响 **数据一致性**
|
||||
- **部分失败** → 可能导致 **脑裂** → 需要 **共识算法** 来解决
|
||||
- **数据一致性** → 需要 **分布式事务** → 但事务又受 **网络不可靠** 影响
|
||||
|
||||
::: tip 没有银弹
|
||||
分布式系统没有"完美"的解决方案,只有"合适"的权衡。理解这些挑战的本质,才能在设计系统时做出正确的取舍。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 共识算法:如何让多台机器"达成一致"
|
||||
|
||||
共识算法是分布式系统的核心——它解决的问题是:多个节点如何就某个值达成一致?即使部分节点故障或网络延迟。
|
||||
|
||||
### 4.1 Paxos
|
||||
|
||||
Leslie Lamport 在 1990 年提出,是第一个被严格证明正确的共识算法。
|
||||
|
||||
| 角色 | 职责 |
|
||||
|------|------|
|
||||
| Proposer | 提出提案(值) |
|
||||
| Acceptor | 投票接受或拒绝提案 |
|
||||
| Learner | 学习最终被选定的值 |
|
||||
|
||||
**两阶段流程**:
|
||||
1. **Prepare 阶段**:Proposer 发送提案编号,Acceptor 承诺不再接受更小编号的提案
|
||||
2. **Accept 阶段**:Proposer 发送具体值,多数 Acceptor 接受则提案通过
|
||||
|
||||
::: tip Paxos 的问题
|
||||
Paxos 虽然正确,但出了名的难以理解和实现。Lamport 自己的论文用了一个希腊议会的比喻,结果让更多人困惑了。
|
||||
:::
|
||||
|
||||
### 4.2 Raft:为可理解性而生
|
||||
|
||||
2014 年 Diego Ongaro 提出 Raft,目标是做一个"容易理解的 Paxos"。它把共识问题分解为三个子问题:
|
||||
|
||||
| 子问题 | 说明 |
|
||||
|--------|------|
|
||||
| Leader 选举 | 集群中选出一个 Leader,所有写入都经过 Leader |
|
||||
| 日志复制 | Leader 将操作日志复制到所有 Follower |
|
||||
| 安全性 | 保证已提交的日志不会被覆盖 |
|
||||
|
||||
**Raft 的核心流程**:
|
||||
1. 集群启动时,所有节点都是 Follower
|
||||
2. 如果 Follower 超时没收到 Leader 心跳,就变成 Candidate 发起选举
|
||||
3. 获得多数票的 Candidate 成为新 Leader
|
||||
4. Leader 接收客户端请求,将日志复制到多数节点后提交
|
||||
|
||||
### 4.3 共识算法对比
|
||||
|
||||
| 算法 | 提出时间 | 可理解性 | 使用系统 |
|
||||
|------|---------|---------|---------|
|
||||
| Paxos | 1990 | 困难 | Google Chubby |
|
||||
| Raft | 2014 | 容易 | etcd、Consul、TiKV |
|
||||
| ZAB | 2011 | 中等 | ZooKeeper |
|
||||
| EPaxos | 2013 | 困难 | 学术研究为主 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 分布式事务:跨节点的"全有或全无"
|
||||
|
||||
单机数据库的事务靠本地锁和日志就能实现 ACID。但当一个业务操作涉及多个服务/数据库时,如何保证原子性?
|
||||
|
||||
### 5.1 两阶段提交(2PC)
|
||||
|
||||
最经典的分布式事务协议,分为两个阶段:
|
||||
|
||||
| 阶段 | 协调者动作 | 参与者动作 |
|
||||
|------|-----------|-----------|
|
||||
| Prepare | 问所有参与者"能提交吗?" | 执行操作但不提交,回复 Yes/No |
|
||||
| Commit | 如果全部 Yes,发送 Commit | 正式提交;如果有 No,全部回滚 |
|
||||
|
||||
**2PC 的问题**:
|
||||
- **阻塞**:Prepare 后如果协调者挂了,参与者会一直等待
|
||||
- **单点故障**:协调者是单点,挂了整个事务卡住
|
||||
- **性能差**:需要多次网络往返,锁持有时间长
|
||||
|
||||
### 5.2 Saga 模式
|
||||
|
||||
Saga 把一个大事务拆成多个本地事务,每个本地事务有对应的补偿操作。如果某一步失败,就逆序执行补偿。
|
||||
|
||||
**电商下单的 Saga 示例**:
|
||||
|
||||
| 步骤 | 正向操作 | 补偿操作 |
|
||||
|------|---------|---------|
|
||||
| T1 | 创建订单(待支付) | 取消订单 |
|
||||
| T2 | 扣减库存 | 恢复库存 |
|
||||
| T3 | 扣减余额 | 退还余额 |
|
||||
| T4 | 确认订单(已支付) | — |
|
||||
|
||||
如果 T3(扣减余额)失败:执行 C2(恢复库存)→ C1(取消订单)。
|
||||
|
||||
**两种编排方式**:
|
||||
- **编排式(Choreography)**:每个服务监听事件,自行决定下一步。简单但难以追踪全局状态
|
||||
- **协调式(Orchestration)**:有一个中心协调者控制流程。清晰但协调者是单点
|
||||
|
||||
### 5.3 TCC(Try-Confirm-Cancel)
|
||||
|
||||
TCC 是 2PC 的业务层实现,把每个操作分为三个阶段:
|
||||
|
||||
| 阶段 | 说明 | 示例(扣库存) |
|
||||
|------|------|---------------|
|
||||
| Try | 预留资源,但不真正执行 | 冻结 10 件库存(可用库存 -10,冻结库存 +10) |
|
||||
| Confirm | 确认执行,消耗预留资源 | 冻结库存 -10(真正扣减) |
|
||||
| Cancel | 取消预留,释放资源 | 冻结库存 -10,可用库存 +10(恢复) |
|
||||
|
||||
### 5.4 三种方案对比
|
||||
|
||||
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|
||||
|------|--------|------|--------|---------|
|
||||
| 2PC | 强一致 | 低 | 中 | 数据库层面的跨库事务 |
|
||||
| Saga | 最终一致 | 高 | 高 | 长流程业务(订单、物流) |
|
||||
| TCC | 最终一致 | 中 | 最高 | 资金类高可靠场景 |
|
||||
|
||||
::: tip 实际选择建议
|
||||
- 能用单库事务就不要用分布式事务
|
||||
- 大多数业务场景用 Saga + 消息队列就够了
|
||||
- TCC 适合对一致性要求极高的金融场景,但开发成本很高
|
||||
- 2PC 适合数据库中间件(如 ShardingSphere)自动处理
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
分布式系统是现代互联网的基础设施,但它的复杂性远超单机系统。理解这些挑战不是为了"解决"它们(很多是根本性的),而是为了在设计系统时做出正确的权衡。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **CAP 定理**:网络分区不可避免,实际选择是在一致性和可用性之间权衡
|
||||
2. **一致性模型**:从强一致到最终一致是一个光谱,根据业务需求选择
|
||||
3. **八大挑战**:网络不可靠、时钟不同步、网络分区、脑裂等相互关联
|
||||
4. **共识算法**:Raft 是目前最实用的共识算法,etcd/Consul 都基于它
|
||||
5. **分布式事务**:Saga 适合大多数场景,TCC 适合金融场景,2PC 适合数据库层
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Designing Data-Intensive Applications](https://dataintensive.net/) - Martin Kleppmann 的分布式系统经典
|
||||
- [The Raft Consensus Algorithm](https://raft.github.io/) - Raft 官方可视化演示
|
||||
- [CAP Twelve Years Later](https://www.infoq.com/articles/cap-twelve-years-later-how-the-rules-have-changed/) - Brewer 对 CAP 的重新审视
|
||||
- [Jepsen](https://jepsen.io/) - 分布式系统正确性测试框架
|
||||
- [分布式系统模式](https://martinfowler.com/articles/patterns-of-distributed-systems/) - Martin Fowler 的分布式模式合集
|
||||
@@ -1,3 +1,204 @@
|
||||
# 高可用与容灾
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**系统挂了 1 分钟,可能意味着几十万的损失。** 高可用(High Availability)是指系统在面对硬件故障、软件 Bug、网络问题等异常情况时,仍能持续提供服务的能力。容灾(Disaster Recovery)则是在更大范围的灾难发生时,系统能够恢复服务的能力。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **可用性度量**:理解"几个 9"的含义和对应的停机时间
|
||||
- **故障转移**:掌握主备、主主、多活等高可用架构
|
||||
- **容灾策略**:了解 RPO 和 RTO 的概念及设计方法
|
||||
- **故障检测**:理解心跳、探针、熔断等故障发现机制
|
||||
- **混沌工程**:了解如何主动注入故障来验证系统韧性
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 可用性度量 | SLA、几个 9、停机时间 |
|
||||
| **第 2 章** | 故障转移架构 | 主备、主主、多可用区、异地多活 |
|
||||
| **第 3 章** | 容灾设计 | RPO、RTO、备份策略 |
|
||||
| **第 4 章** | 故障检测与恢复 | 心跳、熔断、自动扩缩容 |
|
||||
| **第 5 章** | 混沌工程 | 故障注入、韧性验证 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 可用性度量:几个 9 意味着什么?
|
||||
|
||||
可用性通常用"几个 9"来衡量,计算公式为:
|
||||
|
||||
**可用性 = 正常运行时间 / 总时间 × 100%**
|
||||
|
||||
例如一个月(30 天 = 43200 分钟)内停机了 43 分钟,可用性就是 (43200 - 43) / 43200 ≈ 99.9%。每多一个 9,允许的停机时间就少一个数量级,实现难度和成本也指数级增长。
|
||||
|
||||
| 可用性等级 | 百分比 | 每月允许停机 | 每年允许停机 | 典型要求 |
|
||||
|-----------|--------|------------|------------|---------|
|
||||
| 2 个 9 | 99% | 7.3 小时 | 3.65 天 | 内部工具 |
|
||||
| 3 个 9 | 99.9% | 43 分钟 | 8.76 小时 | 普通业务系统 |
|
||||
| 4 个 9 | 99.99% | 4.3 分钟 | 52.6 分钟 | 电商、SaaS |
|
||||
| 5 个 9 | 99.999% | 26 秒 | 5.26 分钟 | 金融、支付 |
|
||||
|
||||
<AvailabilityCalculatorDemo />
|
||||
|
||||
::: tip SLA 是什么?
|
||||
**SLA(Service Level Agreement,服务等级协议)** 是服务提供方与客户之间的正式承诺。比如 AWS S3 承诺 99.99% 的可用性,如果没达到,会按比例退款。SLA 不只是技术指标,更是商业合同——违反 SLA 意味着赔钱。
|
||||
:::
|
||||
|
||||
::: tip 从 3 个 9 到 4 个 9 的鸿沟
|
||||
3 个 9(99.9%)意味着每月可以停机 43 分钟——一次部署出问题,回滚一下就用完了。
|
||||
4 个 9(99.99%)意味着每月只能停机 4 分钟——这要求你必须有自动故障转移、滚动部署、健康检查等完整的高可用体系。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 故障转移架构
|
||||
|
||||
故障转移(Failover)是高可用的核心机制:当主节点故障时,自动切换到备用节点继续提供服务。
|
||||
|
||||
### 主备模式(Active-Standby)
|
||||
|
||||
最常见的高可用架构。主节点处理所有请求,备节点实时同步数据但不处理请求。主节点故障时,备节点自动接管。
|
||||
|
||||
```
|
||||
正常状态:
|
||||
客户端 → 主节点(处理请求)
|
||||
备节点(同步数据,待命)
|
||||
|
||||
故障转移:
|
||||
客户端 → 备节点(接管为新主节点)
|
||||
原主节点(故障,等待修复)
|
||||
```
|
||||
|
||||
关键问题是**脑裂(Split Brain)**:网络分区时,主备节点都认为对方挂了,同时对外提供服务,导致数据不一致。解决方案是引入**仲裁节点(Quorum)**——至少 3 个节点投票决定谁是主节点。
|
||||
|
||||
### 多可用区(Multi-AZ)
|
||||
|
||||
将服务部署在同一地域的多个数据中心(可用区)。单个数据中心断电、断网不影响整体服务。云厂商的可用区之间通常有低延迟专线连接(< 2ms)。
|
||||
|
||||
### 异地多活(Multi-Region Active-Active)
|
||||
|
||||
在不同城市甚至不同国家部署完整的服务副本,每个站点都能独立处理请求。这是最高级别的高可用架构,但也最复杂——核心挑战是**跨地域数据同步**的延迟和一致性问题。
|
||||
|
||||
<FailoverStrategyDemo />
|
||||
|
||||
| 架构 | 可用性级别 | 成本 | 复杂度 | 适用场景 |
|
||||
|------|-----------|------|--------|---------|
|
||||
| 单机 | 99%~99.9% | 低 | 低 | 开发测试、内部工具 |
|
||||
| 主备 | 99.9%~99.99% | 中 | 中 | 中小型业务系统 |
|
||||
| 多可用区 | 99.99% | 高 | 高 | 电商、SaaS 平台 |
|
||||
| 异地多活 | 99.999% | 极高 | 极高 | 金融、大型互联网 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 容灾设计:RPO 与 RTO
|
||||
|
||||
容灾设计围绕两个核心指标展开:
|
||||
|
||||
| 指标 | 全称 | 含义 | 举例 |
|
||||
|------|------|------|------|
|
||||
| RPO | Recovery Point Objective | 能容忍丢失多少数据 | RPO=0 表示不能丢任何数据 |
|
||||
| RTO | Recovery Time Objective | 能容忍停机多长时间 | RTO=5min 表示 5 分钟内恢复 |
|
||||
|
||||
### 备份策略与 RPO 的关系
|
||||
|
||||
| 备份方式 | RPO | 成本 | 说明 |
|
||||
|---------|-----|------|------|
|
||||
| 每日全量备份 | 24 小时 | 低 | 最多丢一天数据 |
|
||||
| 实时增量备份 | 分钟级 | 中 | binlog/WAL 持续同步 |
|
||||
| 同步复制 | 0 | 高 | 写入必须等副本确认 |
|
||||
|
||||
::: tip 不是所有数据都需要 RPO=0
|
||||
用户头像丢了可以重新上传(RPO=24h 够了),但支付记录一条都不能丢(RPO=0)。根据数据的业务价值来决定备份策略,而不是一刀切。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 故障检测与恢复
|
||||
|
||||
### 4.1 故障检测机制
|
||||
|
||||
| 机制 | 原理 | 检测速度 | 适用场景 |
|
||||
|------|------|---------|---------|
|
||||
| 心跳检测 | 定期发送心跳包,超时判定故障 | 秒级 | 节点存活检测 |
|
||||
| 健康检查 | HTTP/TCP 探针检查服务状态 | 秒级 | 负载均衡器后端检测 |
|
||||
| 业务探针 | 模拟真实请求检查业务逻辑 | 秒~分钟级 | 端到端可用性监控 |
|
||||
|
||||
**心跳检测的工作原理**:节点 A 每隔固定时间(如 5 秒)向监控方发送一个"我还活着"的信号。如果连续 N 次(如 3 次)没收到心跳,就判定节点 A 故障。关键参数是**心跳间隔**和**超时阈值**——间隔太短会增加网络开销,太长会延迟故障发现。
|
||||
|
||||
**健康检查的三种级别**:
|
||||
- **存活探针(Liveness)**:进程还在运行吗?不在就重启
|
||||
- **就绪探针(Readiness)**:服务能接受请求吗?不能就从负载均衡中摘除
|
||||
- **启动探针(Startup)**:服务启动完成了吗?没完成就等待,不要误判为故障
|
||||
|
||||
### 4.2 自动恢复机制
|
||||
|
||||
| 机制 | 描述 | 典型工具 |
|
||||
|------|------|---------|
|
||||
| 自动重启 | 进程崩溃后自动拉起 | systemd、PM2、K8s |
|
||||
| 自动扩缩容 | 负载升高时自动增加实例 | K8s HPA、云厂商 Auto Scaling |
|
||||
| 熔断降级 | 下游故障时快速失败,防止级联故障 | Hystrix、Sentinel、Resilience4j |
|
||||
| 限流 | 超过容量的请求直接拒绝 | Nginx limit_req、网关限流 |
|
||||
|
||||
**熔断器模式(Circuit Breaker)详解**:
|
||||
|
||||
熔断器的灵感来自电路中的保险丝——当电流过大时自动断开,保护整个电路不被烧毁。在微服务中,当下游服务故障时,熔断器会"断开",让请求快速失败,而不是傻等超时。
|
||||
|
||||
```
|
||||
熔断器三种状态:
|
||||
|
||||
关闭(正常)──→ 失败率超过阈值 ──→ 打开(熔断)
|
||||
↑ │
|
||||
│ 等待冷却时间
|
||||
│ ↓
|
||||
└── 探测请求成功 ←── 半开(试探)
|
||||
```
|
||||
|
||||
- **关闭状态**:正常转发请求,同时统计失败率
|
||||
- **打开状态**:所有请求直接返回错误(快速失败),不再调用下游
|
||||
- **半开状态**:冷却时间到后,放行少量探测请求。如果成功,恢复关闭;如果失败,继续打开
|
||||
|
||||
**降级(Fallback)** 是熔断的配套策略:熔断触发后,不是直接报错,而是返回一个"兜底"结果。比如推荐服务挂了,就返回热门商品列表;用户头像加载失败,就显示默认头像。
|
||||
|
||||
---
|
||||
|
||||
## 5. 混沌工程:主动找问题
|
||||
|
||||
混沌工程的核心理念是:**与其等故障发生,不如主动制造故障**,在可控环境中验证系统的韧性。
|
||||
|
||||
| 工具 | 提出者 | 核心能力 |
|
||||
|------|--------|---------|
|
||||
| Chaos Monkey | Netflix | 随机终止生产环境的实例 |
|
||||
| Chaos Mesh | PingCAP | K8s 环境下的故障注入 |
|
||||
| Litmus | CNCF | 云原生混沌工程框架 |
|
||||
| ChaosBlade | 阿里巴巴 | 多场景故障注入工具 |
|
||||
|
||||
::: tip 混沌工程的实施步骤
|
||||
1. **定义稳态**:明确系统正常运行的指标(如 P99 延迟 < 200ms)
|
||||
2. **提出假设**:如果某个节点挂了,系统应该在 30 秒内自动恢复
|
||||
3. **注入故障**:在可控范围内制造故障(先在测试环境,再到生产)
|
||||
4. **观察结果**:系统是否如预期恢复?有没有级联故障?
|
||||
5. **修复弱点**:发现问题后改进架构和流程
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
高可用不是一个功能,而是一种架构能力。它需要从设计、开发、部署、运维的每个环节去保障。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **几个 9**:每多一个 9,停机时间少一个数量级,成本和复杂度指数增长
|
||||
2. **故障转移**:从主备到异地多活,根据业务需求选择合适的架构
|
||||
3. **RPO 与 RTO**:根据数据价值和业务容忍度设计备份和恢复策略
|
||||
4. **自动化**:故障检测、自动重启、熔断降级是高可用的基础设施
|
||||
5. **混沌工程**:主动制造故障,在可控环境中验证系统韧性
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Site Reliability Engineering](https://sre.google/sre-book/table-of-contents/) - Google SRE 经典
|
||||
- [Chaos Monkey](https://netflix.github.io/chaosmonkey/) - Netflix 混沌工程工具
|
||||
- [Release It!](https://pragprog.com/titles/mnee2/release-it-second-edition/) - 生产环境设计模式
|
||||
- [Chaos Mesh](https://chaos-mesh.org/) - K8s 混沌工程平台
|
||||
|
||||
|
||||
@@ -1,3 +1,156 @@
|
||||
# 从单体到微服务的演进
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**没有哪个架构是"最好的",只有"最适合当前阶段的"。** 从单体到微服务不是一步到位的跳跃,而是随着业务规模和团队规模增长,逐步演进的过程。过早拆分微服务和过晚拆分一样危险。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **演进路径**:理解从单体到微服务的四个阶段
|
||||
- **拆分时机**:知道什么时候该拆、什么时候不该拆
|
||||
- **拆分策略**:掌握按业务域拆分的方法论
|
||||
- **通信模式**:了解服务间同步和异步通信的选择
|
||||
- **数据拆分**:理解数据库拆分的挑战和方案
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 架构演进路径 | 单体→模块化→SOA→微服务 |
|
||||
| **第 2 章** | 拆分时机与原则 | Conway 定律、团队自治 |
|
||||
| **第 3 章** | 拆分策略 | DDD 限界上下文、绞杀者模式 |
|
||||
| **第 4 章** | 服务通信 | REST、gRPC、消息队列 |
|
||||
| **第 5 章** | 数据拆分 | 数据库拆分、数据同步 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 架构演进路径
|
||||
|
||||
架构演进不是技术驱动的,而是**组织规模驱动的**。当团队从 5 人增长到 500 人时,单体架构的协作效率会急剧下降。
|
||||
|
||||
| 阶段 | 架构 | 团队规模 | 特点 |
|
||||
|------|------|---------|------|
|
||||
| 起步期 | 单体应用 | 1~10 人 | 所有代码在一个项目中,部署简单 |
|
||||
| 成长期 | 模块化单体 | 10~50 人 | 代码按模块划分,但仍然一起部署 |
|
||||
| 扩张期 | SOA(面向服务) | 50~200 人 | 按业务线拆分为粗粒度服务 |
|
||||
| 规模期 | 微服务 | 200+ 人 | 细粒度服务,每个团队独立开发部署 |
|
||||
|
||||
<ArchEvolutionDemo />
|
||||
|
||||
::: tip Conway 定律
|
||||
"设计系统的组织,其产生的架构等同于组织的沟通结构。"——Melvin Conway
|
||||
|
||||
简单说:3 个团队做一个系统,最终会变成 3 个服务。架构拆分的本质是**组织拆分**。
|
||||
|
||||
**反向 Conway 定律**:既然组织结构决定了系统架构,那么想要什么样的架构,就先调整成什么样的组织结构。比如你想拆出独立的支付服务,就先组建一个独立的支付团队。很多公司微服务拆分失败,不是技术问题,而是组织没有跟着调整。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 什么时候该拆微服务?
|
||||
|
||||
不是所有系统都需要微服务。过早拆分会带来不必要的复杂性。
|
||||
|
||||
| 信号 | 说明 | 建议 |
|
||||
|------|------|------|
|
||||
| 部署冲突频繁 | 多个团队改同一个代码库,经常冲突 | 考虑拆分 |
|
||||
| 某模块需要独立扩容 | 搜索模块需要 10 倍于其他模块的资源 | 考虑拆分 |
|
||||
| 技术栈需要差异化 | AI 模块用 Python,主站用 Java | 考虑拆分 |
|
||||
| 团队 < 10 人 | 沟通成本低,单体足够 | 不要拆 |
|
||||
| 业务还在探索期 | 需求变化快,边界不清晰 | 不要拆 |
|
||||
| 没有 DevOps 能力 | 没有 CI/CD、容器化、监控体系 | 不要拆 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 拆分策略
|
||||
|
||||
### 3.1 按业务域拆分(DDD 限界上下文)
|
||||
|
||||
DDD(领域驱动设计)的限界上下文(Bounded Context)是拆分微服务的最佳指导原则。每个限界上下文对应一个独立的业务域,有自己的数据模型和业务规则。
|
||||
|
||||
**什么是限界上下文?** 同一个词在不同业务域中含义不同。比如"用户"在用户域是指注册信息(姓名、邮箱),在订单域是指下单人(收货地址、支付方式),在推荐域是指行为画像(浏览历史、偏好标签)。限界上下文就是划定一个边界,在这个边界内,术语和模型有明确统一的含义。
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ 用户域 │ │ 订单域 │ │ 支付域 │
|
||||
│ │ │ │ │ │
|
||||
│ User │ │ Order │ │ Payment │
|
||||
│ Profile │ │ OrderItem │ │ Refund │
|
||||
│ Address │ │ Cart │ │ Transaction │
|
||||
│ │ │ │ │ │
|
||||
│ 用户服务 │ │ 订单服务 │ │ 支付服务 │
|
||||
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
|
||||
│ │ │
|
||||
└────── API 调用 / 事件通信 ───────┘
|
||||
```
|
||||
|
||||
| 限界上下文 | 核心实体 | 对应服务 |
|
||||
|-----------|---------|---------|
|
||||
| 用户域 | User、Profile、Address | 用户服务 |
|
||||
| 商品域 | Product、Category、SKU | 商品服务 |
|
||||
| 订单域 | Order、OrderItem | 订单服务 |
|
||||
| 支付域 | Payment、Refund | 支付服务 |
|
||||
| 物流域 | Shipment、Tracking | 物流服务 |
|
||||
|
||||
### 3.2 绞杀者模式(Strangler Fig Pattern)
|
||||
|
||||
不要一次性重写整个单体,而是像绞杀榕一样,逐步用新服务替换旧模块:
|
||||
|
||||
1. 在单体外部创建新服务
|
||||
2. 通过代理层将部分流量路由到新服务
|
||||
3. 验证新服务稳定后,逐步迁移更多流量
|
||||
4. 最终完全替换旧模块
|
||||
|
||||
---
|
||||
|
||||
## 4. 服务通信模式
|
||||
|
||||
| 方式 | 协议 | 特点 | 适用场景 |
|
||||
|------|------|------|---------|
|
||||
| REST | HTTP/JSON | 简单通用,生态好 | 对外 API、CRUD 操作 |
|
||||
| gRPC | HTTP/2 + Protobuf | 高性能,强类型 | 内部服务间高频调用 |
|
||||
| 消息队列 | AMQP/Kafka | 异步解耦,削峰填谷 | 事件通知、异步任务 |
|
||||
| GraphQL | HTTP/JSON | 客户端按需查询 | BFF 层、移动端 |
|
||||
|
||||
::: tip 同步 vs 异步的选择
|
||||
- **需要立即返回结果** → 同步(REST/gRPC)
|
||||
- **不需要立即返回** → 异步(消息队列)
|
||||
- **一个事件触发多个动作** → 异步(发布-订阅)
|
||||
|
||||
经验法则:能异步就异步,同步调用链越长,系统越脆弱。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据拆分:最难的部分
|
||||
|
||||
微服务拆分中最痛苦的不是代码拆分,而是数据库拆分。每个服务应该拥有自己的数据库,但这意味着跨服务查询变得困难。
|
||||
|
||||
| 挑战 | 描述 | 解决方案 |
|
||||
|------|------|---------|
|
||||
| 跨服务 JOIN | 不能直接 JOIN 两个服务的表 | API 组合查询、数据冗余 |
|
||||
| 分布式事务 | 跨库事务无法用本地事务 | Saga、本地消息表 |
|
||||
| 数据一致性 | 多个服务的数据可能暂时不一致 | 最终一致性、事件驱动 |
|
||||
| 数据迁移 | 从共享库迁移到独立库 | 双写过渡、数据同步工具 |
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
从单体到微服务是一个渐进的过程,不是一蹴而就的革命。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **演进路径**:单体→模块化单体→SOA→微服务,每一步都有明确的驱动力
|
||||
2. **拆分时机**:团队规模、部署冲突、扩容需求是拆分的信号
|
||||
3. **拆分策略**:用 DDD 限界上下文指导拆分,用绞杀者模式渐进迁移
|
||||
4. **通信选择**:能异步就异步,同步调用链越短越好
|
||||
5. **数据拆分**:最难但最重要,接受最终一致性是关键心态转变
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Building Microservices](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/) - Sam Newman 微服务经典
|
||||
- [Monolith to Microservices](https://www.oreilly.com/library/view/monolith-to-microservices/9781492047834/) - 渐进式迁移指南
|
||||
- [Domain-Driven Design](https://www.domainlanguage.com/ddd/) - Eric Evans 的 DDD 经典
|
||||
- [The Strangler Fig Pattern](https://martinfowler.com/bliki/StranglerFigApplication.html) - Martin Fowler 的绞杀者模式
|
||||
|
||||
|
||||
@@ -1,3 +1,252 @@
|
||||
# 系统设计方法论
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**系统设计不是拍脑袋画架构图,而是一套有章可循的方法论。** 无论是面试中的系统设计题,还是实际工作中的架构设计,都遵循相似的思考框架:先搞清楚问题,再估算规模,然后设计方案,最后深入优化。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **设计流程**:掌握系统设计的四步法框架
|
||||
- **容量估算**:学会"信封背面估算"的技巧
|
||||
- **常见模式**:熟悉缓存、分库分表、消息队列等核心模式
|
||||
- **权衡思维**:理解架构设计中的 trade-off 思维
|
||||
- **实战案例**:通过短链服务、Feed 流等案例理解设计过程
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 设计四步法 | 需求澄清、容量估算、架构设计、深入优化 |
|
||||
| **第 2 章** | 容量估算 | QPS、存储、带宽、信封背面估算 |
|
||||
| **第 3 章** | 核心设计模式 | 缓存、分库分表、消息队列、CDN |
|
||||
| **第 4 章** | 权衡思维 | 一致性 vs 可用性、性能 vs 成本 |
|
||||
| **第 5 章** | 经典案例 | 短链服务、Feed 流、秒杀系统 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 系统设计四步法
|
||||
|
||||
系统设计不是一上来就画架构图。无论是面试还是实战,都应该遵循一个结构化的流程。
|
||||
|
||||
<SystemDesignStepsDemo />
|
||||
|
||||
::: tip 为什么要先澄清需求?
|
||||
很多人拿到题目就开始画图,结果设计了一个"正确但不是面试官想要的"系统。花 5 分钟问清楚需求,能避免后面 30 分钟的返工。
|
||||
|
||||
常见的澄清问题:
|
||||
- 系统的核心功能是什么?(不要设计所有功能)
|
||||
- 用户规模多大?(决定是否需要分布式)
|
||||
- 读写比例?(决定缓存策略)
|
||||
- 数据需要保留多久?(决定存储方案)
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 容量估算:信封背面的艺术
|
||||
|
||||
"信封背面估算"(Back-of-envelope estimation)是系统设计中的核心技能。不需要精确计算,只需要知道量级。
|
||||
|
||||
<CapacityEstimationDemo />
|
||||
|
||||
### 常用换算速查
|
||||
|
||||
| 量级 | 换算 | 记忆技巧 |
|
||||
|------|------|---------|
|
||||
| 1 天 | 86,400 秒 | ≈ 10 万秒 |
|
||||
| 1 亿请求/天 | ≈ 1,200 QPS | 除以 10 万 |
|
||||
| 1 KB × 1 亿 | ≈ 100 GB | 1 亿条小记录 |
|
||||
| 1 MB × 100 万 | ≈ 1 TB | 100 万张图片 |
|
||||
|
||||
### 2-8 法则在估算中的应用
|
||||
|
||||
大多数系统遵循 80/20 法则:20% 的数据承载 80% 的请求。这意味着:
|
||||
|
||||
- **缓存大小** ≈ 总数据量 × 20%
|
||||
- **热点 QPS** ≈ 总 QPS × 80% 集中在 20% 的 key 上
|
||||
- **缓存命中率**目标 ≈ 80%+(低于这个值说明缓存策略有问题)
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心设计模式
|
||||
|
||||
系统设计中反复出现的模式,掌握这些就能应对大多数场景。
|
||||
|
||||
### 3.1 缓存模式
|
||||
|
||||
| 模式 | 读路径 | 写路径 | 适用场景 |
|
||||
|------|--------|--------|---------|
|
||||
| Cache-Aside | 先查缓存,miss 则查 DB 并回填 | 先写 DB,再删缓存 | 通用场景,最常用 |
|
||||
| Read-Through | 缓存层自动从 DB 加载 | 同 Cache-Aside | 需要缓存框架支持 |
|
||||
| Write-Behind | 同 Cache-Aside | 先写缓存,异步写 DB | 写密集型,可容忍丢数据 |
|
||||
|
||||
::: tip 为什么是"删缓存"而不是"更新缓存"?
|
||||
更新缓存在并发场景下容易出现数据不一致:线程 A 和 B 同时更新,A 先写 DB 但 B 先更新缓存,导致缓存中是 B 的旧值。删除缓存则让下次读请求重新从 DB 加载,天然避免这个问题。
|
||||
:::
|
||||
|
||||
### 3.2 分库分表
|
||||
|
||||
当单表数据量超过千万级,或单库 QPS 超过瓶颈时,就需要考虑分库分表。
|
||||
|
||||
| 策略 | 做法 | 优点 | 缺点 |
|
||||
|------|------|------|------|
|
||||
| 垂直分库 | 按业务域拆分数据库 | 业务解耦,独立扩展 | 跨库 JOIN 困难 |
|
||||
| 水平分表 | 同一张表按规则拆成多张 | 单表数据量可控 | 分片键选择关键 |
|
||||
| 垂直分表 | 把大字段拆到独立表 | 减少 IO,提升查询效率 | 需要额外 JOIN |
|
||||
|
||||
**分片键选择原则**:
|
||||
- 选择查询最频繁的字段(如 user_id)
|
||||
- 数据分布要均匀,避免热点
|
||||
- 尽量让同一用户的数据在同一分片(减少跨分片查询)
|
||||
|
||||
### 3.3 消息队列
|
||||
|
||||
消息队列是分布式系统的"减震器",核心作用是解耦、异步、削峰。
|
||||
|
||||
| 场景 | 不用队列 | 用队列 |
|
||||
|------|---------|--------|
|
||||
| 下单后发通知 | 下单接口同步调用通知服务,通知失败导致下单失败 | 下单成功后发消息,通知服务异步消费 |
|
||||
| 秒杀抢购 | 瞬间流量打爆数据库 | 请求先入队列,后端按能力消费 |
|
||||
| 数据同步 | 服务 A 直接调用服务 B 的接口 | 服务 A 发事件,服务 B 订阅处理 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 权衡思维:没有银弹
|
||||
|
||||
架构设计的本质是权衡(Trade-off)。每个决策都有代价,关键是理解代价并做出适合当前阶段的选择。
|
||||
|
||||
| 权衡维度 | 选项 A | 选项 B | 决策依据 |
|
||||
|---------|--------|--------|---------|
|
||||
| 一致性 vs 可用性 | 强一致(CP) | 高可用(AP) | 业务能否容忍短暂不一致? |
|
||||
| 性能 vs 成本 | 全量缓存 | 按需缓存 | 数据量和预算 |
|
||||
| 简单 vs 灵活 | 单体架构 | 微服务 | 团队规模和业务复杂度 |
|
||||
| 实时 vs 批量 | 流式处理 | 批处理 | 数据时效性要求 |
|
||||
| 自建 vs 托管 | 自己搭 MySQL | 用云数据库 RDS | 运维能力和成本 |
|
||||
|
||||
::: tip 架构决策记录(ADR)
|
||||
每个重要的架构决策都应该记录下来:**背景是什么、考虑了哪些方案、为什么选了这个、有什么代价**。这不是为了甩锅,而是为了让后来的人理解"为什么当时这么设计"。
|
||||
|
||||
格式很简单:
|
||||
- **标题**:用 XXX 替代 YYY
|
||||
- **背景**:我们遇到了什么问题
|
||||
- **决策**:我们选择了什么方案
|
||||
- **理由**:为什么选这个
|
||||
- **代价**:这个决策的缺点和风险
|
||||
:::
|
||||
|
||||
### 常见的错误权衡
|
||||
|
||||
| 错误 | 表现 | 正确做法 |
|
||||
|------|------|---------|
|
||||
| 过早优化 | 日活 1000 就上分库分表 | 先用单库,遇到瓶颈再拆 |
|
||||
| 技术驱动 | "我想用 Kafka" 而不是 "我需要异步" | 从问题出发,而非从技术出发 |
|
||||
| 忽略运维成本 | 选了最优方案但团队维护不了 | 方案要匹配团队能力 |
|
||||
| 追求完美一致性 | 所有场景都用分布式事务 | 大多数场景最终一致性就够了 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 经典案例
|
||||
|
||||
通过三个经典案例,把前面学到的方法论串起来。
|
||||
|
||||
### 5.1 短链服务(TinyURL)
|
||||
|
||||
短链服务是系统设计面试的经典题目,麻雀虽小五脏俱全。
|
||||
|
||||
**需求澄清**:
|
||||
- 核心功能:长链接 → 短链接(写),短链接 → 重定向(读)
|
||||
- 读写比:约 100:1(读远多于写)
|
||||
- 日均重定向:1 亿次
|
||||
- 短链永不过期
|
||||
|
||||
**容量估算**:
|
||||
|
||||
| 指标 | 计算 | 结果 |
|
||||
|------|------|------|
|
||||
| 写 QPS | 1 亿 / 100 / 86400 | ≈ 12 QPS |
|
||||
| 读 QPS | 1 亿 / 86400 | ≈ 1,200 QPS |
|
||||
| 峰值读 QPS | 1,200 × 3 | ≈ 3,600 QPS |
|
||||
| 5 年存储 | 100 万/天 × 365 × 5 × 100B | ≈ 18 GB |
|
||||
| 缓存(20%) | 18 GB × 20% | ≈ 3.6 GB |
|
||||
|
||||
**架构设计**:
|
||||
|
||||
```
|
||||
写路径:客户端 → API Server → ID 生成器 → Base62 编码 → 写入 MySQL + Redis
|
||||
读路径:客户端 → CDN → API Server → Redis 查询 → 302 重定向
|
||||
↓ (cache miss)
|
||||
MySQL 查询 → 回填 Redis
|
||||
```
|
||||
|
||||
**关键设计决策**:
|
||||
- 短码生成:Snowflake 分布式 ID + Base62 编码,避免哈希冲突
|
||||
- 缓存策略:Cache-Aside,热点短链用 CDN 加速
|
||||
- 数据库:单表即可(18GB 很小),按短码做索引
|
||||
|
||||
### 5.2 Feed 流系统
|
||||
|
||||
社交平台的 Feed 流(朋友圈、微博首页)是另一个经典题目。
|
||||
|
||||
**核心挑战**:用户发一条动态,如何让所有关注者看到?
|
||||
|
||||
| 方案 | 做法 | 优点 | 缺点 |
|
||||
|------|------|------|------|
|
||||
| 拉模式(Pull) | 读取时实时聚合关注者的动态 | 写入简单,存储少 | 读取慢,关注多时延迟高 |
|
||||
| 推模式(Push) | 发布时写入所有粉丝的收件箱 | 读取极快 | 大 V 发动态写扩散严重 |
|
||||
| 推拉结合 | 普通用户推,大 V 拉 | 平衡读写性能 | 实现复杂 |
|
||||
|
||||
**推拉结合方案**:
|
||||
- 粉丝数 < 1 万:发布时推送到所有粉丝的 Feed 缓存(推模式)
|
||||
- 粉丝数 > 1 万:不推送,粉丝读取时实时拉取(拉模式)
|
||||
- 用户打开 Feed 时:合并推送的内容 + 实时拉取大 V 的内容,按时间排序
|
||||
|
||||
### 5.3 秒杀系统
|
||||
|
||||
秒杀的核心挑战:瞬间超高并发 + 库存不能超卖。
|
||||
|
||||
**流量特征**:
|
||||
- 活动开始前:大量用户刷新页面等待
|
||||
- 活动开始瞬间:QPS 可能是平时的 100 倍以上
|
||||
- 活动结束后:流量迅速回落
|
||||
|
||||
**分层削峰策略**:
|
||||
|
||||
```
|
||||
用户请求 → CDN(静态页面)→ 网关(限流)→ 消息队列(削峰)→ 库存服务(扣减)
|
||||
```
|
||||
|
||||
| 层级 | 策略 | 效果 |
|
||||
|------|------|------|
|
||||
| 前端 | 按钮置灰 + 随机延迟 + 验证码 | 过滤机器人,分散请求 |
|
||||
| CDN | 静态资源缓存 | 减少 90% 的页面请求 |
|
||||
| 网关 | 令牌桶限流 | 只放行系统能承受的流量 |
|
||||
| 消息队列 | 请求入队,异步处理 | 削峰填谷,保护数据库 |
|
||||
| 库存服务 | Redis 预扣减 + Lua 原子操作 | 防止超卖,毫秒级响应 |
|
||||
|
||||
::: tip 秒杀的核心原则
|
||||
1. **尽量拦截在上游**:能在 CDN 挡住的就不要到应用层
|
||||
2. **读写分离**:商品详情页走缓存,只有下单走数据库
|
||||
3. **异步处理**:用户点击"抢购"后立即返回"排队中",后台异步处理
|
||||
4. **兜底方案**:限流、熔断、降级,任何一层出问题都有 Plan B
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
系统设计是一门实践性很强的技能,核心在于结构化思考和权衡取舍。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **四步法框架**:需求澄清 → 容量估算 → 架构设计 → 深入优化,每一步都不可跳过
|
||||
2. **信封背面估算**:不需要精确,只需要知道量级,用于指导架构决策
|
||||
3. **核心模式**:缓存、分库分表、消息队列、CDN、限流熔断——这些是系统设计的"积木"
|
||||
4. **权衡思维**:没有完美方案,只有适合当前阶段的方案,记录每个决策的理由和代价
|
||||
5. **经典案例**:短链服务练基础、Feed 流练推拉模型、秒杀练高并发——掌握这三个就能举一反三
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [System Design Interview](https://www.amazon.com/System-Design-Interview-insiders-Second/dp/B08CMF2CQF) - Alex Xu 系统设计面试经典
|
||||
- [Designing Data-Intensive Applications](https://dataintensive.net/) - Martin Kleppmann 数据密集型应用设计
|
||||
- [The System Design Primer](https://github.com/donnemartin/system-design-primer) - GitHub 上最全的系统设计学习资源
|
||||
- [ByteByteGo](https://bytebytego.com/) - Alex Xu 的系统设计可视化博客
|
||||
|
||||
|
||||
@@ -1,3 +1,221 @@
|
||||
# Docker 容器化
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**"在我机器上能跑"是开发者最经典的借口,Docker 让这个借口彻底消失。** 容器化技术将应用及其所有依赖打包成一个标准化的单元,确保在任何环境中都能一致运行。它是现代软件交付的基石。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **核心概念**:理解镜像、容器、仓库三大核心概念
|
||||
- **架构对比**:明白容器和虚拟机的本质区别
|
||||
- **实操能力**:掌握 Dockerfile 编写和常用命令
|
||||
- **编排基础**:学会用 Docker Compose 管理多服务应用
|
||||
- **最佳实践**:了解镜像优化、安全加固等生产级实践
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 为什么需要容器 | 环境一致性、资源效率、标准化交付 |
|
||||
| **第 2 章** | 核心概念 | 镜像、容器、仓库、Dockerfile |
|
||||
| **第 3 章** | Docker 生命周期 | 编写、构建、推送、运行、管理 |
|
||||
| **第 4 章** | Docker Compose | 多服务编排、网络、数据卷 |
|
||||
| **第 5 章** | 最佳实践 | 镜像优化、安全、多阶段构建 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 为什么需要容器?
|
||||
|
||||
在容器出现之前,部署一个应用需要在服务器上手动安装运行时、配置环境变量、处理依赖冲突。不同环境(开发、测试、生产)之间的差异是 bug 的温床。
|
||||
|
||||
<DockerArchitectureDemo />
|
||||
|
||||
### 容器解决了什么问题?
|
||||
|
||||
| 问题 | 传统方式 | 容器方式 |
|
||||
|------|---------|---------|
|
||||
| 环境不一致 | "我本地能跑" | 打包所有依赖,到处一致 |
|
||||
| 依赖冲突 | App A 要 Node 14,App B 要 Node 18 | 每个容器独立环境 |
|
||||
| 资源浪费 | 每个 VM 一个完整 OS | 共享内核,MB 级开销 |
|
||||
| 部署慢 | 手动安装配置 | docker run 一条命令 |
|
||||
| 扩容难 | 新建 VM、装环境、部署 | 秒级启动新容器 |
|
||||
|
||||
::: tip 容器的本质
|
||||
容器不是轻量级虚拟机。它的本质是**被隔离的进程**。Linux 内核通过两个机制实现容器:
|
||||
- **Namespace**:隔离进程的视野(PID、网络、文件系统等)
|
||||
- **Cgroups**:限制进程的资源使用(CPU、内存、IO)
|
||||
|
||||
容器里的进程和宿主机上的普通进程没有本质区别,只是被"关在了一个看不到外面的房间里"。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心概念
|
||||
|
||||
Docker 的世界围绕三个核心概念:镜像(Image)、容器(Container)、仓库(Registry)。
|
||||
|
||||
| 概念 | 类比 | 说明 |
|
||||
|------|------|------|
|
||||
| 镜像(Image) | 类 / 模板 | 只读的应用模板,包含代码、运行时、库、配置 |
|
||||
| 容器(Container) | 实例 / 对象 | 镜像的运行实例,可读写,有独立的生命周期 |
|
||||
| 仓库(Registry) | 应用商店 | 存储和分发镜像的服务(Docker Hub、ACR、ECR) |
|
||||
| Dockerfile | 配方 / 蓝图 | 定义如何构建镜像的文本文件 |
|
||||
| 数据卷(Volume) | 外接硬盘 | 持久化数据,容器删除后数据不丢失 |
|
||||
|
||||
### 镜像的分层结构
|
||||
|
||||
Docker 镜像由多个只读层(Layer)叠加而成,每条 Dockerfile 指令创建一层:
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ CMD ["node", "app.js"] │ ← 启动命令层
|
||||
├─────────────────────────┤
|
||||
│ COPY . /app │ ← 应用代码层(经常变)
|
||||
├─────────────────────────┤
|
||||
│ RUN npm install │ ← 依赖安装层(偶尔变)
|
||||
├─────────────────────────┤
|
||||
│ FROM node:18-alpine │ ← 基础镜像层(很少变)
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
::: tip 为什么分层很重要?
|
||||
Docker 会缓存每一层。如果某一层没有变化,构建时会直接复用缓存。所以 Dockerfile 中应该把**变化频率低的指令放在前面**(如安装依赖),**变化频率高的放在后面**(如复制代码)。这样大部分构建都能命中缓存,速度快很多。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. Docker 生命周期
|
||||
|
||||
从编写 Dockerfile 到容器运行,Docker 的工作流程是一条清晰的流水线。
|
||||
|
||||
<DockerLifecycleDemo />
|
||||
|
||||
### Dockerfile 常用指令速查
|
||||
|
||||
| 指令 | 作用 | 示例 |
|
||||
|------|------|------|
|
||||
| `FROM` | 指定基础镜像 | `FROM node:18-alpine` |
|
||||
| `WORKDIR` | 设置工作目录 | `WORKDIR /app` |
|
||||
| `COPY` | 复制文件到镜像 | `COPY package.json ./` |
|
||||
| `RUN` | 构建时执行命令 | `RUN npm install` |
|
||||
| `ENV` | 设置环境变量 | `ENV NODE_ENV=production` |
|
||||
| `EXPOSE` | 声明端口(仅文档作用) | `EXPOSE 3000` |
|
||||
| `CMD` | 容器启动命令 | `CMD ["node", "app.js"]` |
|
||||
| `ENTRYPOINT` | 容器入口点(不易被覆盖) | `ENTRYPOINT ["nginx"]` |
|
||||
|
||||
---
|
||||
|
||||
## 4. Docker Compose:多服务编排
|
||||
|
||||
真实项目通常不止一个容器。一个 Web 应用可能需要:应用服务器 + 数据库 + Redis + Nginx。Docker Compose 用一个 YAML 文件定义和管理多个容器。
|
||||
|
||||
### docker-compose.yml 示例
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- DB_HOST=db
|
||||
- REDIS_HOST=redis
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=secret
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
```
|
||||
|
||||
### Compose 核心概念
|
||||
|
||||
| 概念 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| services | 定义各个容器服务 | app、db、redis |
|
||||
| volumes | 持久化数据卷 | db-data 保存数据库文件 |
|
||||
| networks | 自定义网络(默认自动创建) | 服务间通过服务名互相访问 |
|
||||
| depends_on | 启动顺序依赖 | app 依赖 db 和 redis |
|
||||
| environment | 环境变量 | 数据库密码、连接地址 |
|
||||
|
||||
::: tip 服务发现
|
||||
在 Docker Compose 中,服务名就是主机名。app 容器可以直接用 `db:5432` 访问数据库,用 `redis:6379` 访问 Redis,不需要知道 IP 地址。这是 Docker 内置 DNS 的功劳。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 最佳实践
|
||||
|
||||
### 5.1 多阶段构建(Multi-stage Build)
|
||||
|
||||
多阶段构建是优化镜像大小的利器。构建阶段安装所有工具和依赖,最终阶段只保留运行时需要的文件。
|
||||
|
||||
```dockerfile
|
||||
# 构建阶段
|
||||
FROM node:18-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# 运行阶段
|
||||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
EXPOSE 3000
|
||||
CMD ["node", "dist/server.js"]
|
||||
```
|
||||
|
||||
### 5.2 镜像优化清单
|
||||
|
||||
| 优化项 | 做法 | 效果 |
|
||||
|--------|------|------|
|
||||
| 选择小基础镜像 | 用 `alpine` 而非 `ubuntu` | 镜像从 ~200MB 降到 ~50MB |
|
||||
| 合并 RUN 指令 | 多个命令用 `&&` 连接 | 减少镜像层数 |
|
||||
| 使用 .dockerignore | 排除 node_modules、.git 等 | 加速构建,减小上下文 |
|
||||
| 多阶段构建 | 分离构建和运行环境 | 最终镜像不含构建工具 |
|
||||
| 固定版本号 | `node:18.17-alpine` 而非 `node:latest` | 构建可重复 |
|
||||
|
||||
### 5.3 安全实践
|
||||
|
||||
| 实践 | 说明 |
|
||||
|------|------|
|
||||
| 不用 root 运行 | `USER node` 指定非 root 用户 |
|
||||
| 扫描漏洞 | `docker scout` 或 Trivy 扫描镜像 |
|
||||
| 最小权限 | 只安装必要的包,不装调试工具 |
|
||||
| 不硬编码密钥 | 用环境变量或 Docker Secrets |
|
||||
| 定期更新基础镜像 | 及时修复安全漏洞 |
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
Docker 容器化是现代软件交付的基础设施,理解它对于任何开发者都至关重要。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **容器 vs 虚拟机**:容器共享宿主内核,更轻量、更快,但隔离性略弱于 VM
|
||||
2. **核心三件套**:镜像(模板)、容器(实例)、仓库(分发)
|
||||
3. **Dockerfile**:分层构建,利用缓存,变化少的指令放前面
|
||||
4. **Docker Compose**:用 YAML 定义多服务应用,服务名即主机名
|
||||
5. **生产实践**:多阶段构建减小镜像、alpine 基础镜像、非 root 运行
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Docker 官方文档](https://docs.docker.com/) - 最权威的参考资料
|
||||
- [Docker Getting Started](https://docs.docker.com/get-started/) - 官方入门教程
|
||||
- [Dockerfile Best Practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) - 官方最佳实践指南
|
||||
- [Docker Compose 文档](https://docs.docker.com/compose/) - Compose 完整参考
|
||||
|
||||
@@ -1,3 +1,192 @@
|
||||
# Kubernetes 编排
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**Docker 解决了"打包"问题,Kubernetes 解决了"管理"问题。** 当你有几十上百个容器需要部署、扩缩容、故障恢复时,手动管理是不现实的。Kubernetes(K8s)就是容器的"操作系统",它自动化了容器化应用的部署、扩展和运维。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **架构理解**:掌握 K8s 控制平面和工作节点的组成
|
||||
- **核心资源**:熟悉 Pod、Deployment、Service 等核心概念
|
||||
- **声明式管理**:理解"声明期望状态,系统自动收敛"的思想
|
||||
- **运维能力**:了解滚动更新、自动扩缩容、健康检查等机制
|
||||
- **实战入门**:能用 kubectl 和 YAML 部署一个完整应用
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 为什么需要 K8s | 容器编排的挑战 |
|
||||
| **第 2 章** | K8s 架构 | 控制平面、工作节点、etcd |
|
||||
| **第 3 章** | 核心资源 | Pod、Deployment、Service、Ingress |
|
||||
| **第 4 章** | 声明式管理 | YAML、kubectl、控制循环 |
|
||||
| **第 5 章** | 运维实践 | 滚动更新、HPA、健康检查 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 为什么需要 Kubernetes?
|
||||
|
||||
Docker 让单个容器的打包和运行变得简单,但当你面对以下场景时,手动管理就力不从心了:
|
||||
|
||||
| 挑战 | 描述 | K8s 的解决方案 |
|
||||
|------|------|---------------|
|
||||
| 多实例部署 | 一个服务需要运行 10 个副本 | Deployment 自动管理副本数 |
|
||||
| 故障恢复 | 某个容器挂了需要自动重启 | 控制器自动检测并重建 Pod |
|
||||
| 服务发现 | 容器 IP 会变,怎么找到对方? | Service 提供稳定的 DNS 和 IP |
|
||||
| 滚动更新 | 更新版本时不能停服 | 逐步替换旧 Pod,零停机 |
|
||||
| 弹性伸缩 | 流量高峰自动扩容 | HPA 根据 CPU/内存自动调整副本数 |
|
||||
| 资源调度 | 把容器放到最合适的机器上 | Scheduler 智能调度 |
|
||||
|
||||
::: tip K8s 的核心思想:声明式
|
||||
你不需要告诉 K8s "启动 3 个容器"(命令式),而是告诉它 "我要 3 个副本在运行"(声明式)。K8s 会持续监控,确保实际状态与你声明的期望状态一致。如果一个 Pod 挂了,它会自动创建新的来补上。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. Kubernetes 架构
|
||||
|
||||
K8s 集群由控制平面(Control Plane)和工作节点(Worker Node)组成。
|
||||
|
||||
<K8sArchitectureDemo />
|
||||
|
||||
### 一次请求的完整路径
|
||||
|
||||
```
|
||||
用户请求 → Ingress Controller → Service → kube-proxy → Pod(容器)
|
||||
↑
|
||||
Endpoint 列表(由 Service 维护)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心资源对象
|
||||
|
||||
K8s 通过各种"资源对象"来描述集群的期望状态。
|
||||
|
||||
<K8sWorkloadsDemo />
|
||||
|
||||
### 资源对象分类
|
||||
|
||||
| 类别 | 资源 | 用途 |
|
||||
|------|------|------|
|
||||
| 工作负载 | Pod、Deployment、StatefulSet、DaemonSet、Job | 运行应用 |
|
||||
| 网络 | Service、Ingress、NetworkPolicy | 服务发现和流量管理 |
|
||||
| 配置 | ConfigMap、Secret | 配置和敏感数据管理 |
|
||||
| 存储 | PersistentVolume、PersistentVolumeClaim | 持久化存储 |
|
||||
| 调度 | Node、Namespace、ResourceQuota | 资源隔离和限制 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 声明式管理与 kubectl
|
||||
|
||||
### 控制循环(Reconciliation Loop)
|
||||
|
||||
K8s 的核心工作机制是控制循环:
|
||||
|
||||
```
|
||||
观察(Observe)→ 比较(Diff)→ 行动(Act)→ 观察...
|
||||
↓ ↓ ↓
|
||||
读取实际状态 与期望状态对比 执行修正操作
|
||||
```
|
||||
|
||||
你声明 `replicas: 3`,控制器发现只有 2 个 Pod 在运行,就会创建 1 个新的。这个循环每隔几秒执行一次,确保系统始终向期望状态收敛。
|
||||
|
||||
### kubectl 常用命令
|
||||
|
||||
| 命令 | 作用 | 示例 |
|
||||
|------|------|------|
|
||||
| `kubectl apply -f` | 应用 YAML 配置 | `kubectl apply -f deployment.yaml` |
|
||||
| `kubectl get` | 查看资源列表 | `kubectl get pods -o wide` |
|
||||
| `kubectl describe` | 查看资源详情 | `kubectl describe pod my-app-xxx` |
|
||||
| `kubectl logs` | 查看 Pod 日志 | `kubectl logs -f my-app-xxx` |
|
||||
| `kubectl exec` | 进入 Pod 终端 | `kubectl exec -it my-app-xxx -- sh` |
|
||||
| `kubectl delete` | 删除资源 | `kubectl delete -f deployment.yaml` |
|
||||
| `kubectl scale` | 手动扩缩容 | `kubectl scale deploy my-app --replicas=5` |
|
||||
|
||||
::: tip apply vs create
|
||||
`kubectl create` 是命令式的——"创建这个资源",如果已存在会报错。`kubectl apply` 是声明式的——"确保资源是这个状态",不存在就创建,已存在就更新。生产环境中应该始终使用 `apply`。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 运维实践
|
||||
|
||||
### 5.1 滚动更新与回滚
|
||||
|
||||
Deployment 默认使用滚动更新策略:逐步创建新版本 Pod,同时逐步终止旧版本 Pod。
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1 # 最多多创建 1 个 Pod
|
||||
maxUnavailable: 0 # 不允许有 Pod 不可用
|
||||
```
|
||||
|
||||
| 操作 | 命令 |
|
||||
|------|------|
|
||||
| 更新镜像 | `kubectl set image deploy/my-app app=my-app:2.0` |
|
||||
| 查看更新状态 | `kubectl rollout status deploy/my-app` |
|
||||
| 查看历史版本 | `kubectl rollout history deploy/my-app` |
|
||||
| 回滚到上一版本 | `kubectl rollout undo deploy/my-app` |
|
||||
|
||||
### 5.2 自动扩缩容(HPA)
|
||||
|
||||
HPA(Horizontal Pod Autoscaler)根据 CPU、内存或自定义指标自动调整 Pod 副本数。
|
||||
|
||||
```yaml
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: my-app-hpa
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: my-app
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 70
|
||||
```
|
||||
|
||||
### 5.3 健康检查(Probe)
|
||||
|
||||
K8s 通过三种探针监控 Pod 的健康状态:
|
||||
|
||||
| 探针 | 作用 | 失败后果 |
|
||||
|------|------|---------|
|
||||
| livenessProbe | 检测容器是否存活 | 重启容器 |
|
||||
| readinessProbe | 检测容器是否就绪 | 从 Service 摘除,不接收流量 |
|
||||
| startupProbe | 检测容器是否启动完成 | 启动期间不执行其他探针 |
|
||||
|
||||
::: tip 探针的重要性
|
||||
没有配置健康检查的 Pod,K8s 只能通过进程是否存在来判断健康状态。但很多时候进程还在,服务已经不响应了(比如死锁、OOM 边缘)。配置 livenessProbe 可以让 K8s 自动重启这些"假死"的容器。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
Kubernetes 是容器编排的事实标准,理解它的核心概念是云原生开发的基础。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **声明式管理**:告诉 K8s "我要什么",而不是"怎么做",控制循环自动收敛
|
||||
2. **架构分层**:控制平面负责决策,工作节点负责执行,etcd 存储状态
|
||||
3. **核心资源**:Pod(最小单元)、Deployment(副本管理)、Service(服务发现)、Ingress(外部入口)
|
||||
4. **运维自动化**:滚动更新零停机、HPA 弹性伸缩、探针自动故障恢复
|
||||
5. **配置分离**:ConfigMap 和 Secret 让配置与镜像解耦
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Kubernetes 官方文档](https://kubernetes.io/zh-cn/docs/) - 最权威的中文参考
|
||||
- [Kubernetes the Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way) - 从零手动搭建 K8s 集群
|
||||
- [The Illustrated Children's Guide to Kubernetes](https://www.cncf.io/phippy/) - CNCF 出品的趣味入门
|
||||
- [Kubernetes Patterns](https://www.oreilly.com/library/view/kubernetes-patterns-2nd/9781098131678/) - K8s 设计模式
|
||||
|
||||
@@ -1,3 +1,397 @@
|
||||
# Linux 基础
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**服务器的世界,Linux 是绝对的主角。** 全球超过 90% 的服务器运行 Linux,从你每天用的微信到 Google 搜索,背后都是 Linux 在支撑。作为开发者,掌握 Linux 基础不是可选项,而是必修课。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **文件系统**:理解 Linux 目录结构和"一切皆文件"的哲学
|
||||
- **常用命令**:掌握文件操作、文本处理、进程管理等核心命令
|
||||
- **权限模型**:理解用户、组、权限的概念
|
||||
- **Shell 基础**:了解管道、重定向、环境变量等 Shell 核心概念
|
||||
- **实战技能**:学会日志查看、进程排查、网络诊断等运维基本功
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 文件系统 | 目录结构、一切皆文件 |
|
||||
| **第 2 章** | 常用命令 | 文件、文本、进程、网络 |
|
||||
| **第 3 章** | 权限模型 | 用户、组、rwx、sudo |
|
||||
| **第 4 章** | Shell 基础 | 管道、重定向、变量、脚本 |
|
||||
| **第 5 章** | 实战场景 | 日志排查、性能诊断 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 文件系统:一切皆文件
|
||||
|
||||
Linux 最核心的哲学之一就是**一切皆文件**。普通文件是文件,目录是文件,硬盘是文件,甚至网络连接、进程信息都是文件。这个统一的抽象让你可以用同一套工具(读、写、权限控制)操作几乎所有系统资源。
|
||||
|
||||
<LinuxFileSystemDemo />
|
||||
|
||||
### 目录结构速记
|
||||
|
||||
把 Linux 文件系统想象成一棵倒过来的树:
|
||||
|
||||
```
|
||||
/ ← 根目录(树根)
|
||||
├── home/ ← 用户的家(你的文件都在这)
|
||||
├── etc/ ← 配置文件(系统的"设置面板")
|
||||
├── var/ ← 变化的数据(日志、缓存)
|
||||
├── usr/ ← 用户安装的程序
|
||||
├── tmp/ ← 临时文件(重启就没了)
|
||||
├── proc/ ← 进程信息(虚拟的,不占磁盘)
|
||||
├── dev/ ← 设备文件(硬盘、终端)
|
||||
├── bin/ ← 基础命令(ls、cp、mv)
|
||||
├── sbin/ ← 系统管理命令(需要 root)
|
||||
├── opt/ ← 第三方软件
|
||||
└── root/ ← root 用户的家目录
|
||||
```
|
||||
|
||||
### 路径的两种写法
|
||||
|
||||
| 类型 | 格式 | 示例 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 绝对路径 | 从 `/` 开始 | `/home/alice/code/app.js` | 从根目录出发,不会歧义 |
|
||||
| 相对路径 | 从当前目录开始 | `./code/app.js` 或 `../config` | `.` 是当前目录,`..` 是上级目录 |
|
||||
|
||||
::: tip "一切皆文件"的威力
|
||||
想知道 CPU 信息?读文件:`cat /proc/cpuinfo`
|
||||
想知道内存使用?读文件:`cat /proc/meminfo`
|
||||
想产生随机数?读文件:`cat /dev/urandom`
|
||||
想丢弃输出?写文件:`echo "no thanks" > /dev/null`
|
||||
|
||||
不需要专门的 API,读写文件就够了。这就是 Unix 哲学的优雅之处。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 常用命令
|
||||
|
||||
Linux 命令遵循一个统一的格式:`命令 [选项] [参数]`。比如 `ls -la /home` 中,`ls` 是命令,`-la` 是选项,`/home` 是参数。
|
||||
|
||||
<LinuxCommandDemo />
|
||||
|
||||
### 最常用的 10 个命令
|
||||
|
||||
如果只能记住 10 个命令,记这些:
|
||||
|
||||
| 命令 | 用途 | 记忆技巧 |
|
||||
|------|------|----------|
|
||||
| `ls` | 列出文件 | list |
|
||||
| `cd` | 切换目录 | change directory |
|
||||
| `cat` | 查看文件 | concatenate |
|
||||
| `grep` | 搜索文本 | global regular expression print |
|
||||
| `find` | 查找文件 | 就是 find |
|
||||
| `ps` | 查看进程 | process status |
|
||||
| `tail -f` | 实时看日志 | 看文件"尾巴",-f 是 follow |
|
||||
| `chmod` | 改权限 | change mode |
|
||||
| `curl` | 发 HTTP 请求 | client URL |
|
||||
| `ssh` | 远程登录 | secure shell |
|
||||
|
||||
### 命令组合的艺术
|
||||
|
||||
Linux 的强大不在于单个命令,而在于**命令组合**。通过管道 `|` 把多个简单命令串起来,解决复杂问题:
|
||||
|
||||
```bash
|
||||
# 找出占用 CPU 最多的 5 个进程
|
||||
ps aux --sort=-%cpu | head -6
|
||||
|
||||
# 统计日志中出现最多的错误类型
|
||||
grep "ERROR" app.log | awk '{print $4}' | sort | uniq -c | sort -rn | head -10
|
||||
|
||||
# 查找大于 100MB 的文件
|
||||
find / -size +100M -type f 2>/dev/null
|
||||
|
||||
# 实时监控日志中的错误
|
||||
tail -f /var/log/app.log | grep --color "ERROR"
|
||||
```
|
||||
|
||||
::: tip Unix 哲学
|
||||
"做一件事,做好它。" 每个命令只负责一个功能,通过管道组合实现复杂操作。这就是为什么 Linux 命令都很短小——它们是积木,不是瑞士军刀。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 权限模型
|
||||
|
||||
Linux 是多用户系统,权限模型是安全的基石。每个文件都有三组权限,分别控制**所有者(Owner)**、**所属组(Group)**、**其他人(Others)**能做什么。
|
||||
|
||||
### 读懂 `ls -l` 的输出
|
||||
|
||||
```bash
|
||||
$ ls -l app.js
|
||||
-rwxr-xr-- 1 alice developers 2048 Jan 15 10:30 app.js
|
||||
│├──┤├──┤├──┤ │ │ │
|
||||
│ │ │ │ │ │ └── 文件大小
|
||||
│ │ │ │ │ └── 所属组
|
||||
│ │ │ │ └── 所有者
|
||||
│ │ │ └── 其他人权限:r-- (只读)
|
||||
│ │ └── 组权限:r-x (读+执行)
|
||||
│ └── 所有者权限:rwx (读+写+执行)
|
||||
└── 文件类型:- 普通文件,d 目录,l 链接
|
||||
```
|
||||
|
||||
### 权限的三种操作
|
||||
|
||||
| 权限 | 字母 | 数字 | 对文件的含义 | 对目录的含义 |
|
||||
|------|------|------|-------------|-------------|
|
||||
| 读 | `r` | 4 | 查看文件内容 | 列出目录内容(ls) |
|
||||
| 写 | `w` | 2 | 修改文件内容 | 创建/删除目录中的文件 |
|
||||
| 执行 | `x` | 1 | 运行程序/脚本 | 进入目录(cd) |
|
||||
|
||||
<LinuxPermissionsDemo />
|
||||
|
||||
### 数字权限速算
|
||||
|
||||
三个数字分别代表 Owner、Group、Others 的权限,每个数字是 r(4) + w(2) + x(1) 的和:
|
||||
|
||||
```
|
||||
chmod 755 script.sh
|
||||
7 = rwx (4+2+1) → 所有者:读+写+执行
|
||||
5 = r-x (4+0+1) → 组:读+执行
|
||||
5 = r-x (4+0+1) → 其他人:读+执行
|
||||
```
|
||||
|
||||
| 常见权限 | 含义 | 典型用途 |
|
||||
|---------|------|---------|
|
||||
| `644` | rw-r--r-- | 普通文件(所有者可写,其他人只读) |
|
||||
| `755` | rwxr-xr-x | 可执行文件/目录 |
|
||||
| `600` | rw------- | 私密文件(如 SSH 密钥) |
|
||||
| `777` | rwxrwxrwx | 所有人可读写执行(危险,避免使用) |
|
||||
|
||||
### sudo:临时获取超级权限
|
||||
|
||||
普通用户权限有限,有些操作需要 root 权限。`sudo` 让你临时以 root 身份执行命令:
|
||||
|
||||
```bash
|
||||
# 普通用户无法修改系统配置
|
||||
$ vim /etc/nginx/nginx.conf
|
||||
# Permission denied
|
||||
|
||||
# 用 sudo 临时提权
|
||||
$ sudo vim /etc/nginx/nginx.conf
|
||||
# 输入你的密码后可以编辑
|
||||
|
||||
# 切换到 root 用户(谨慎使用)
|
||||
$ sudo su -
|
||||
```
|
||||
|
||||
::: warning 最小权限原则
|
||||
永远不要用 `chmod 777` 解决权限问题,这等于把门锁拆了。正确做法是搞清楚谁需要什么权限,精确授予。同样,不要长期以 root 身份操作,只在必要时用 `sudo`。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. Shell 基础
|
||||
|
||||
Shell 是你和 Linux 内核之间的"翻译官"。你输入命令,Shell 解释并交给内核执行。最常用的 Shell 是 **Bash**(大多数 Linux 发行版默认)和 **Zsh**(macOS 默认)。
|
||||
|
||||
### 管道与重定向
|
||||
|
||||
这是 Shell 最强大的两个特性:
|
||||
|
||||
| 符号 | 名称 | 作用 | 示例 |
|
||||
|------|------|------|------|
|
||||
| `|` | 管道 | 把前一个命令的输出作为后一个的输入 | `cat log | grep ERROR` |
|
||||
| `>` | 输出重定向 | 把输出写入文件(覆盖) | `echo "hello" > file.txt` |
|
||||
| `>>` | 追加重定向 | 把输出追加到文件末尾 | `echo "world" >> file.txt` |
|
||||
| `<` | 输入重定向 | 从文件读取输入 | `wc -l < file.txt` |
|
||||
| `2>` | 错误重定向 | 把错误信息写入文件 | `cmd 2> error.log` |
|
||||
| `2>&1` | 合并输出 | 把错误和正常输出合并 | `cmd > all.log 2>&1` |
|
||||
|
||||
### 环境变量
|
||||
|
||||
环境变量是 Shell 中的"全局配置",影响命令的行为:
|
||||
|
||||
```bash
|
||||
# 查看所有环境变量
|
||||
env
|
||||
|
||||
# 查看某个变量
|
||||
echo $PATH
|
||||
echo $HOME
|
||||
|
||||
# 临时设置(只在当前 Shell 有效)
|
||||
export API_KEY="abc123"
|
||||
|
||||
# 永久设置(写入配置文件)
|
||||
echo 'export API_KEY="abc123"' >> ~/.bashrc
|
||||
source ~/.bashrc # 让配置立即生效
|
||||
```
|
||||
|
||||
| 常见变量 | 含义 | 示例值 |
|
||||
|---------|------|--------|
|
||||
| `$PATH` | 命令搜索路径 | `/usr/local/bin:/usr/bin:/bin` |
|
||||
| `$HOME` | 用户主目录 | `/home/alice` |
|
||||
| `$USER` | 当前用户名 | `alice` |
|
||||
| `$PWD` | 当前工作目录 | `/var/log` |
|
||||
| `$SHELL` | 当前使用的 Shell | `/bin/bash` |
|
||||
|
||||
### Shell 脚本入门
|
||||
|
||||
把多个命令写进一个文件,就是 Shell 脚本。它是自动化运维的起点:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# deploy.sh - 简单的部署脚本
|
||||
|
||||
APP_DIR="/opt/myapp"
|
||||
LOG_FILE="/var/log/deploy.log"
|
||||
|
||||
echo "$(date) - 开始部署..." >> $LOG_FILE
|
||||
|
||||
# 拉取最新代码
|
||||
cd $APP_DIR && git pull origin main
|
||||
|
||||
# 安装依赖
|
||||
npm install --production
|
||||
|
||||
# 重启服务
|
||||
pm2 restart myapp
|
||||
|
||||
echo "$(date) - 部署完成" >> $LOG_FILE
|
||||
```
|
||||
|
||||
```bash
|
||||
# 给脚本执行权限并运行
|
||||
chmod +x deploy.sh
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
::: tip 脚本调试技巧
|
||||
在脚本开头加 `set -ex`:`-e` 让脚本遇到错误立即退出(而不是继续执行),`-x` 会打印每条执行的命令(方便排查问题)。这两个选项在生产脚本中几乎是标配。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 实战场景
|
||||
|
||||
理论学完了,来看几个开发中最常遇到的实战场景。
|
||||
|
||||
### 5.1 日志排查
|
||||
|
||||
服务出问题,第一反应就是看日志。以下是日志排查的常用套路:
|
||||
|
||||
```bash
|
||||
# 1. 实时跟踪日志(最常用)
|
||||
tail -f /var/log/app/error.log
|
||||
|
||||
# 2. 搜索特定时间段的错误
|
||||
grep "2024-01-15 14:" error.log | grep "ERROR"
|
||||
|
||||
# 3. 统计每小时的错误数量
|
||||
grep "ERROR" app.log | awk '{print substr($1,1,13)}' | uniq -c
|
||||
|
||||
# 4. 查看最近 100 行日志
|
||||
tail -100 app.log
|
||||
|
||||
# 5. 在多个日志文件中搜索
|
||||
grep -r "OutOfMemory" /var/log/app/
|
||||
```
|
||||
|
||||
### 5.2 进程排查
|
||||
|
||||
应用卡死、CPU 飙高、内存泄漏——这些问题都需要从进程入手:
|
||||
|
||||
```bash
|
||||
# 查看 CPU 占用最高的进程
|
||||
ps aux --sort=-%cpu | head -10
|
||||
|
||||
# 查看内存占用最高的进程
|
||||
ps aux --sort=-%mem | head -10
|
||||
|
||||
# 查找特定进程
|
||||
ps aux | grep "node"
|
||||
|
||||
# 查看进程的详细信息(包括线程)
|
||||
top -Hp <PID>
|
||||
|
||||
# 查看进程打开的文件
|
||||
lsof -p <PID>
|
||||
|
||||
# 优雅终止进程(SIGTERM)
|
||||
kill <PID>
|
||||
|
||||
# 强制终止(SIGKILL,最后手段)
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
### 5.3 网络诊断
|
||||
|
||||
服务连不上?先搞清楚是网络问题还是应用问题:
|
||||
|
||||
```bash
|
||||
# 测试目标是否可达
|
||||
ping -c 4 google.com
|
||||
|
||||
# 检查端口是否开放
|
||||
telnet db-server 3306
|
||||
# 或者用 nc
|
||||
nc -zv db-server 3306
|
||||
|
||||
# 查看本机监听的端口
|
||||
ss -tlnp
|
||||
# 或
|
||||
netstat -tlnp
|
||||
|
||||
# DNS 解析检查
|
||||
dig api.example.com
|
||||
nslookup api.example.com
|
||||
|
||||
# 测试 HTTP 接口
|
||||
curl -v http://localhost:3000/health
|
||||
|
||||
# 查看网络连接状态统计
|
||||
ss -s
|
||||
```
|
||||
|
||||
### 5.4 磁盘空间排查
|
||||
|
||||
磁盘满了是线上最常见的故障之一:
|
||||
|
||||
```bash
|
||||
# 查看各分区使用情况
|
||||
df -h
|
||||
|
||||
# 找出占用空间最大的目录
|
||||
du -sh /* 2>/dev/null | sort -rh | head -10
|
||||
|
||||
# 进一步定位大目录
|
||||
du -sh /var/log/* | sort -rh | head -10
|
||||
|
||||
# 查找大文件(>100MB)
|
||||
find / -type f -size +100M 2>/dev/null | head -20
|
||||
|
||||
# 清理常见的空间占用
|
||||
# 清理旧日志
|
||||
sudo journalctl --vacuum-size=500M
|
||||
# 清理 Docker 无用镜像
|
||||
docker system prune -a
|
||||
```
|
||||
|
||||
::: tip 线上排查口诀
|
||||
**"一看日志,二看进程,三看网络,四看磁盘"**。90% 的线上问题都能通过这四步定位到原因。养成习惯后,排查效率会大幅提升。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
Linux 是开发者的必备技能,掌握基础就能应对大部分日常开发和运维场景。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **一切皆文件**:Linux 用文件抽象统一了对硬件、进程、网络等资源的访问方式
|
||||
2. **命令组合**:单个命令功能简单,通过管道 `|` 组合才能发挥真正威力
|
||||
3. **权限模型**:Owner/Group/Others × Read/Write/Execute,用数字(如 755)快速设置
|
||||
4. **Shell 基础**:管道、重定向、环境变量、脚本是自动化的基石
|
||||
5. **实战排查**:日志 → 进程 → 网络 → 磁盘,四步定位大部分线上问题
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Linux 命令大全](https://man7.org/linux/man-pages/) - Linux man pages 官方文档
|
||||
- [The Linux Command Line](https://linuxcommand.org/tlcl.php) - 免费的 Linux 命令行入门书
|
||||
- [Linux Journey](https://linuxjourney.com/) - 交互式 Linux 学习网站
|
||||
- [explainshell.com](https://explainshell.com/) - 输入命令自动解释每个参数的含义
|
||||
|
||||
@@ -1,3 +1,315 @@
|
||||
# 神经网络与深度学习
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**神经网络是 AI 革命的引擎。** 从 ChatGPT 的语言理解到自动驾驶的图像识别,背后都是神经网络在工作。它不是魔法,而是一套精巧的数学框架——通过大量数据"学习"出输入到输出的映射关系。理解它的基本原理,能帮你更好地使用和调试 AI 工具。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **核心概念**:理解神经元、层、前向传播、反向传播的基本原理
|
||||
- **网络类型**:了解 CNN、RNN、Transformer 等主流架构的特点和适用场景
|
||||
- **训练过程**:明白模型是如何从数据中"学习"的
|
||||
- **关键技巧**:掌握过拟合、学习率、正则化等实用概念
|
||||
- **发展脉络**:了解从感知机到大语言模型的演进历程
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 从神经元到网络 | 感知机、激活函数、前向传播 |
|
||||
| **第 2 章** | 网络如何学习 | 损失函数、梯度下降、反向传播 |
|
||||
| **第 3 章** | 主流网络架构 | CNN、RNN、Transformer |
|
||||
| **第 4 章** | 训练的艺术 | 过拟合、正则化、超参数调优 |
|
||||
| **第 5 章** | 发展历程与前沿 | 从感知机到 GPT |
|
||||
|
||||
---
|
||||
|
||||
## 1. 从神经元到网络
|
||||
|
||||
### 单个神经元
|
||||
|
||||
神经网络的最小单元是**神经元**(Neuron)。它模拟了生物神经元的工作方式:接收多个输入信号,加权求和,通过激活函数产生输出。
|
||||
|
||||
```
|
||||
输入 x1 ──→ ×w1 ──┐
|
||||
输入 x2 ──→ ×w2 ──┼──→ Σ(加权求和) + b(偏置) ──→ f(激活函数) ──→ 输出
|
||||
输入 x3 ──→ ×w3 ──┘
|
||||
```
|
||||
|
||||
数学表达:**y = f(w₁x₁ + w₂x₂ + w₃x₃ + b)**
|
||||
|
||||
<NeuronDemo />
|
||||
|
||||
### 激活函数:为什么需要非线性?
|
||||
|
||||
如果没有激活函数,无论多少层神经元叠加,最终都等价于一个线性变换(矩阵乘法)。激活函数引入**非线性**,让网络能学习复杂的模式。
|
||||
|
||||
| 激活函数 | 公式 | 特点 | 常用场景 |
|
||||
|---------|------|------|---------|
|
||||
| ReLU | max(0, x) | 简单高效,训练快 | 隐藏层的默认选择 |
|
||||
| Sigmoid | 1/(1+e⁻ˣ) | 输出 0~1 | 二分类输出层 |
|
||||
| Tanh | (eˣ-e⁻ˣ)/(eˣ+e⁻ˣ) | 输出 -1~1 | RNN 中常用 |
|
||||
| Softmax | eˣᵢ/Σeˣⱼ | 输出概率分布 | 多分类输出层 |
|
||||
|
||||
### 从神经元到网络
|
||||
|
||||
把多个神经元组织成**层**,多个层串联起来,就构成了神经网络:
|
||||
|
||||
```
|
||||
输入层 隐藏层1 隐藏层2 输出层
|
||||
(特征) (提取低级特征) (提取高级特征) (预测结果)
|
||||
|
||||
x1 ──→ [○ ○ ○ ○] ──→ [○ ○ ○] ──→ [○ ○]
|
||||
x2 ──→ [○ ○ ○ ○] ──→ [○ ○ ○] ──→ 猫/狗
|
||||
x3 ──→ [○ ○ ○ ○] ──→ [○ ○ ○]
|
||||
```
|
||||
|
||||
| 概念 | 说明 |
|
||||
|------|------|
|
||||
| 输入层 | 接收原始数据(图片像素、文本向量等) |
|
||||
| 隐藏层 | 中间处理层,层数越多网络越"深"(深度学习的"深") |
|
||||
| 输出层 | 产生最终预测(分类概率、回归值等) |
|
||||
| 前向传播 | 数据从输入层逐层流向输出层的过程 |
|
||||
|
||||
::: tip 为什么叫"深度"学习?
|
||||
传统机器学习通常只有 1-2 层。当隐藏层数量增加到几十甚至上百层时,就叫"深度"学习。更深的网络能学习更抽象的特征:第一层学边缘,第二层学纹理,第三层学部件,更深的层学到"这是一只猫"。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 网络如何学习
|
||||
|
||||
神经网络的"学习"本质上是一个**优化问题**:找到一组权重(w)和偏置(b),使得网络的预测尽可能接近真实答案。
|
||||
|
||||
### 训练三步曲
|
||||
|
||||
```
|
||||
1. 前向传播:输入数据,得到预测结果
|
||||
2. 计算损失:用损失函数衡量预测与真实值的差距
|
||||
3. 反向传播:根据损失,计算每个权重的梯度,更新权重
|
||||
↓
|
||||
重复以上步骤,直到损失足够小
|
||||
```
|
||||
|
||||
### 损失函数:衡量"错得有多离谱"
|
||||
|
||||
损失函数(Loss Function)量化了预测值和真实值之间的差距。训练的目标就是最小化损失。
|
||||
|
||||
| 损失函数 | 公式简述 | 适用场景 |
|
||||
|---------|---------|---------|
|
||||
| MSE(均方误差) | 预测值与真实值差的平方的均值 | 回归问题 |
|
||||
| Cross-Entropy(交叉熵) | -Σ y·log(ŷ) | 分类问题 |
|
||||
| Binary Cross-Entropy | 交叉熵的二分类版本 | 二分类问题 |
|
||||
|
||||
### 梯度下降:找到最低点
|
||||
|
||||
想象你站在一座山上,蒙着眼睛要走到最低点。你能做的就是**摸一下脚下的坡度,然后往下坡方向走一步**。这就是梯度下降。
|
||||
|
||||
```
|
||||
损失值
|
||||
↑
|
||||
│ ╱╲
|
||||
│ ╱ ╲ ← 当前位置
|
||||
│ ╱ ╲ ↙ 沿梯度方向下降
|
||||
│ ╱ ╲╱ ← 局部最小值
|
||||
│╱ ╲╱ ← 全局最小值
|
||||
└──────────────→ 权重值
|
||||
```
|
||||
|
||||
| 概念 | 说明 |
|
||||
|------|------|
|
||||
| 梯度 | 损失函数对每个权重的偏导数,指示"往哪个方向调整能减少损失" |
|
||||
| 学习率 | 每一步走多远。太大会跳过最低点,太小会收敛太慢 |
|
||||
| 批量大小 | 每次用多少样本计算梯度。全量太慢,单样本太抖,小批量(mini-batch)是折中 |
|
||||
|
||||
### 反向传播:链式法则的胜利
|
||||
|
||||
反向传播(Backpropagation)是计算梯度的高效算法。它利用微积分的**链式法则**,从输出层开始,逐层向后计算每个权重对损失的贡献。
|
||||
|
||||
```
|
||||
前向传播:输入 → 隐藏层1 → 隐藏层2 → 输出 → 损失
|
||||
反向传播:损失 → 输出 → 隐藏层2 → 隐藏层1 → 更新所有权重
|
||||
```
|
||||
|
||||
::: tip 直觉理解反向传播
|
||||
把神经网络想象成一条流水线。产品(预测)出了问题(损失大),你需要从最后一道工序开始往回查,看每道工序(每层权重)对最终问题贡献了多少,然后按贡献大小调整。贡献大的多调,贡献小的少调。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 主流网络架构
|
||||
|
||||
不同类型的数据需要不同的网络架构。选对架构,事半功倍。
|
||||
|
||||
<NetworkLayersDemo />
|
||||
|
||||
### 3.1 CNN(卷积神经网络)
|
||||
|
||||
CNN 是处理图像的王者。核心思想:用小的卷积核在图像上滑动,提取局部特征。
|
||||
|
||||
```
|
||||
输入图像 → [卷积层→激活→池化] × N → 全连接层 → 输出
|
||||
28×28 提取边缘/纹理/形状 分类结果
|
||||
```
|
||||
|
||||
| 特点 | 说明 |
|
||||
|------|------|
|
||||
| 局部连接 | 每个神经元只看一小块区域,而非整张图 |
|
||||
| 参数共享 | 同一个卷积核在整张图上复用,大幅减少参数 |
|
||||
| 平移不变性 | 猫在图片左边还是右边,都能识别 |
|
||||
| 层级特征 | 浅层学边缘,深层学语义 |
|
||||
|
||||
代表模型:LeNet、AlexNet、VGG、ResNet、EfficientNet
|
||||
|
||||
### 3.2 RNN(循环神经网络)
|
||||
|
||||
RNN 专为**序列数据**设计。它的隐藏状态会传递到下一个时间步,让网络具有"记忆"能力。
|
||||
|
||||
```
|
||||
时间步 t1 时间步 t2 时间步 t3
|
||||
"我" ──→ "喜欢" ──→ "猫"
|
||||
↓ ↓ ↓
|
||||
[h1] ──→ [h2] ──→ [h3] ──→ 输出
|
||||
↑ ↑ ↑
|
||||
隐藏状态在时间步之间传递(记忆)
|
||||
```
|
||||
|
||||
| 变体 | 解决的问题 | 核心机制 |
|
||||
|------|-----------|---------|
|
||||
| 原始 RNN | 基础序列建模 | 简单循环连接 |
|
||||
| LSTM | 长序列梯度消失 | 遗忘门、输入门、输出门 |
|
||||
| GRU | LSTM 参数太多 | 简化为重置门和更新门 |
|
||||
| 双向 RNN | 只能看到过去 | 同时从前往后和从后往前处理 |
|
||||
|
||||
::: tip LSTM 的门控机制
|
||||
LSTM 的精妙之处在于三个"门":**遗忘门**决定丢弃哪些旧记忆,**输入门**决定存入哪些新信息,**输出门**决定输出哪些内容。就像你读一本书,会选择性地记住重要情节、忘掉无关细节。
|
||||
:::
|
||||
|
||||
### 3.3 Transformer:注意力就是一切
|
||||
|
||||
2017 年 Google 发表的 "Attention Is All You Need" 论文提出了 Transformer,彻底改变了 AI 领域。它用**自注意力机制**替代了循环结构,是 GPT、BERT、Claude 等大模型的基础。
|
||||
|
||||
```
|
||||
输入序列 → 嵌入 + 位置编码 → [多头注意力 → 前馈网络] × N → 输出
|
||||
↑
|
||||
每个词都能"看到"所有其他词
|
||||
```
|
||||
|
||||
| 优势 | 说明 |
|
||||
|------|------|
|
||||
| 并行计算 | 不像 RNN 必须逐步处理,Transformer 可以并行处理整个序列 |
|
||||
| 长距离依赖 | 任意两个位置之间直接建立联系,不受距离限制 |
|
||||
| 可扩展性 | 模型越大、数据越多,效果越好(Scaling Law) |
|
||||
|
||||
**自注意力的直觉**:读"小猫坐在垫子上,因为**它**很累"这句话时,"它"需要关注"小猫"才能理解含义。自注意力让模型学会这种关联——为序列中的每对词计算一个"相关性分数"。
|
||||
|
||||
<NetworkArchitectureDemo />
|
||||
|
||||
## 4. 训练的艺术
|
||||
|
||||
有了好的架构还不够,训练过程中有很多"坑"需要避开。
|
||||
|
||||
### 4.1 过拟合 vs 欠拟合
|
||||
|
||||
| 问题 | 表现 | 原因 | 解决方案 |
|
||||
|------|------|------|---------|
|
||||
| 过拟合 | 训练集表现好,测试集表现差 | 模型太复杂,"背答案"而非学规律 | 正则化、Dropout、数据增强、早停 |
|
||||
| 欠拟合 | 训练集和测试集都表现差 | 模型太简单,学不到规律 | 增加模型容量、训练更久、更好的特征 |
|
||||
|
||||
```
|
||||
误差
|
||||
↑
|
||||
│ ╲ 训练误差 测试误差 ╱
|
||||
│ ╲ ╱
|
||||
│ ╲─────────────────╱
|
||||
│ 欠拟合 ← 最佳点 → 过拟合
|
||||
└──────────────────────────→ 模型复杂度
|
||||
```
|
||||
|
||||
### 4.2 关键超参数
|
||||
|
||||
超参数是训练前需要人为设定的参数(不是模型自己学的):
|
||||
|
||||
| 超参数 | 作用 | 常见范围 | 调优建议 |
|
||||
|--------|------|---------|---------|
|
||||
| 学习率 | 每步更新的幅度 | 1e-5 ~ 1e-1 | 最重要的超参数,通常从 1e-3 开始 |
|
||||
| 批量大小 | 每次训练用多少样本 | 16 ~ 512 | 越大训练越稳定,但需要更多显存 |
|
||||
| 训练轮数(Epoch) | 遍历整个数据集的次数 | 10 ~ 100+ | 配合早停法,验证集不再提升就停 |
|
||||
| 优化器 | 梯度更新策略 | Adam、SGD | Adam 是默认选择,SGD+动量适合精调 |
|
||||
|
||||
### 4.3 正则化技巧
|
||||
|
||||
防止过拟合的常用手段:
|
||||
|
||||
| 技巧 | 原理 | 使用方式 |
|
||||
|------|------|---------|
|
||||
| Dropout | 训练时随机关闭部分神经元 | 通常 p=0.1~0.5 |
|
||||
| 权重衰减 | 在损失函数中加入权重大小的惩罚 | L2 正则化,λ=1e-4 |
|
||||
| 数据增强 | 对训练数据做随机变换(翻转、裁剪、旋转) | 图像任务必备 |
|
||||
| 早停法 | 验证集损失不再下降时停止训练 | patience=5~10 |
|
||||
| Batch Normalization | 标准化每层的输入分布 | 加速收敛,有轻微正则化效果 |
|
||||
|
||||
::: tip 训练的经验法则
|
||||
1. 先用小数据集跑通整个流程,确认代码没 bug
|
||||
2. 从已有的预训练模型开始微调,而非从零训练
|
||||
3. 学习率是最值得花时间调的超参数
|
||||
4. 如果训练损失不下降,先检查数据和代码,再怀疑模型
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 发展历程与前沿
|
||||
|
||||
神经网络的发展经历了几次"寒冬"和"复兴",每次突破都源于关键的技术创新。
|
||||
|
||||
| 年代 | 里程碑 | 关键突破 |
|
||||
|------|--------|---------|
|
||||
| 1958 | 感知机(Perceptron) | 第一个神经网络模型,只能处理线性问题 |
|
||||
| 1986 | 反向传播算法 | 让多层网络的训练成为可能 |
|
||||
| 1998 | LeNet(CNN) | 卷积网络在手写数字识别上大获成功 |
|
||||
| 2012 | AlexNet | 深度 CNN 在 ImageNet 上碾压传统方法,深度学习爆发 |
|
||||
| 2014 | GAN(生成对抗网络) | 两个网络对抗训练,能生成逼真图像 |
|
||||
| 2017 | Transformer | "Attention Is All You Need",注意力机制取代 RNN |
|
||||
| 2018 | BERT | 预训练+微调范式,NLP 全面突破 |
|
||||
| 2020 | GPT-3 | 1750 亿参数,展示了大模型的涌现能力 |
|
||||
| 2022 | ChatGPT | RLHF 对齐技术,AI 进入大众视野 |
|
||||
| 2023+ | 多模态大模型 | GPT-4V、Claude 等,同时理解文本和图像 |
|
||||
|
||||
### 当前趋势
|
||||
|
||||
| 方向 | 说明 |
|
||||
|------|------|
|
||||
| 大模型(LLM) | 参数量从亿级到万亿级,涌现出推理、编程等能力 |
|
||||
| 多模态 | 同一个模型处理文本、图像、音频、视频 |
|
||||
| 高效微调 | LoRA、QLoRA 等技术让普通开发者也能微调大模型 |
|
||||
| AI Agent | 让大模型使用工具、规划任务、自主完成复杂目标 |
|
||||
| 小模型蒸馏 | 用大模型的知识训练小模型,在端侧部署 |
|
||||
|
||||
::: tip 对开发者的启示
|
||||
你不需要从零训练神经网络。现代 AI 开发更多是**调用 API**(如 OpenAI、Claude API)或**微调预训练模型**(如用 Hugging Face)。但理解底层原理能帮你更好地选择模型、设计 prompt、诊断问题。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 核心概念 | 一句话总结 |
|
||||
|---------|-----------|
|
||||
| 神经元 | 加权求和 + 激活函数,网络的最小计算单元 |
|
||||
| 前向传播 | 数据从输入层逐层流向输出层,产生预测 |
|
||||
| 反向传播 | 从损失出发,逐层计算梯度,更新权重 |
|
||||
| CNN | 卷积核提取局部特征,图像处理的首选 |
|
||||
| RNN/LSTM | 循环连接保持记忆,处理序列数据 |
|
||||
| Transformer | 自注意力并行处理,大模型的基础架构 |
|
||||
| 过拟合 | 模型"背答案",用正则化、Dropout 等手段防止 |
|
||||
| 迁移学习 | 站在巨人肩膀上,用预训练模型微调解决新问题 |
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [3Blue1Brown - 神经网络系列视频](https://www.3blue1brown.com/topics/neural-networks) — 最直观的可视化讲解
|
||||
- [Stanford CS231n](http://cs231n.stanford.edu/) — 经典的卷积神经网络课程
|
||||
- [The Illustrated Transformer](https://jalammar.github.io/illustrated-transformer/) — 图解 Transformer 架构
|
||||
- [Neural Networks and Deep Learning](http://neuralnetworksanddeeplearning.com/) — 免费在线教材
|
||||
- [Hugging Face 课程](https://huggingface.co/learn) — 动手实践 Transformer 和大模型
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
本附录涵盖从计算机基础到工程素养的完整知识体系,是你学习旅程中的重要参考库。
|
||||
|
||||
## 📍 附录知识地图
|
||||
|
||||
点击下方的分类卡片,查看每个分类的完整学习路径:
|
||||
|
||||
<AppendixFlowMap />
|
||||
|
||||
---
|
||||
|
||||
## 内容分类
|
||||
|
||||
### 一、计算机是怎么回事
|
||||
|
||||
Reference in New Issue
Block a user