Init
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
---
|
||||
tags: [后端, Go, Gin, 路由, 性能]
|
||||
create time: 2026-04-27 00:00
|
||||
---
|
||||
|
||||
# 路由匹配复杂度对比:Gin vs http.ServeMux
|
||||
|
||||
## 概述
|
||||
|
||||
对比 Gin 的 Radix Tree 路由匹配与 Go 标准库 `http.ServeMux` 的匹配机制,从数据结构、算法复杂度到实际性能差异进行全面分析。
|
||||
|
||||
## 正文
|
||||
|
||||
### 1. ServeMux 的匹配机制
|
||||
|
||||
`http.ServeMux` 内部维护一个 `map[string]pattern` 映射,匹配一个 URL 时需要:
|
||||
|
||||
1. **遍历所有已注册的路由**(按最长前缀匹配规则)
|
||||
2. 对每条路由做一次**完整的路径字符串比较**
|
||||
3. 时间复杂度 **O(R × L)**,其中 **R = 路由总数**,**L = URL 长度**
|
||||
|
||||
> 补充细节:`ServeMux` 支持两种匹配模式:前缀匹配(`/prefix/`)和精确匹配。前缀匹配需要从所有可能的前缀中筛选出最长的候选者,因此会遍历多个条目,而不是 O(1) 的哈希查找。
|
||||
|
||||
### 2. Gin Radix Tree 的匹配机制
|
||||
|
||||
Gin 的路由组织为一棵压缩前缀树(Radix Tree),匹配过程:
|
||||
|
||||
1. 从根节点开始,沿着 URL 的字符**逐段向下走**
|
||||
2. 每段匹配利用前缀共享,一次比较就决定走向
|
||||
3. 时间复杂度 **O(D)**,其中 **D = URL 深度(路径段数)**,通常只有 3-5 层
|
||||
|
||||
> 与路由总数 **R 无关**。10 条路由和 10000 条路由,匹配 `/api/v1/users/5` 的步数几乎一样。
|
||||
|
||||
### 3. 具体差距
|
||||
|
||||
| 场景 | ServeMux | Gin (Radix Tree) |
|
||||
|------|----------|-----------------|
|
||||
| 10 条路由 | ~10 次完整比较 | ~4 步 |
|
||||
| 100 条路由 | ~100 次 | ~4 步 |
|
||||
| 1000 条路由 | ~1000 次 | ~4 步 |
|
||||
| 10000 条路由 | ~10000 次 | ~4 步 |
|
||||
|
||||
**数量级差异**:当路由数增长时,ServeMux 线性增长,Gin 基本保持不变。在路由较多的场景下,Gin 的匹配速度通常是 ServeMux 的 **10~100 倍甚至更多**。
|
||||
|
||||
### 4. 根本原因:数据结构差异
|
||||
|
||||
- **ServeMux 用的是哈希表**:`map[string]Handler` 虽然对精确匹配是 O(1),但 HTTP 路由还需要处理前缀匹配(`/api/...`),这退化为遍历比较。哈希表丢失了"路径前缀"的结构信息。
|
||||
|
||||
- **Gin 用的是 Radix Tree**:天然保留路径前缀结构,匹配时走树不走表,每一步都能利用前缀信息直接跳到下一段,不需要回溯或比较无关路由。
|
||||
|
||||
**一句话总结**:ServeMux 是在一叠卡片里逐张翻,Gin 是在字典里按部首和拼音直接定位。
|
||||
|
||||
### 5. 可视化对比
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph S ["ServeMux:遍历比较"]
|
||||
S1["遍历路由 #1: /users/list"] --> S2["遍历路由 #2: /users/:id"]
|
||||
S2 --> S3["遍历路由 #3: /users/*path"]
|
||||
S3 --> S4["..."]
|
||||
S4 --> S5["遍历路由 #N"]
|
||||
end
|
||||
|
||||
subgraph G ["Radix Tree:树形定位"]
|
||||
G1["根节点 /"] --> G2["users"]
|
||||
G2 --> G3{下一步?}
|
||||
G3 -->|"list"| G4["list ✓"]
|
||||
G3 -->|"数字"| G5[":id ✓"]
|
||||
G3 -->|"其他"| G6["*path ✓"]
|
||||
end
|
||||
|
||||
classDef S fill:#ffebee,stroke:#c62828
|
||||
classDef G fill:#e8f5e9,stroke:#2e7d32
|
||||
class S,S1,S2,S3,S4,S5 S
|
||||
class G,G1,G2,G3,G4,G5,G6 G
|
||||
```
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- `[[2-routing]]` — Gin 路由匹配与分组机制
|
||||
Reference in New Issue
Block a user