Skip to content

Commit 57c04a1

Browse files
committed
feat(database): 重构数据库结构为列式存储并更新相关文档
将数据库从 JSON 存储重构为列式存储,提升查询性能和数据管理能力。新增 V2 迁移指南文档,更新 Worker 代码以支持新结构,并完善了 D1 同步设置文档中的远程数据库操作说明。
1 parent 761c268 commit 57c04a1

File tree

13 files changed

+1904
-294
lines changed

13 files changed

+1904
-294
lines changed

D1_IMPLEMENTATION.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
# D1 列式存储实现总结
2+
3+
## 概述
4+
5+
完成了从 JSON 存储到 D1 列式存储的重构,所有数据现在以结构化列的形式存储在 Cloudflare D1 数据库中,提升了查询性能和数据管理能力。
6+
7+
## 架构变更
8+
9+
### 数据存储结构
10+
11+
**之前 (JSON 存储)**:
12+
```json
13+
{
14+
"path_stats": {...},
15+
"banned_ips": {...},
16+
"config": {...}
17+
}
18+
```
19+
20+
**现在 (列式存储)**:
21+
- `path_stats` → 独立列(path, request_count, error_count, bytes_transferred, ...)
22+
- `banned_ips` → 独立列(ip, ban_time, ban_end_time, reason, error_count, ...)
23+
- `banned_ips_history` → 历史记录表
24+
- `config_maps` → 路径配置表(一个路径一行)
25+
- `config_other` → 键值对配置表(系统配置)
26+
27+
## 核心文件
28+
29+
### Cloudflare Worker
30+
31+
1. **cloudflare-worker/migrations/0001_initial_schema.sql**
32+
- 定义了 5 个表的结构
33+
- path_stats: 路径统计(15 列)
34+
- banned_ips: IP 封禁(9 列)
35+
- banned_ips_history: 封禁历史(8 列 + 自增 ID)
36+
- config_maps: 路径配置(7 列)
37+
- config_other: 系统配置(4 列)
38+
39+
2. **cloudflare-worker/src/index.ts**
40+
- RESTful API 端点
41+
- 批量 UPSERT 支持
42+
- Bearer Token 认证
43+
44+
3. **cloudflare-worker/src/db.ts**
45+
- TypeScript 类型定义
46+
- 数据库操作辅助函数
47+
- 批量操作使用 D1 batch API
48+
49+
### Go 后端
50+
51+
1. **pkg/sync/d1_client.go**
52+
- D1 HTTP 客户端
53+
- 类型定义与 Worker 完全对应
54+
- 批量上传/查询方法
55+
56+
2. **pkg/sync/d1_converter.go**
57+
- 数据格式转换函数
58+
- `ConvertPathStatsFromFile()` - 转换路径统计
59+
- `ConvertBannedIPsFromFile()` - 转换封禁 IP
60+
- `ConvertBannedIPHistoryFromFile()` - 转换封禁历史
61+
- `ConvertConfigFromFile()` - 转换配置(拆分为 MAP 和 Other)
62+
63+
3. **pkg/sync/d1_manager.go**
64+
- D1 同步管理器
65+
- `SyncNow()` - 完整同步(config + path_stats + banned_ips)
66+
- `UploadConfig()` - 配置上传(使用转换器)
67+
- `downloadConfigWithFallback()` - 配置下载(重建 JSON 格式)
68+
- 自动转换数据格式
69+
70+
4. **pkg/sync/service.go**
71+
- `StopSyncService()` - **退出前自动同步**
72+
- 在程序关闭时触发一次完整同步
73+
- 30 秒超时保护
74+
75+
## 数据转换逻辑
76+
77+
### Config 转换
78+
79+
**原始格式** (config.json):
80+
```json
81+
{
82+
"MAP": {
83+
"/path1": {
84+
"DefaultTarget": "https://example.com",
85+
"Enabled": true,
86+
"ExtensionMap": {...},
87+
"CacheConfig": {...}
88+
}
89+
},
90+
"Cache": {...},
91+
"Compression": {...}
92+
}
93+
```
94+
95+
**转换后**:
96+
- ConfigMaps 表:
97+
```
98+
path | default_target | enabled | extension_rules (JSON) | cache_config (JSON)
99+
```
100+
- ConfigOther 表:
101+
```
102+
key (Cache/Compression/...) | value (JSON)
103+
```
104+
105+
### 下载时重建
106+
107+
下载配置时,D1Manager 会:
108+
1. 查询 ConfigMaps 和 ConfigOther
109+
2. 重建原始 JSON 结构
110+
3. 保存到本地 config.json
111+
112+
这样确保了与现有代码的兼容性。
113+
114+
## 同步流程
115+
116+
### 启动时
117+
118+
1. **InitSyncService()** - 初始化同步服务
119+
2. **DownloadConfigOnly()** - 下载远程配置
120+
- 如果远程没有配置,上传本地配置作为初始版本
121+
- 如果远程有配置,下载并重建本地配置
122+
123+
### 运行时
124+
125+
1. **定时同步** - 每 10 分钟自动同步
126+
- 同步 config.json(转换为 ConfigMaps + ConfigOther)
127+
- 同步 path_stats.json(转换为 PathStat[]
128+
- 同步 banned_ips.json(转换为 BannedIP[]
129+
130+
2. **配置变更同步** - 通过回调触发
131+
- config 保存时自动上传到 D1
132+
133+
### 退出时
134+
135+
**StopSyncService()** - 执行最后一次完整同步
136+
```go
137+
log.Printf("[Sync] Performing final sync before shutdown...")
138+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
139+
defer cancel()
140+
141+
if err := globalSyncService.manager.SyncNow(ctx); err != nil {
142+
log.Printf("[Sync] Warning: Final sync failed: %v", err)
143+
} else {
144+
log.Printf("[Sync] Final sync completed successfully")
145+
}
146+
```
147+
148+
## 环境变量配置
149+
150+
```bash
151+
# D1 Worker 配置
152+
D1_ENDPOINT=https://your-worker.your-subdomain.workers.dev
153+
D1_TOKEN=your-secure-token
154+
155+
# 旧的 S3 配置仍然支持(向后兼容)
156+
S3_ENDPOINT=...
157+
S3_BUCKET=...
158+
```
159+
160+
优先级: D1 > S3
161+
162+
## 部署步骤
163+
164+
### 1. 部署 Cloudflare Worker
165+
166+
```bash
167+
cd cloudflare-worker
168+
169+
# 创建 D1 数据库(如果还没有)
170+
wrangler d1 create proxy-go-data
171+
172+
# 更新 wrangler.toml 中的 database_id
173+
174+
# 运行迁移(重要:必须使用 --remote)
175+
wrangler d1 migrations apply proxy-go-data --remote
176+
177+
# 部署 Worker
178+
wrangler deploy
179+
180+
# 设置 API Token(生产环境)
181+
wrangler secret put API_TOKEN
182+
```
183+
184+
### 2. 配置 Go 后端
185+
186+
```bash
187+
# 设置环境变量
188+
export D1_ENDPOINT=https://your-worker.your-subdomain.workers.dev
189+
export D1_TOKEN=your-secure-token
190+
191+
# 启动服务
192+
./proxy-go
193+
```
194+
195+
## 数据迁移
196+
197+
如果已有 JSON 数据文件,不需要手动迁移:
198+
199+
1. 启动服务时会自动检测 D1 是否有数据
200+
2. 如果 D1 为空,会自动上传本地 JSON 文件数据
201+
3. 转换器会自动处理格式转换
202+
203+
手动迁移(如果需要):
204+
```bash
205+
# 触发完整同步
206+
curl -X POST http://localhost:3336/admin/api/sync/now
207+
```
208+
209+
## API 端点
210+
211+
### Worker API
212+
213+
```
214+
GET /path-stats # 查询路径统计
215+
POST /path-stats # 批量上传路径统计
216+
GET /banned-ips # 查询封禁 IP
217+
POST /banned-ips # 批量上传封禁 IP
218+
GET /banned-ips/history # 查询封禁历史
219+
GET /config-maps # 查询路径配置
220+
POST /config-maps # 批量上传路径配置
221+
GET /config-other # 查询系统配置
222+
POST /config-other # 批量上传系统配置
223+
```
224+
225+
### Proxy-Go Admin API
226+
227+
```
228+
POST /admin/api/sync/now # 立即触发完整同步
229+
GET /admin/api/sync/status # 查询同步状态
230+
```
231+
232+
## 性能优化
233+
234+
1. **批量操作** - 使用 D1 batch API,一次性处理多行
235+
2. **UPSERT** - INSERT ... ON CONFLICT,避免先查询再插入
236+
3. **索引** - 主键自动索引(path, ip, key)
237+
4. **按需同步** - 封禁历史暂不同步,避免重复写入
238+
239+
## 注意事项
240+
241+
1. **时间戳格式** - 统一使用 UnixMilli(毫秒时间戳)
242+
2. **JSON 字段** - ExtensionRules 和 CacheConfig 存为 JSON TEXT
243+
3. **布尔值** - SQLite 使用 INTEGER (0/1),自动转换
244+
4. **退出同步** - 程序退出时自动触发一次完整同步,确保数据不丢失
245+
246+
## 向后兼容
247+
248+
- S3 同步仍然支持(如果同时配置了 D1 和 S3,优先使用 D1)
249+
- 本地 JSON 文件格式不变(配置、统计、封禁列表)
250+
- 现有 API 端点不变
251+
- 前端界面无需修改
252+
253+
## 测试清单
254+
255+
- [ ] Worker 部署成功
256+
- [ ] D1 迁移执行成功(--remote)
257+
- [ ] 启动时自动下载配置
258+
- [ ] 定时同步正常工作(10 分钟)
259+
- [ ] 配置变更自动同步
260+
- [ ] 退出时完整同步
261+
- [ ] 数据格式转换正确
262+
- [ ] 批量操作性能良好

D1_SYNC_SETUP.md

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,46 @@ database_id = "你的-database-id" # 替换为实际 ID
3838

3939
### 3. 运行数据库迁移
4040

41+
**重要**: D1 有本地和远程两个数据库环境,Worker 部署后访问的是**远程数据库**,所以必须对远程数据库运行迁移!
42+
43+
```bash
44+
# ⚠️ 对远程数据库运行迁移 (正确方式)
45+
wrangler d1 migrations apply proxy-go-data --remote
46+
47+
# 或者使用完整命令
48+
npx wrangler d1 migrations apply proxy-go-data --remote
49+
```
50+
51+
**验证迁移成功**:
52+
4153
```bash
42-
npm run d1:migrations
54+
# 查看远程数据库的表 (注意 --remote 标志)
55+
wrangler d1 execute proxy-go-data --remote --command "SELECT name FROM sqlite_master WHERE type='table'"
56+
57+
# 应该看到输出:
58+
# 🌀 Executing on remote database proxy-go-data...
59+
# ┌─────────────────┐
60+
# │ name │
61+
# ├─────────────────┤
62+
# │ config │
63+
# │ path_stats │
64+
# │ banned_ips │
65+
# └─────────────────┘
4366
```
4467

45-
这将创建 `config`, `path_stats`, `banned_ips` 三个表。
68+
**常见错误**:
69+
70+
**错误**: 忘记 `--remote` 标志
71+
```bash
72+
# 这只会在本地数据库创建表,Worker 无法访问!
73+
wrangler d1 migrations apply proxy-go-data # 缺少 --remote
74+
```
75+
76+
**正确**: 使用 `--remote` 标志
77+
```bash
78+
# Worker 可以访问远程数据库的表
79+
wrangler d1 migrations apply proxy-go-data --remote
80+
```
4681

4782
### 4. 设置 API Token (推荐)
4883

@@ -190,14 +225,55 @@ curl https://your-worker.workers.dev/banned_ips \
190225
```bash
191226
cd cloudflare-worker
192227

193-
# 查询数据
194-
wrangler d1 execute proxy-go-data \
228+
# ⚠️ 查询远程数据库 (别忘了 --remote)
229+
wrangler d1 execute proxy-go-data --remote \
195230
--command "SELECT * FROM config"
231+
232+
# 查看所有表
233+
wrangler d1 execute proxy-go-data --remote \
234+
--command "SELECT name FROM sqlite_master WHERE type='table'"
235+
236+
# 查看更新时间
237+
wrangler d1 execute proxy-go-data --remote \
238+
--command "SELECT updated_at FROM config"
196239
```
197240

198241
## 故障排查
199242

200-
### 1. 无法连接到 Worker
243+
### 1. D1 表不存在错误
244+
245+
**症状**:
246+
```
247+
D1_ERROR: no such table: config: SQLITE_ERROR
248+
```
249+
250+
**原因**: 没有对**远程数据库**运行迁移,只在本地数据库创建了表
251+
252+
**解决**:
253+
```bash
254+
cd cloudflare-worker
255+
256+
# 1. 对远程数据库运行迁移 (重要!)
257+
wrangler d1 migrations apply proxy-go-data --remote
258+
259+
# 2. 验证表已创建
260+
wrangler d1 execute proxy-go-data --remote \
261+
--command "SELECT name FROM sqlite_master WHERE type='table'"
262+
263+
# 3. 重新部署 Worker
264+
wrangler deploy
265+
266+
# 4. 重启 proxy-go
267+
cd ..
268+
./proxy-go
269+
```
270+
271+
**说明**:
272+
- 本地数据库 (`.wrangler/state/v3/d1/`) 只用于本地开发
273+
- Worker 部署后访问的是远程数据库 (Cloudflare 云端)
274+
- 必须使用 `--remote` 标志对远程数据库运行迁移!
275+
276+
### 2. 无法连接到 Worker
201277

202278
**症状**: 日志显示 "failed to send request"
203279

@@ -206,7 +282,7 @@ wrangler d1 execute proxy-go-data \
206282
- 确认 Worker 已成功部署
207283
- 测试 Worker URL: `curl https://your-worker.workers.dev/`
208284

209-
### 2. 认证失败
285+
### 3. 认证失败
210286

211287
**症状**: 日志显示 "Unauthorized" 或 "401"
212288

0 commit comments

Comments
 (0)