1770 字
5 分钟
为什么这么设计 - 系列导读
系列简介
为什么这么设计(Why’s THE Design)是一系列关于计算机领域中程序设计决策的文章,我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。
本系列的核心目标是理解计算机领域中常见设计决策背后的原因。每个技术选择都有其历史背景、性能考量和工程权衡,通过深入分析这些决策,可以更好地理解系统的运行原理,并在实际工作中做出更明智的技术选择。
核心理念
mindmap
root((为什么这么设计))
为什么
历史背景
技术根源
设计哲学
怎么设计
权衡取舍
性能考量
工程实践
带来什么
优势
局限
应用场景
按分类组织
网络
| 编号 | 标题 | 简介 |
|---|---|---|
| 001 | 为什么 TCP 建立连接需要三次握手 | 深入解析三次握手的设计原理 |
| 005 | 为什么 DNS 使用 UDP 协议 | DNS 协议选择背后的考量 |
| 012 | 为什么 TCP 协议有性能问题 | TCP 性能瓶颈分析 |
| 013 | 为什么 UDP 头只有 8 个字节 | 协议头部设计原理 |
| 017 | 为什么 TCP/IP 协议会拆分数据 | MTU、MSS 与数据分片 |
| 018 | 为什么流媒体直播的延迟很高 | 直播延迟的根源与优化 |
| 019 | 为什么 HTTPS 需要 7 次握手以及 9 倍时延 | TLS 连接建立过程 |
| 020 | 为什么 TCP 协议有粘包问题 | 字节流与消息边界的矛盾 |
| 021 | 为什么 TCP 协议有 TIME_WAIT 状态 | 连接关闭后的等待机制 |
| 026 | 为什么 MAC 地址不需要全球唯一 | 局域网寻址机制 |
| 027 | 为什么 IPv6 难以取代 IPv4 | 协议演进困境 |
| 028 | 为什么集群需要 Overlay 网络 | 容器网络解决方案 |
| 049 | 为什么 CDN 能加速访问 | CDN 架构与缓存加速原理 |
| 053 | 为什么 HTTPS 需要证书 | PKI 证书体系与安全认证 |
| 048 | 为什么 HTTP 是无状态协议 | 无状态协议的设计考量 |
| 056 | 为什么 WebSocket 需要握手 | 实时通信协议升级机制 |
| 057 | 为什么 gRPC 使用 HTTP/2 | 高性能 RPC 框架设计 |
数据库
| 编号 | 标题 | 简介 |
|---|---|---|
| 003 | 为什么 Redis 选择单线程模型 | 单线程架构的优势 |
| 009 | 为什么 MySQL 使用 B+ 树 | 索引结构设计选择 |
| 010 | 为什么 Redis 快照使用子进程 | COW 机制与持久化 |
| 011 | 为什么 MongoDB 使用 B 树 | B 树与 B+ 树的对比 |
| 015 | 为什么数据库会丢失数据 | 存储系统的可靠性 |
| 016 | 为什么比特币可以防篡改 | 区块链与分布式账本 |
| 024 | 为什么 MySQL 的自增主键不单调也不连续 | 自增锁与 InnoDB 机制 |
| 031 | 为什么数据库不应该使用外键 | 外键的代价与替代方案 |
| 036 | 为什么 OLAP 需要列式存储 | 列式存储的优势 |
| 047 | 为什么分布式系统有 CAP 定理 | 一致性、可用性与分区容错 |
| 051 | 为什么 Elasticsearch 使用倒排索引 | 搜索引擎核心数据结构 |
| 052 | 为什么 PostgreSQL 使用 MVCC | 多版本并发控制原理 |
| 054 | 为什么需要连接池 | 数据库连接优化策略 |
操作系统
| 编号 | 标题 | 简介 |
|---|---|---|
| 023 | 为什么 Linux 需要虚拟内存 | 虚拟内存的设计动机 |
| 029 | 为什么系统调用会消耗较多资源 | 用户态与内核态切换 |
| 030 | 为什么 Linux 默认页大小是 4KB | 页大小选择的历史因素 |
| 032 | 为什么 CPU 访问硬盘很慢 | 存储层次与访问延迟 |
| 033 | 为什么 NUMA 会影响程序的延迟 | 非统一内存访问架构 |
| 034 | 为什么 HugePages 可以提升数据库性能 | 大页内存与 TLB |
| 035 | 为什么 Linux 需要 Swapping | 内存交换机制 |
| 038 | 为什么早期的 Windows 需要整理碎片 | 磁盘碎片与文件系统 |
| 039 | 为什么 Linux 和 macOS 不需要碎片整理 | 先进的文件系统设计 |
编程语言
| 编号 | 标题 | 简介 |
|---|---|---|
| 014 | 为什么 Go 语言没有泛型 | Go 泛型设计背后的考量 |
| 041 | 为什么 Python 有 GIL | 全局解释器锁的设计权衡 |
| 055 | 为什么 Rust 有所有权系统 | 无 GC 内存安全的实现原理 |
软件工程
| 编号 | 标题 | 简介 |
|---|---|---|
| 002 | 为什么使用通信来共享内存 | CSP vs 共享内存模型 |
| 004 | 为什么你应该使用 Git 进行版本控制 | Git 的优势分析 |
| 006 | 为什么使用 MD5 存储密码非常危险 | 密码存储安全 |
| 007 | 为什么基础服务不需要高可用 | 可靠性与成本的权衡 |
| 008 | 为什么总是需要无意义的 ID | ID 设计的艺术 |
| 022 | 为什么 0.1 + 0.2 = 0.300000004 | IEEE 754 浮点数 |
| 025 | 为什么 0.1 + 0.2 = 0.3 | 十进制与二进制 |
| 037 | 为什么 Kubernetes 要替换 Docker | 容器运行时演进 |
| 043 | 为什么 JSON 没有注释 | JSON 设计哲学与限制 |
中间件
| 编号 | 标题 | 简介 |
|---|---|---|
| 044 | 为什么 Kafka 这么快 | 顺序写与零拷贝的性能优化 |
| 045 | 为什么 Redis 使用跳表而不是红黑树 | 有序集合数据结构选择 |
| 050 | 为什么 Docker 使用分层镜像 | UnionFS 与 Copy-on-Write 机制 |
| 058 | 为什么需要服务网格 | 微服务治理的演进与核心技术 |
前端
| 编号 | 标题 | 简介 |
|---|---|---|
| 040 | 为什么 JavaScript 是单线程的 | 事件循环与异步编程 |
| 042 | 为什么 CSS 选择器从右向左解析 | 浏览器渲染引擎的设计考量 |
| 046 | 为什么 React 使用虚拟 DOM | 声明式 UI 的设计原理 |
系列特色
深入浅出的分析
每一篇文章都从一个问题出发,通过多角度的分析,帮助读者理解技术决策背后的原因。我们不仅讨论”是什么”,更关注”为什么”和”会带来什么”。
图文并茂
大量使用 Mermaid 图表来可视化复杂的技术概念,包括流程图、架构图、时序图等,让抽象的概念变得直观易懂。
实用导向
所有分析都与实际工程实践紧密结合,帮助读者在理解原理的基础上,更好地应用于实际工作中。
学习建议
- 带着问题阅读:每篇文章都围绕一个核心问题展开,先思考这个问题,再阅读分析
- 理解权衡:技术选择往往没有绝对的对错,理解每种选择的优缺点比记住结论更重要
- 联系实际:尝试将文章中的分析与自己的工作实践联系起来
- 举一反三:掌握分析方法后,可以自己尝试分析其他技术决策
本系列持续更新中,欢迎读者提出感兴趣的技术问题。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时
相关文章 智能推荐
1
为什么 Rust 有所有权系统
技术科普 深入解析 Rust 所有权系统的设计哲学,理解如何在没有 GC 的情况下实现内存安全。
2
为什么 PostgreSQL 使用 MVCC
技术科普 深入解析多版本并发控制的设计原理,理解 PostgreSQL 如何实现高并发事务处理。
3
为什么 Python 有 GIL
技术科普 深入剖析 Python GIL 的设计原因、性能影响以及多线程编程的替代方案。
4
为什么总是需要无意义的 ID
技术科普 深入解析为什么计算机系统总是需要无意义的 ID,以及各种 ID 生成算法的设计权衡。
5
为什么 NUMA 会影响程序的延迟
技术科普 深入解析 NUMA 架构对程序性能的影响,为什么跨节点内存访问会带来额外延迟。






