393 lines
10 KiB
Markdown
393 lines
10 KiB
Markdown
# Cosmo 平台架构升级规划
|
||
|
||
## 一、现状分析
|
||
|
||
### 当前架构
|
||
- **前端数据**:静态JSON文件(galaxies, constellations, stars, probe-models)
|
||
- **后端数据**:NASA Horizons API实时查询 + 简单内存缓存
|
||
- **资源存储**:纹理和3D模型在前端 `public/` 目录
|
||
- **缓存策略**:内存缓存(进程级别,重启失效)
|
||
|
||
### 痛点
|
||
1. **数据管理分散**:前端JSON + 后端代码硬编码
|
||
2. **缓存不持久**:服务重启后需要重新查询NASA API
|
||
3. **资源管理混乱**:纹理、模型路径分散在前后端
|
||
4. **扩展困难**:添加新天体类型需要修改多处代码
|
||
5. **无历史数据**:无法查询天体历史轨迹
|
||
6. **性能问题**:NASA API查询慢(每个天体1-2秒)
|
||
|
||
### 未来需求
|
||
- 支持更多天体类型(彗星、小行星、系外行星等)
|
||
- 用户自定义天体
|
||
- 历史轨迹查询和时间旅行
|
||
- 性能优化(减少NASA API调用)
|
||
- 统一的资源管理
|
||
- 可能的多用户支持
|
||
|
||
---
|
||
|
||
## 二、技术方案
|
||
|
||
### 2.1 数据库方案
|
||
|
||
#### 推荐:PostgreSQL + SQLAlchemy
|
||
|
||
**选择理由**:
|
||
1. **功能强大**:支持复杂查询、全文搜索、JSON字段
|
||
2. **PostGIS扩展**:专门处理空间数据(未来可能需要)
|
||
3. **时间序列优化**:通过TimescaleDB扩展支持高效时间序列查询
|
||
4. **成熟生态**:Python生态支持好(asyncpg, SQLAlchemy 2.0异步支持)
|
||
5. **扩展性**:支持大规模数据和并发
|
||
|
||
**备选方案**:
|
||
- **SQLite**:适合单机部署,但功能和性能有限
|
||
- **MongoDB**:文档型数据库,但对关系查询支持不如PostgreSQL
|
||
|
||
#### 数据库设计
|
||
|
||
```sql
|
||
-- 天体类型枚举
|
||
CREATE TYPE celestial_type AS ENUM ('star', 'planet', 'moon', 'probe', 'comet', 'asteroid', 'galaxy', 'constellation');
|
||
|
||
-- 天体基本信息表
|
||
CREATE TABLE celestial_bodies (
|
||
id VARCHAR(50) PRIMARY KEY, -- JPL Horizons ID 或自定义ID
|
||
name VARCHAR(200) NOT NULL,
|
||
name_zh VARCHAR(200),
|
||
type celestial_type NOT NULL,
|
||
description TEXT,
|
||
metadata JSONB, -- 灵活存储各种元数据(launch_date, status等)
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 位置历史表(时间序列数据)
|
||
CREATE TABLE positions (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
body_id VARCHAR(50) REFERENCES celestial_bodies(id),
|
||
time TIMESTAMP NOT NULL,
|
||
x DOUBLE PRECISION NOT NULL, -- AU
|
||
y DOUBLE PRECISION NOT NULL,
|
||
z DOUBLE PRECISION NOT NULL,
|
||
source VARCHAR(50), -- 'nasa_horizons', 'calculated', 'user_defined'
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
CREATE INDEX idx_positions_body_time ON positions(body_id, time DESC);
|
||
|
||
-- 资源管理表(纹理、模型等)
|
||
CREATE TABLE resources (
|
||
id SERIAL PRIMARY KEY,
|
||
body_id VARCHAR(50) REFERENCES celestial_bodies(id),
|
||
type VARCHAR(50) NOT NULL, -- 'texture', 'model', 'icon'
|
||
file_path VARCHAR(500) NOT NULL, -- 相对于upload目录的路径
|
||
file_size INTEGER,
|
||
mime_type VARCHAR(100),
|
||
metadata JSONB, -- 分辨率、格式等信息
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- 静态数据表(星座、星系等不变数据)
|
||
CREATE TABLE static_data (
|
||
id SERIAL PRIMARY KEY,
|
||
category VARCHAR(50) NOT NULL, -- 'constellation', 'galaxy', 'star'
|
||
name VARCHAR(200) NOT NULL,
|
||
name_zh VARCHAR(200),
|
||
data JSONB NOT NULL, -- 完整的静态数据
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
|
||
-- NASA API缓存表(持久化缓存)
|
||
CREATE TABLE nasa_cache (
|
||
cache_key VARCHAR(500) PRIMARY KEY,
|
||
body_id VARCHAR(50),
|
||
start_time TIMESTAMP,
|
||
end_time TIMESTAMP,
|
||
step VARCHAR(10),
|
||
data JSONB NOT NULL,
|
||
expires_at TIMESTAMP NOT NULL,
|
||
created_at TIMESTAMP DEFAULT NOW()
|
||
);
|
||
CREATE INDEX idx_nasa_cache_expires ON nasa_cache(expires_at);
|
||
```
|
||
|
||
### 2.2 缓存策略
|
||
|
||
#### 三层缓存架构
|
||
|
||
```
|
||
请求 → L1 (内存) → L2 (Redis) → L3 (数据库) → L4 (NASA API)
|
||
```
|
||
|
||
**L1: 进程内存缓存**(已有)
|
||
- TTL: 10分钟
|
||
- 用途:当前时间的天体位置(最热数据)
|
||
- 实现:Python dict + TTL(已有cache.py)
|
||
|
||
**L2: Redis缓存**(新增)
|
||
- TTL:
|
||
- 当前时间数据:1小时
|
||
- 历史数据:7天
|
||
- 静态数据:永久(手动失效)
|
||
- 用途:
|
||
- NASA API响应缓存
|
||
- 会话数据
|
||
- 预计算结果
|
||
- 好处:
|
||
- 进程间共享
|
||
- 持久化(重启不丢失)
|
||
- 分布式支持
|
||
|
||
**L3: PostgreSQL数据库**
|
||
- 持久化存储
|
||
- 历史数据查询
|
||
- 复杂查询和统计
|
||
|
||
**L4: NASA Horizons API**
|
||
- 最终数据源
|
||
- 只在缓存未命中时调用
|
||
|
||
#### 缓存键设计
|
||
|
||
```python
|
||
# L1/L2 缓存键格式
|
||
"positions:{body_id}:{start}:{end}:{step}"
|
||
"static:{category}:{name}"
|
||
"texture:{body_id}:{type}"
|
||
|
||
# 示例
|
||
"positions:-31:2025-11-27:2025-11-27:1d"
|
||
"static:constellation:orion"
|
||
```
|
||
|
||
### 2.3 文件存储方案
|
||
|
||
#### 目录结构
|
||
|
||
```
|
||
cosmo/
|
||
├── backend/
|
||
│ ├── upload/ # 统一上传目录
|
||
│ │ ├── textures/
|
||
│ │ │ ├── planets/ # 行星纹理
|
||
│ │ │ ├── stars/ # 恒星纹理
|
||
│ │ │ └── probes/ # 探测器图标
|
||
│ │ ├── models/
|
||
│ │ │ ├── probes/ # 探测器3D模型
|
||
│ │ │ └── spacecraft/
|
||
│ │ └── data/ # 数据文件备份
|
||
│ └── app/
|
||
│ └── api/
|
||
│ └── static.py # 静态文件服务API
|
||
```
|
||
|
||
#### 文件访问
|
||
|
||
```python
|
||
# 后端提供统一的静态文件API
|
||
GET /api/static/textures/planets/earth.jpg
|
||
GET /api/static/models/probes/voyager1.glb
|
||
|
||
# 数据库记录
|
||
{
|
||
"body_id": "399",
|
||
"type": "texture",
|
||
"file_path": "textures/planets/2k_earth_daymap.jpg",
|
||
"url": "/api/static/textures/planets/2k_earth_daymap.jpg"
|
||
}
|
||
```
|
||
|
||
### 2.4 数据迁移路径
|
||
|
||
#### 阶段1:数据库基础设施(1-2天)
|
||
1. 安装PostgreSQL和Redis
|
||
2. 设置SQLAlchemy ORM
|
||
3. 创建数据库表结构
|
||
4. 数据迁移脚本:
|
||
- `CELESTIAL_BODIES` dict → `celestial_bodies` 表
|
||
- 前端JSON文件 → `static_data` 表
|
||
|
||
#### 阶段2:缓存层升级(1天)
|
||
1. 集成Redis客户端
|
||
2. 实现三层缓存逻辑
|
||
3. NASA API结果持久化到数据库
|
||
|
||
#### 阶段3:资源管理迁移(1天)
|
||
1. 迁移纹理文件到 `backend/upload/textures/`
|
||
2. 迁移3D模型到 `backend/upload/models/`
|
||
3. 建立资源表记录
|
||
4. 实现静态文件服务API
|
||
|
||
#### 阶段4:API重构(1-2天)
|
||
1. 新增数据库查询API
|
||
2. 前端调整为从后端API获取所有数据
|
||
3. 移除前端静态JSON文件依赖
|
||
|
||
#### 阶段5:优化和测试(1天)
|
||
1. 性能测试
|
||
2. 缓存命中率监控
|
||
3. 数据一致性验证
|
||
|
||
---
|
||
|
||
## 三、技术栈
|
||
|
||
### 后端新增依赖
|
||
|
||
#### Python包
|
||
```bash
|
||
# ORM和数据库
|
||
sqlalchemy>=2.0.0 # ORM框架(支持async)
|
||
asyncpg>=0.29.0 # PostgreSQL异步驱动
|
||
alembic>=1.12.0 # 数据库迁移工具
|
||
|
||
# Redis缓存
|
||
redis>=5.0.0 # Redis客户端
|
||
aioredis>=2.0.0 # 异步Redis(可选,redis 5.0+已内置async支持)
|
||
|
||
# 文件处理
|
||
python-multipart>=0.0.6 # 文件上传支持
|
||
aiofiles>=23.0.0 # 异步文件操作
|
||
Pillow>=10.0.0 # 图片处理(缩略图等)
|
||
```
|
||
|
||
#### 系统依赖
|
||
|
||
**PostgreSQL 15+**
|
||
```bash
|
||
# macOS
|
||
brew install postgresql@15
|
||
brew services start postgresql@15
|
||
|
||
# 创建数据库
|
||
createdb cosmo_db
|
||
```
|
||
|
||
**Redis 7+**
|
||
```bash
|
||
# macOS
|
||
brew install redis
|
||
brew services start redis
|
||
|
||
# 验证
|
||
redis-cli ping # 应返回 PONG
|
||
```
|
||
|
||
### 前端调整
|
||
- 移除静态JSON文件依赖
|
||
- 所有数据通过API获取
|
||
- 静态资源URL指向后端API
|
||
|
||
---
|
||
|
||
## 四、性能优化
|
||
|
||
### 预期改进
|
||
1. **NASA API调用减少90%**:通过数据库+Redis缓存
|
||
2. **首次加载加速**:从缓存/数据库读取,无需等待NASA API
|
||
3. **支持历史查询**:数据库存储历史位置数据
|
||
4. **并发能力提升**:Redis支持分布式缓存
|
||
|
||
### 监控指标
|
||
- 缓存命中率(L1/L2/L3)
|
||
- NASA API调用次数
|
||
- 数据库查询时间
|
||
- API响应时间
|
||
|
||
---
|
||
|
||
## 五、成本分析
|
||
|
||
### 开发成本
|
||
- 总工时:约6-7天
|
||
- 可分阶段实施,每阶段独立可用
|
||
|
||
### 运行成本
|
||
- PostgreSQL:~100MB内存(小规模)
|
||
- Redis:~50MB内存
|
||
- 磁盘:~500MB-1GB(数据+资源文件)
|
||
|
||
### 维护成本
|
||
- 数据库备份:每日自动备份
|
||
- 缓存清理:自动过期,无需人工干预
|
||
- 资源管理:统一后端管理,更容易维护
|
||
|
||
---
|
||
|
||
## 六、风险和备选方案
|
||
|
||
### 风险
|
||
1. **PostgreSQL依赖**:需要额外安装和维护
|
||
- 备选:先用SQLite,后续迁移
|
||
2. **数据迁移复杂度**:现有数据分散
|
||
- 缓解:编写完善的迁移脚本和回滚方案
|
||
3. **Redis单点故障**:Redis挂了影响性能
|
||
- 缓解:Redis只是缓存,挂了仍可从数据库读取
|
||
|
||
### 回滚方案
|
||
- 保留现有代码分支
|
||
- 数据库和缓存作为可选功能
|
||
- 降级到内存缓存 + NASA API直连
|
||
|
||
---
|
||
|
||
## 七、实施建议
|
||
|
||
### 推荐方案:**完整实施**
|
||
理由:
|
||
1. 项目正处于扩展期,早期投入架构收益大
|
||
2. PostgreSQL+Redis是成熟方案,风险可控
|
||
3. 支持未来功能扩展(用户系统、自定义天体等)
|
||
4. 性能提升明显(缓存命中后响应 <50ms)
|
||
|
||
### 简化方案(如果资源有限)
|
||
1. **只用PostgreSQL,不用Redis**
|
||
- 降级为两层:内存 → 数据库 → NASA API
|
||
- 仍可实现持久化和历史查询
|
||
- 性能略低但可接受
|
||
|
||
2. **只用Redis,不用PostgreSQL**
|
||
- 只做缓存,不做持久化
|
||
- 适合小规模、不需要历史数据的场景
|
||
- 不推荐(失去了数据管理能力)
|
||
|
||
---
|
||
|
||
## 八、下一步
|
||
|
||
确认方案后,我将:
|
||
|
||
1. **准备安装脚本**:自动化安装PostgreSQL和Redis
|
||
2. **生成数据库Schema**:完整的SQL DDL
|
||
3. **编写迁移脚本**:将现有数据导入数据库
|
||
4. **实现缓存层**:三层缓存逻辑
|
||
5. **重构API**:支持数据库查询
|
||
6. **迁移静态资源**:统一到后端管理
|
||
|
||
---
|
||
|
||
## 附录:配置示例
|
||
|
||
### PostgreSQL连接配置
|
||
```python
|
||
# .env
|
||
DATABASE_URL=postgresql+asyncpg://cosmo:password@localhost:5432/cosmo_db
|
||
```
|
||
|
||
### Redis连接配置
|
||
```python
|
||
# .env
|
||
REDIS_URL=redis://localhost:6379/0
|
||
```
|
||
|
||
### 数据库连接池配置
|
||
```python
|
||
# app/database.py
|
||
engine = create_async_engine(
|
||
DATABASE_URL,
|
||
pool_size=20,
|
||
max_overflow=10,
|
||
pool_pre_ping=True,
|
||
)
|
||
```
|