ethan-skill 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -24
- package/dist/cli/index.js +3 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/skills/15-git-workflow.d.ts +3 -0
- package/dist/skills/15-git-workflow.d.ts.map +1 -0
- package/dist/skills/15-git-workflow.js +288 -0
- package/dist/skills/15-git-workflow.js.map +1 -0
- package/dist/skills/16-unit-testing.d.ts +3 -0
- package/dist/skills/16-unit-testing.d.ts.map +1 -0
- package/dist/skills/16-unit-testing.js +298 -0
- package/dist/skills/16-unit-testing.js.map +1 -0
- package/dist/skills/17-system-design.d.ts +3 -0
- package/dist/skills/17-system-design.d.ts.map +1 -0
- package/dist/skills/17-system-design.js +294 -0
- package/dist/skills/17-system-design.js.map +1 -0
- package/dist/skills/18-database-optimize.d.ts +3 -0
- package/dist/skills/18-database-optimize.d.ts.map +1 -0
- package/dist/skills/18-database-optimize.js +294 -0
- package/dist/skills/18-database-optimize.js.map +1 -0
- package/dist/skills/19-docker.d.ts +3 -0
- package/dist/skills/19-docker.d.ts.map +1 -0
- package/dist/skills/19-docker.js +360 -0
- package/dist/skills/19-docker.js.map +1 -0
- package/dist/skills/20-cicd.d.ts +3 -0
- package/dist/skills/20-cicd.d.ts.map +1 -0
- package/dist/skills/20-cicd.js +364 -0
- package/dist/skills/20-cicd.js.map +1 -0
- package/dist/skills/21-performance.d.ts +3 -0
- package/dist/skills/21-performance.d.ts.map +1 -0
- package/dist/skills/21-performance.js +139 -0
- package/dist/skills/21-performance.js.map +1 -0
- package/dist/skills/22-refactoring.d.ts +3 -0
- package/dist/skills/22-refactoring.d.ts.map +1 -0
- package/dist/skills/22-refactoring.js +235 -0
- package/dist/skills/22-refactoring.js.map +1 -0
- package/dist/skills/23-observability.d.ts +3 -0
- package/dist/skills/23-observability.d.ts.map +1 -0
- package/dist/skills/23-observability.js +266 -0
- package/dist/skills/23-observability.js.map +1 -0
- package/dist/skills/24-design-patterns.d.ts +3 -0
- package/dist/skills/24-design-patterns.d.ts.map +1 -0
- package/dist/skills/24-design-patterns.js +258 -0
- package/dist/skills/24-design-patterns.js.map +1 -0
- package/dist/skills/index.d.ts +10 -0
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +41 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/skills.test.js +3 -3
- package/dist/skills/skills.test.js.map +1 -1
- package/dist/templates/templates.test.js +2 -3
- package/dist/templates/templates.test.js.map +1 -1
- package/package.json +1 -1
- package/rules/claude-code/CLAUDE.md +2410 -3
- package/rules/cline/.clinerules +2262 -2
- package/rules/codebuddy/CODEBUDDY.md +2361 -2
- package/rules/continue/.continuerules +2262 -2
- package/rules/copilot/copilot-instructions.md +2331 -2
- package/rules/cursor/.cursorrules +2399 -2
- package/rules/cursor/smart-flow.mdc +2399 -2
- package/rules/jetbrains/smart-flow.md +2331 -2
- package/rules/lingma/smart-flow.md +2352 -3
- package/rules/windsurf/.windsurf/rules/smart-flow.md +2332 -3
- package/rules/zed/smart-flow.rules +2251 -1
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.systemDesignSkill = void 0;
|
|
4
|
+
exports.systemDesignSkill = {
|
|
5
|
+
id: 'system-design',
|
|
6
|
+
name: '系统设计',
|
|
7
|
+
nameEn: 'system_design',
|
|
8
|
+
order: 17,
|
|
9
|
+
category: '执行侧',
|
|
10
|
+
description: '从需求澄清到架构设计全流程,完成高并发分布式系统的方案设计与权衡分析',
|
|
11
|
+
descriptionEn: 'Full system design process from requirement clarification to architecture with scalability and availability tradeoffs',
|
|
12
|
+
detailDescription: `系统性指导大型系统设计流程,涵盖需求澄清与非功能指标量化、容量估算、高层架构选型、
|
|
13
|
+
核心组件设计、数据库模型与扩展性分析,帮助在面试或实际项目中输出结构清晰、考量全面的系统设计方案。`,
|
|
14
|
+
triggers: [
|
|
15
|
+
'系统设计',
|
|
16
|
+
'system design',
|
|
17
|
+
'架构设计',
|
|
18
|
+
'architecture design',
|
|
19
|
+
'高并发系统',
|
|
20
|
+
'分布式系统',
|
|
21
|
+
'distributed system',
|
|
22
|
+
'容量估算',
|
|
23
|
+
'capacity estimation',
|
|
24
|
+
'扩展性设计',
|
|
25
|
+
'scalability',
|
|
26
|
+
'@ethan design',
|
|
27
|
+
'@ethan system-design',
|
|
28
|
+
],
|
|
29
|
+
steps: [
|
|
30
|
+
{
|
|
31
|
+
title: '1. 需求澄清与范围界定',
|
|
32
|
+
content: `在动手设计前,花 5 分钟澄清需求:
|
|
33
|
+
|
|
34
|
+
**功能需求(Functional Requirements)**
|
|
35
|
+
- 系统的核心用例是什么?(写出 3-5 个最关键的)
|
|
36
|
+
- 哪些功能在 scope 内,哪些明确 out of scope?
|
|
37
|
+
- 用户角色有哪些?各自的主要操作是什么?
|
|
38
|
+
|
|
39
|
+
**非功能需求(Non-Functional Requirements)**
|
|
40
|
+
|
|
41
|
+
| 维度 | 问题 | 示例指标 |
|
|
42
|
+
|------|------|---------|
|
|
43
|
+
| 规模 | 用户量 / DAU / QPS 是多少? | 1亿用户,1000万 DAU |
|
|
44
|
+
| 性能 | 读写延迟要求?P99 是多少? | P99 < 100ms |
|
|
45
|
+
| 可用性 | 允许多少停机时间? | 99.9%(每年 8.7h) |
|
|
46
|
+
| 一致性 | 强一致 or 最终一致? | 最终一致(可接受) |
|
|
47
|
+
| 持久性 | 数据丢失容忍度? | RPO = 0(不允许丢失) |
|
|
48
|
+
|
|
49
|
+
**明确边界的示例问题**
|
|
50
|
+
\`\`\`
|
|
51
|
+
Q: 设计一个 Twitter
|
|
52
|
+
A(先澄清):
|
|
53
|
+
- 只需要发推/关注/Feed 功能吗?(排除私信、广告)
|
|
54
|
+
- 用户规模:3亿用户,1亿 DAU?
|
|
55
|
+
- 读写比例:推文读多写少,100:1?
|
|
56
|
+
- 媒体文件:支持图片/视频吗?
|
|
57
|
+
- 全球分发还是单地区?
|
|
58
|
+
\`\`\``,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
title: '2. 容量估算(Back-of-Envelope)',
|
|
62
|
+
content: `快速估算系统规模,为架构决策提供数据依据:
|
|
63
|
+
|
|
64
|
+
**常用基准数字**
|
|
65
|
+
\`\`\`
|
|
66
|
+
内存访问: ~100ns
|
|
67
|
+
SSD 访问: ~100μs
|
|
68
|
+
HDD 访问: ~10ms
|
|
69
|
+
网络往返(同数据中心):~0.5ms
|
|
70
|
+
网络往返(跨地区): ~100ms
|
|
71
|
+
|
|
72
|
+
1 MB = 10^6 bytes
|
|
73
|
+
1 GB = 10^9 bytes
|
|
74
|
+
1 TB = 10^12 bytes
|
|
75
|
+
\`\`\`
|
|
76
|
+
|
|
77
|
+
**估算示例:设计微博(Twitter-like)**
|
|
78
|
+
\`\`\`
|
|
79
|
+
用户数据:
|
|
80
|
+
- DAU: 1亿
|
|
81
|
+
- 每用户每天发1条推文 → 写 QPS = 100M / 86400 ≈ 1160 QPS
|
|
82
|
+
- 每用户每天读100条 → 读 QPS = 100 × 1160 = 116,000 QPS
|
|
83
|
+
|
|
84
|
+
存储估算:
|
|
85
|
+
- 单条推文: 140字 × 2字节(UTF-16) = 280字节 ≈ 300字节
|
|
86
|
+
- 元数据(user_id, timestamp等): 100字节
|
|
87
|
+
- 每条推文总计: ~400字节
|
|
88
|
+
- 每日新增: 1.16K QPS × 400字节 × 86400 = ~40 GB/天
|
|
89
|
+
- 5年存储: 40GB × 365 × 5 ≈ 73 TB
|
|
90
|
+
|
|
91
|
+
带宽估算:
|
|
92
|
+
- 写带宽: 1160 × 400字节 = ~450 KB/s
|
|
93
|
+
- 读带宽: 116K × 400字节 = ~45 MB/s
|
|
94
|
+
\`\`\`
|
|
95
|
+
|
|
96
|
+
**结论:** 读多写少(100:1),需要读缓存;存储量大需分库分表;单机无法支撑读 QPS 需多副本。`,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
title: '3. 高层架构设计',
|
|
100
|
+
content: `从整体入手,画出系统的核心模块和数据流:
|
|
101
|
+
|
|
102
|
+
**通用分层架构**
|
|
103
|
+
\`\`\`
|
|
104
|
+
客户端 (Web/Mobile/API Consumer)
|
|
105
|
+
│
|
|
106
|
+
▼
|
|
107
|
+
DNS + CDN (静态资源 / 地理路由)
|
|
108
|
+
│
|
|
109
|
+
▼
|
|
110
|
+
Load Balancer (L4/L7, 负载均衡 + SSL 终止)
|
|
111
|
+
┌────┴────┐
|
|
112
|
+
▼ ▼
|
|
113
|
+
API Srv API Srv (无状态,水平扩展)
|
|
114
|
+
│
|
|
115
|
+
├──→ Cache (Redis: 热数据)
|
|
116
|
+
├──→ Message Queue (Kafka: 异步解耦)
|
|
117
|
+
├──→ Primary DB (写操作)
|
|
118
|
+
└──→ Read Replica (读操作)
|
|
119
|
+
│
|
|
120
|
+
▼
|
|
121
|
+
Object Storage (S3: 文件/媒体)
|
|
122
|
+
Search Engine (Elasticsearch)
|
|
123
|
+
\`\`\`
|
|
124
|
+
|
|
125
|
+
**架构选型决策点**
|
|
126
|
+
|
|
127
|
+
| 场景 | 选型建议 |
|
|
128
|
+
|------|---------|
|
|
129
|
+
| 读多写少 | 读写分离 + 缓存层 |
|
|
130
|
+
| 高写入吞吐 | 异步消息队列削峰 |
|
|
131
|
+
| 数据量超百亿行 | 分库分表 / NoSQL |
|
|
132
|
+
| 强一致性 | 单主 / Paxos / Raft |
|
|
133
|
+
| 最终一致性 | 多主 / CRDT |
|
|
134
|
+
| 低延迟全球访问 | CDN + 多地域部署 |
|
|
135
|
+
| 复杂查询 | 专用搜索引擎 |
|
|
136
|
+
|
|
137
|
+
**微服务 vs 单体 决策**
|
|
138
|
+
- 团队 < 10人,初创期:单体优先(避免过度工程)
|
|
139
|
+
- 明确的服务边界、独立扩展需求:拆分微服务
|
|
140
|
+
- 拆分原则:按业务边界(DDD 限界上下文),而非技术层`,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
title: '4. 核心组件深度设计',
|
|
144
|
+
content: `针对最关键的 2-3 个组件进行深入设计:
|
|
145
|
+
|
|
146
|
+
**数据库 Schema 设计**
|
|
147
|
+
\`\`\`sql
|
|
148
|
+
-- 示例:推文表设计
|
|
149
|
+
CREATE TABLE tweets (
|
|
150
|
+
id BIGINT PRIMARY KEY, -- Snowflake ID(分布式唯一ID)
|
|
151
|
+
user_id BIGINT NOT NULL,
|
|
152
|
+
content VARCHAR(280) NOT NULL,
|
|
153
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
154
|
+
like_count INT DEFAULT 0,
|
|
155
|
+
retweet_count INT DEFAULT 0,
|
|
156
|
+
INDEX idx_user_created (user_id, created_at DESC) -- 用户时间线查询
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
-- Fan-out 策略:预写 vs 拉取
|
|
160
|
+
-- 方案A: Push(写扩散): 发推时写入所有粉丝的 Feed 表
|
|
161
|
+
-- 方案B: Pull(读扩散): 读取时聚合关注者的推文
|
|
162
|
+
-- 混合方案: 普通用户 Push,大V(粉丝>100万)Pull
|
|
163
|
+
\`\`\`
|
|
164
|
+
|
|
165
|
+
**缓存策略**
|
|
166
|
+
\`\`\`
|
|
167
|
+
Cache-Aside(旁路缓存)- 最通用
|
|
168
|
+
读: 查缓存 → miss → 查DB → 写缓存 → 返回
|
|
169
|
+
写: 更新DB → 删除缓存(避免双写不一致)
|
|
170
|
+
|
|
171
|
+
Write-Through(写穿)- 一致性高
|
|
172
|
+
写: 同时写DB和缓存
|
|
173
|
+
|
|
174
|
+
Write-Behind(写回)- 高性能
|
|
175
|
+
写: 先写缓存,异步批量写DB(风险:缓存宕机丢数据)
|
|
176
|
+
|
|
177
|
+
缓存 Key 设计示例:
|
|
178
|
+
user:{userId}:profile → 用户资料
|
|
179
|
+
user:{userId}:feed:page:{n} → 用户 Feed 分页
|
|
180
|
+
tweet:{tweetId} → 单条推文
|
|
181
|
+
\`\`\`
|
|
182
|
+
|
|
183
|
+
**API 接口设计**
|
|
184
|
+
\`\`\`
|
|
185
|
+
POST /tweets 发布推文
|
|
186
|
+
GET /users/{id}/feed 获取 Feed (cursor分页)
|
|
187
|
+
POST /tweets/{id}/like 点赞
|
|
188
|
+
GET /tweets/{id} 获取单条推文
|
|
189
|
+
|
|
190
|
+
分页策略: cursor-based > offset-based(大数据量场景)
|
|
191
|
+
cursor: base64(created_at + tweet_id)
|
|
192
|
+
\`\`\``,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
title: '5. 可扩展性与可用性权衡',
|
|
196
|
+
content: `**CAP 定理实践**
|
|
197
|
+
\`\`\`
|
|
198
|
+
C(一致性)+ A(可用性)+ P(分区容错)三选二
|
|
199
|
+
网络分区不可避免 → 通常是 CP 或 AP 的选择
|
|
200
|
+
|
|
201
|
+
CP 系统: ZooKeeper, HBase(金融交易、库存扣减)
|
|
202
|
+
AP 系统: Cassandra, DynamoDB(社交Feed、购物车)
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
**水平扩展策略**
|
|
206
|
+
|
|
207
|
+
| 层次 | 策略 |
|
|
208
|
+
|------|------|
|
|
209
|
+
| 无状态应用层 | 直接水平扩展 + 负载均衡 |
|
|
210
|
+
| 有状态缓存 | 一致性哈希分片(Redis Cluster) |
|
|
211
|
+
| 数据库水平 | 分库分表(按 user_id % N) |
|
|
212
|
+
| 数据库垂直 | 主从复制,读写分离 |
|
|
213
|
+
|
|
214
|
+
**单点故障(SPOF)消除清单**
|
|
215
|
+
- [ ] Load Balancer 双活/主备
|
|
216
|
+
- [ ] 数据库主从 + 自动故障转移(MHA/Orchestrator)
|
|
217
|
+
- [ ] 缓存集群(Redis Sentinel / Cluster)
|
|
218
|
+
- [ ] 消息队列多副本(Kafka Replication Factor ≥ 3)
|
|
219
|
+
- [ ] 跨可用区部署(Multi-AZ)
|
|
220
|
+
|
|
221
|
+
**限流与熔断**
|
|
222
|
+
\`\`\`
|
|
223
|
+
限流: Token Bucket(突发流量友好)
|
|
224
|
+
Sliding Window(精准限流)
|
|
225
|
+
分级限流: 用户级 → 接口级 → 全局
|
|
226
|
+
|
|
227
|
+
熔断: Closed → Open(失败率>50%)→ Half-Open(探测恢复)
|
|
228
|
+
工具: Resilience4j(Java)/ hystrix-go / Polly(.NET)
|
|
229
|
+
\`\`\``,
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
title: '6. 输出系统设计文档',
|
|
233
|
+
content: `整理为结构化设计文档:
|
|
234
|
+
|
|
235
|
+
\`\`\`markdown
|
|
236
|
+
## 系统设计方案:[系统名称]
|
|
237
|
+
|
|
238
|
+
### 1. 需求概述
|
|
239
|
+
**功能需求**(核心功能列表)
|
|
240
|
+
**非功能需求**(QPS / 延迟 / 可用性 / 存储)
|
|
241
|
+
|
|
242
|
+
### 2. 容量估算
|
|
243
|
+
| 指标 | 估算值 |
|
|
244
|
+
|------|-------|
|
|
245
|
+
| DAU | X 万 |
|
|
246
|
+
| 写 QPS | X |
|
|
247
|
+
| 读 QPS | X |
|
|
248
|
+
| 存储(5年) | X TB |
|
|
249
|
+
|
|
250
|
+
### 3. 系统架构图
|
|
251
|
+
[ASCII 图或 Mermaid 图]
|
|
252
|
+
|
|
253
|
+
### 4. 核心组件设计
|
|
254
|
+
- **数据库 Schema**:[关键表设计]
|
|
255
|
+
- **缓存策略**:[策略选择与理由]
|
|
256
|
+
- **API 设计**:[关键接口]
|
|
257
|
+
|
|
258
|
+
### 5. 扩展性方案
|
|
259
|
+
- **瓶颈点**:[识别的瓶颈]
|
|
260
|
+
- **解决方案**:[具体方案]
|
|
261
|
+
|
|
262
|
+
### 6. 权衡与风险
|
|
263
|
+
[已知权衡和设计风险]
|
|
264
|
+
\`\`\``,
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
outputFormat: 'Markdown 系统设计文档,含需求澄清结果、容量估算数据、架构图、核心组件设计方案和扩展性权衡分析',
|
|
268
|
+
examples: [
|
|
269
|
+
{
|
|
270
|
+
input: '设计一个短链接服务(如 bit.ly)',
|
|
271
|
+
output: `## 短链接服务设计
|
|
272
|
+
|
|
273
|
+
**规模**:每天 1 亿次创建,10 亿次访问(10:1 读写比)
|
|
274
|
+
**写 QPS**:~1160,**读 QPS**:~11600,存储5年:~40TB
|
|
275
|
+
|
|
276
|
+
**核心架构**:Web → LB → API Server → Redis(热门短链缓存)→ Cassandra(主存储)
|
|
277
|
+
|
|
278
|
+
**短链生成**:Base62 编码 7位(62^7 ≈ 3500亿,够用)
|
|
279
|
+
- 方案A: 预生成随机串存DB(无冲突)
|
|
280
|
+
- 方案B: ID自增 → Base62编码(简单高效,推荐)
|
|
281
|
+
|
|
282
|
+
**重定向**:301(永久,浏览器缓存,减少服务压力)vs 302(临时,可统计点击)
|
|
283
|
+
**数据库**:Cassandra(key-value 访问模式完美匹配,易扩展)`,
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
notes: [
|
|
287
|
+
'系统设计没有标准答案,重点展示思考过程和权衡意识',
|
|
288
|
+
'先画出高层架构,再逐步深入细节,避免一开始陷入细节',
|
|
289
|
+
'主动提出设计中的权衡和不足,展示对复杂度的认知',
|
|
290
|
+
'数量级估算误差在 10x 以内即可,重要的是数量级概念',
|
|
291
|
+
],
|
|
292
|
+
nextSkill: 'database-optimize',
|
|
293
|
+
};
|
|
294
|
+
//# sourceMappingURL=17-system-design.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"17-system-design.js","sourceRoot":"","sources":["../../src/skills/17-system-design.ts"],"names":[],"mappings":";;;AAEa,QAAA,iBAAiB,GAAoB;IAChD,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,eAAe;IACvB,KAAK,EAAE,EAAE;IACT,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,oCAAoC;IACjD,aAAa,EAAE,uHAAuH;IACtI,iBAAiB,EAAE;kDAC6B;IAChD,QAAQ,EAAE;QACR,MAAM;QACN,eAAe;QACf,MAAM;QACN,qBAAqB;QACrB,OAAO;QACP,OAAO;QACP,oBAAoB;QACpB,MAAM;QACN,qBAAqB;QACrB,OAAO;QACP,aAAa;QACb,eAAe;QACf,sBAAsB;KACvB;IACD,KAAK,EAAE;QACL;YACE,KAAK,EAAE,cAAc;YACrB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BR;SACF;QACD;YACE,KAAK,EAAE,2BAA2B;YAClC,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sDAkCuC;SACjD;QACD;YACE,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BAwCe;SACzB;QACD;YACE,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDR;SACF;QACD;YACE,KAAK,EAAE,eAAe;YACtB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCR;SACF;QACD;YACE,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BR;SACF;KACF;IACD,YAAY,EACV,qDAAqD;IACvD,QAAQ,EAAE;QACR;YACE,KAAK,EAAE,qBAAqB;YAC5B,MAAM,EAAE;;;;;;;;;;;;0CAY4B;SACrC;KACF;IACD,KAAK,EAAE;QACL,0BAA0B;QAC1B,2BAA2B;QAC3B,yBAAyB;QACzB,6BAA6B;KAC9B;IACD,SAAS,EAAE,mBAAmB;CAC/B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"18-database-optimize.d.ts","sourceRoot":"","sources":["../../src/skills/18-database-optimize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,qBAAqB,EAAE,eAkSnC,CAAC"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.databaseOptimizeSkill = void 0;
|
|
4
|
+
exports.databaseOptimizeSkill = {
|
|
5
|
+
id: 'database-optimize',
|
|
6
|
+
name: '数据库优化',
|
|
7
|
+
nameEn: 'database_optimize',
|
|
8
|
+
order: 18,
|
|
9
|
+
category: '质量侧',
|
|
10
|
+
description: '系统诊断数据库性能问题,涵盖 Schema 审查、索引设计、慢查询分析和 N+1 修复',
|
|
11
|
+
descriptionEn: 'Diagnose and optimize database performance covering schema review, index design, slow query analysis, and N+1 fixes',
|
|
12
|
+
detailDescription: `端到端数据库性能优化指导,从 Schema 设计审查、索引策略制定、慢查询日志分析、
|
|
13
|
+
N+1 查询识别与修复,到分区分表策略,帮助系统在数据量增长时保持查询性能。`,
|
|
14
|
+
triggers: [
|
|
15
|
+
'数据库优化',
|
|
16
|
+
'database optimize',
|
|
17
|
+
'慢查询',
|
|
18
|
+
'slow query',
|
|
19
|
+
'SQL 优化',
|
|
20
|
+
'SQL optimization',
|
|
21
|
+
'索引优化',
|
|
22
|
+
'index optimization',
|
|
23
|
+
'N+1 问题',
|
|
24
|
+
'N+1 query',
|
|
25
|
+
'查询性能',
|
|
26
|
+
'query performance',
|
|
27
|
+
'@ethan db',
|
|
28
|
+
'@ethan database-optimize',
|
|
29
|
+
],
|
|
30
|
+
steps: [
|
|
31
|
+
{
|
|
32
|
+
title: '1. Schema 设计审查',
|
|
33
|
+
content: `检查数据库表结构是否存在设计问题:
|
|
34
|
+
|
|
35
|
+
**规范化检查(防止冗余)**
|
|
36
|
+
\`\`\`sql
|
|
37
|
+
-- ❌ 反模式:在用户表存储地址字符串
|
|
38
|
+
CREATE TABLE users (
|
|
39
|
+
id INT PRIMARY KEY,
|
|
40
|
+
name VARCHAR(100),
|
|
41
|
+
address VARCHAR(500) -- 难以精准查询城市/省份
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
-- ✅ 正确:拆分为 addresses 表
|
|
45
|
+
CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100));
|
|
46
|
+
CREATE TABLE addresses (
|
|
47
|
+
id INT PRIMARY KEY,
|
|
48
|
+
user_id INT REFERENCES users(id),
|
|
49
|
+
province VARCHAR(50),
|
|
50
|
+
city VARCHAR(50),
|
|
51
|
+
detail VARCHAR(200)
|
|
52
|
+
);
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
**数据类型选择**
|
|
56
|
+
|
|
57
|
+
| 场景 | 推荐类型 | 避免 |
|
|
58
|
+
|------|---------|------|
|
|
59
|
+
| 主键 | BIGINT / UUID | INT(可能溢出) |
|
|
60
|
+
| 状态枚举 | TINYINT / ENUM | VARCHAR |
|
|
61
|
+
| 金额 | DECIMAL(10,2) | FLOAT(精度丢失)|
|
|
62
|
+
| 时间 | TIMESTAMP / DATETIME | VARCHAR |
|
|
63
|
+
| 短字符串(≤255) | VARCHAR(N) | TEXT |
|
|
64
|
+
| 布尔值 | TINYINT(1) | VARCHAR('true') |
|
|
65
|
+
|
|
66
|
+
**常见 Schema 问题清单**
|
|
67
|
+
- [ ] 是否有未使用的列?
|
|
68
|
+
- [ ] VARCHAR 长度是否合理(不要都 VARCHAR(255))?
|
|
69
|
+
- [ ] 外键是否有索引?
|
|
70
|
+
- [ ] 是否有重复的字段(非规范化导致)?
|
|
71
|
+
- [ ] 是否用了 TEXT/BLOB 存储应该单独存储的大文件?`,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
title: '2. 索引设计策略',
|
|
75
|
+
content: `**索引类型选择**
|
|
76
|
+
|
|
77
|
+
\`\`\`sql
|
|
78
|
+
-- 单列索引:高选择性字段(如 email、手机号)
|
|
79
|
+
CREATE INDEX idx_users_email ON users(email);
|
|
80
|
+
|
|
81
|
+
-- 联合索引:遵循最左前缀原则
|
|
82
|
+
-- 适合查询: WHERE status = ? AND created_at > ?
|
|
83
|
+
-- 适合查询: WHERE status = ?
|
|
84
|
+
-- 不适合: WHERE created_at > ? (无法命中)
|
|
85
|
+
CREATE INDEX idx_orders_status_created ON orders(status, created_at);
|
|
86
|
+
|
|
87
|
+
-- 覆盖索引:索引包含查询所有字段,避免回表
|
|
88
|
+
-- 查询: SELECT user_id, status FROM orders WHERE order_no = ?
|
|
89
|
+
CREATE INDEX idx_orders_covering ON orders(order_no, user_id, status);
|
|
90
|
+
|
|
91
|
+
-- 前缀索引:长字符串节省空间
|
|
92
|
+
CREATE INDEX idx_url_prefix ON pages(url(50));
|
|
93
|
+
|
|
94
|
+
-- 函数索引(MySQL 8.0+):对表达式建索引
|
|
95
|
+
CREATE INDEX idx_lower_email ON users((LOWER(email)));
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
**EXPLAIN 分析索引使用**
|
|
99
|
+
\`\`\`sql
|
|
100
|
+
EXPLAIN SELECT * FROM orders
|
|
101
|
+
WHERE user_id = 1001 AND status = 'PAID'
|
|
102
|
+
ORDER BY created_at DESC LIMIT 10;
|
|
103
|
+
|
|
104
|
+
-- 关注字段:
|
|
105
|
+
-- type: ref > range > index > ALL(ALL 最差)
|
|
106
|
+
-- key: 使用的索引名(NULL 表示未使用索引)
|
|
107
|
+
-- rows: 预估扫描行数(越小越好)
|
|
108
|
+
-- Extra: Using filesort / Using temporary(需优化的信号)
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
**索引原则**
|
|
112
|
+
- 高频查询的 WHERE / JOIN / ORDER BY 字段建索引
|
|
113
|
+
- 选择性低的字段慎建索引(如 status 只有3个值)
|
|
114
|
+
- 避免在频繁更新的列上建过多索引(写性能代价)
|
|
115
|
+
- 复合索引字段顺序:等值条件在前,范围条件在后`,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
title: '3. 慢查询分析与优化',
|
|
119
|
+
content: `**开启慢查询日志**
|
|
120
|
+
\`\`\`sql
|
|
121
|
+
-- MySQL 配置
|
|
122
|
+
SET GLOBAL slow_query_log = 'ON';
|
|
123
|
+
SET GLOBAL long_query_time = 1; -- 超过1秒记录
|
|
124
|
+
SET GLOBAL log_queries_not_using_indexes = 'ON';
|
|
125
|
+
|
|
126
|
+
-- 查看慢查询日志文件位置
|
|
127
|
+
SHOW VARIABLES LIKE 'slow_query_log_file';
|
|
128
|
+
|
|
129
|
+
-- 使用 pt-query-digest 分析日志
|
|
130
|
+
pt-query-digest /var/log/mysql/slow.log | head -100
|
|
131
|
+
\`\`\`
|
|
132
|
+
|
|
133
|
+
**常见慢查询模式与修复**
|
|
134
|
+
\`\`\`sql
|
|
135
|
+
-- ❌ 问题1: SELECT * 全列查询
|
|
136
|
+
SELECT * FROM orders WHERE user_id = 1001;
|
|
137
|
+
-- ✅ 修复: 只查需要的列
|
|
138
|
+
SELECT id, order_no, status, total FROM orders WHERE user_id = 1001;
|
|
139
|
+
|
|
140
|
+
-- ❌ 问题2: 对索引列使用函数,导致索引失效
|
|
141
|
+
SELECT * FROM orders WHERE DATE(created_at) = '2024-01-01';
|
|
142
|
+
-- ✅ 修复: 使用范围查询
|
|
143
|
+
SELECT * FROM orders
|
|
144
|
+
WHERE created_at >= '2024-01-01' AND created_at < '2024-01-02';
|
|
145
|
+
|
|
146
|
+
-- ❌ 问题3: OR 导致索引失效(某些情况)
|
|
147
|
+
SELECT * FROM users WHERE email = ? OR phone = ?;
|
|
148
|
+
-- ✅ 修复: UNION ALL
|
|
149
|
+
SELECT * FROM users WHERE email = ?
|
|
150
|
+
UNION ALL
|
|
151
|
+
SELECT * FROM users WHERE phone = ?;
|
|
152
|
+
|
|
153
|
+
-- ❌ 问题4: LIKE 前缀通配符
|
|
154
|
+
SELECT * FROM products WHERE name LIKE '%iPhone%';
|
|
155
|
+
-- ✅ 修复: 使用全文索引或 Elasticsearch
|
|
156
|
+
SELECT * FROM products WHERE MATCH(name) AGAINST('iPhone' IN BOOLEAN MODE);
|
|
157
|
+
|
|
158
|
+
-- ❌ 问题5: 隐式类型转换
|
|
159
|
+
SELECT * FROM users WHERE user_id = '1001'; -- user_id 是 INT
|
|
160
|
+
-- ✅ 修复: 类型匹配
|
|
161
|
+
SELECT * FROM users WHERE user_id = 1001;
|
|
162
|
+
\`\`\``,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
title: '4. N+1 查询识别与修复',
|
|
166
|
+
content: `**N+1 问题定义**:查询1次获取N条记录,再针对每条记录查询1次,共 N+1 次数据库访问。
|
|
167
|
+
|
|
168
|
+
**ORM 场景中的 N+1**
|
|
169
|
+
\`\`\`typescript
|
|
170
|
+
// ❌ TypeORM N+1 示例:查100个用户 → 执行101次SQL
|
|
171
|
+
const users = await userRepository.find(); // Query 1: SELECT * FROM users
|
|
172
|
+
for (const user of users) {
|
|
173
|
+
const orders = await user.orders; // Query 2-101: 每个用户各查一次
|
|
174
|
+
console.log(orders.length);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ✅ 修复:使用 eager loading(JOIN)
|
|
178
|
+
const users = await userRepository.find({
|
|
179
|
+
relations: ['orders'], // 一次 JOIN 查询搞定
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ✅ 或使用 QueryBuilder(更精确控制)
|
|
183
|
+
const users = await userRepository
|
|
184
|
+
.createQueryBuilder('user')
|
|
185
|
+
.leftJoinAndSelect('user.orders', 'order')
|
|
186
|
+
.where('order.status = :status', { status: 'PAID' })
|
|
187
|
+
.getMany();
|
|
188
|
+
\`\`\`
|
|
189
|
+
|
|
190
|
+
**原生 SQL 批量查询模式**
|
|
191
|
+
\`\`\`sql
|
|
192
|
+
-- ❌ N+1: 循环查询
|
|
193
|
+
-- for user_id in user_ids: SELECT * FROM orders WHERE user_id = ?
|
|
194
|
+
|
|
195
|
+
-- ✅ 批量查询 + 应用层 Map 聚合
|
|
196
|
+
SELECT user_id, COUNT(*) as order_count, SUM(total) as total_amount
|
|
197
|
+
FROM orders
|
|
198
|
+
WHERE user_id IN (1,2,3,...,100) -- 一次查询
|
|
199
|
+
GROUP BY user_id;
|
|
200
|
+
-- 在应用层用 Map 按 user_id 聚合
|
|
201
|
+
\`\`\`
|
|
202
|
+
|
|
203
|
+
**检测 N+1 工具**
|
|
204
|
+
\`\`\`
|
|
205
|
+
- Laravel Debugbar(PHP)
|
|
206
|
+
- Django Debug Toolbar(Python)
|
|
207
|
+
- Bullet gem(Rails)
|
|
208
|
+
- TypeORM logging: { logging: true } 观察 SQL 数量
|
|
209
|
+
- DataLoader(GraphQL 场景批量加载)
|
|
210
|
+
\`\`\``,
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
title: '5. 分区与分表策略',
|
|
214
|
+
content: `**表分区(Partitioning)— 单机方案**
|
|
215
|
+
\`\`\`sql
|
|
216
|
+
-- 按时间范围分区(适合日志、订单历史)
|
|
217
|
+
CREATE TABLE orders (
|
|
218
|
+
id BIGINT,
|
|
219
|
+
user_id INT,
|
|
220
|
+
created_at DATETIME,
|
|
221
|
+
total DECIMAL(10,2)
|
|
222
|
+
) PARTITION BY RANGE (YEAR(created_at)) (
|
|
223
|
+
PARTITION p2022 VALUES LESS THAN (2023),
|
|
224
|
+
PARTITION p2023 VALUES LESS THAN (2024),
|
|
225
|
+
PARTITION p2024 VALUES LESS THAN (2025),
|
|
226
|
+
PARTITION pmax VALUES LESS THAN MAXVALUE
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
-- 分区裁剪:查询自动只扫描相关分区
|
|
230
|
+
SELECT * FROM orders WHERE created_at >= '2024-01-01';
|
|
231
|
+
-- 只扫描 p2024 分区,跳过历史分区
|
|
232
|
+
\`\`\`
|
|
233
|
+
|
|
234
|
+
**分库分表策略(超千万行后考虑)**
|
|
235
|
+
|
|
236
|
+
| 方案 | 分片键选择 | 适用场景 |
|
|
237
|
+
|------|----------|---------|
|
|
238
|
+
| 水平分表(同库) | user_id % N | 单库容量瓶颈 |
|
|
239
|
+
| 水平分库 | user_id % N | 读写 QPS 瓶颈 |
|
|
240
|
+
| 按地区分库 | region | 合规/延迟要求 |
|
|
241
|
+
|
|
242
|
+
\`\`\`
|
|
243
|
+
分片键选择原则:
|
|
244
|
+
- 选择查询中高频使用的字段(避免跨分片查询)
|
|
245
|
+
- 选择数据分布均匀的字段(避免热点)
|
|
246
|
+
- 一旦确定不能轻易更改
|
|
247
|
+
|
|
248
|
+
常见工具:
|
|
249
|
+
- ShardingSphere(Java)
|
|
250
|
+
- Vitess(MySQL 集群,YouTube 方案)
|
|
251
|
+
- Citus(PostgreSQL 分布式扩展)
|
|
252
|
+
\`\`\`
|
|
253
|
+
|
|
254
|
+
**读写分离配置**
|
|
255
|
+
\`\`\`
|
|
256
|
+
主库(Primary): 处理写操作 + 强一致读
|
|
257
|
+
从库(Replica): 处理读操作(注意主从延迟,通常 <1s)
|
|
258
|
+
|
|
259
|
+
适用于读写比 > 4:1 的场景
|
|
260
|
+
注意: 写后立即读可能读到旧数据(主从同步延迟)
|
|
261
|
+
解决: 重要读操作路由到主库;或用 Redis 缓存最新写入
|
|
262
|
+
\`\`\``,
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
outputFormat: 'Markdown 优化报告,含 Schema 问题列表、索引设计方案、慢查询 EXPLAIN 分析、N+1 修复代码示例和分区建议',
|
|
266
|
+
examples: [
|
|
267
|
+
{
|
|
268
|
+
input: '用户列表页加载很慢,查询订单表,数据量1000万行',
|
|
269
|
+
output: `## 数据库性能诊断
|
|
270
|
+
|
|
271
|
+
**Step 1: 执行 EXPLAIN**
|
|
272
|
+
\`\`\`sql
|
|
273
|
+
EXPLAIN SELECT o.*, u.name FROM orders o
|
|
274
|
+
JOIN users u ON o.user_id = u.id
|
|
275
|
+
WHERE o.status = 'PAID' ORDER BY o.created_at DESC LIMIT 20;
|
|
276
|
+
\`\`\`
|
|
277
|
+
|
|
278
|
+
**发现问题**:type = ALL(全表扫描),rows = 10,000,000
|
|
279
|
+
|
|
280
|
+
**优化方案**:
|
|
281
|
+
1. 添加联合索引:\`CREATE INDEX idx_orders_status_created ON orders(status, created_at DESC);\`
|
|
282
|
+
2. 加 users 表关联索引:\`CREATE INDEX idx_users_id ON users(id);\`(通常主键已有)
|
|
283
|
+
3. 预期效果:rows 从 1000万 降至 <1000,响应时间从 5s → 50ms`,
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
notes: [
|
|
287
|
+
'优化前先用 EXPLAIN 分析,避免盲目加索引',
|
|
288
|
+
'索引不是越多越好,每个索引都会降低写入性能,控制在 5-8 个以内',
|
|
289
|
+
'分库分表是最后手段,优先考虑索引优化、缓存、读写分离',
|
|
290
|
+
'生产环境加索引使用 gh-ost 或 pt-online-schema-change,避免锁表',
|
|
291
|
+
],
|
|
292
|
+
nextSkill: 'docker',
|
|
293
|
+
};
|
|
294
|
+
//# sourceMappingURL=18-database-optimize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"18-database-optimize.js","sourceRoot":"","sources":["../../src/skills/18-database-optimize.ts"],"names":[],"mappings":";;;AAEa,QAAA,qBAAqB,GAAoB;IACpD,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,mBAAmB;IAC3B,KAAK,EAAE,EAAE;IACT,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,6CAA6C;IAC1D,aAAa,EAAE,qHAAqH;IACpI,iBAAiB,EAAE;uCACkB;IACrC,QAAQ,EAAE;QACR,OAAO;QACP,mBAAmB;QACnB,KAAK;QACL,YAAY;QACZ,QAAQ;QACR,kBAAkB;QAClB,MAAM;QACN,oBAAoB;QACpB,QAAQ;QACR,WAAW;QACX,MAAM;QACN,mBAAmB;QACnB,WAAW;QACX,0BAA0B;KAC3B;IACD,KAAK,EAAE;QACL;YACE,KAAK,EAAE,gBAAgB;YACvB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAsCoB;SAC9B;QACD;YACE,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAwCU;SACpB;QACD;YACE,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CR;SACF;QACD;YACE,KAAK,EAAE,gBAAgB;YACvB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4CR;SACF;QACD;YACE,KAAK,EAAE,YAAY;YACnB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDR;SACF;KACF;IACD,YAAY,EACV,mEAAmE;IACrE,QAAQ,EAAE;QACR;YACE,KAAK,EAAE,2BAA2B;YAClC,MAAM,EAAE;;;;;;;;;;;;;;8CAcgC;SACzC;KACF;IACD,KAAK,EAAE;QACL,0BAA0B;QAC1B,mCAAmC;QACnC,4BAA4B;QAC5B,iDAAiD;KAClD;IACD,SAAS,EAAE,QAAQ;CACpB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"19-docker.d.ts","sourceRoot":"","sources":["../../src/skills/19-docker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,WAAW,EAAE,eAoWzB,CAAC"}
|