summit/DATABASE.md

120 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 数据库设计文档 (DATABASE.md)
本项目采用 **PostgreSQL (PostGIS)** 作为主数据库,**Redis** 作为多级缓存层。
---
## 1. PostgreSQL 数据库设计
### 1.1 扩展 (Extensions)
必须启用 PostGIS 扩展以支持地理空间数据存储与查询。
```sql
CREATE EXTENSION IF NOT EXISTS postgis;
```
### 1.2 表结构 (Tables)
#### 1.2.1 `peaks` (山峰表)
存储 14 座 8000 米级山峰的核心元数据。
```sql
CREATE TABLE peaks (
id SERIAL PRIMARY KEY,
name_en VARCHAR(100) NOT NULL UNIQUE, -- 英文名 (e.g., "Everest")
name_cn VARCHAR(100), -- 中文名
elevation_m INTEGER NOT NULL, -- 海拔 (米)
prominence_m INTEGER, -- 地形突起度
location GEOMETRY(POINT, 4326), -- 经纬度坐标 (WGS84)
first_ascent_year INTEGER, -- 首登年份
description TEXT, -- 简介
thumbnail_url VARCHAR(255), -- 缩略图 URL
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE INDEX idx_peaks_location ON peaks USING GIST (location);
```
#### 1.2.2 `routes` (攀登路线表)
存储山峰的攀登路线,支持一条山峰多条路线。路线的几何数据量较大,通过 GeoJSON 或 PostGIS LineString 存储。
```sql
CREATE TABLE routes (
id SERIAL PRIMARY KEY,
peak_id INTEGER REFERENCES peaks(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL, -- 路线名称 (e.g., "South Col Route")
difficulty VARCHAR(50), -- 难度等级
path_geometry GEOMETRY(LINESTRINGZ, 4326), -- 3D 路径 (包含海拔 Z)
description TEXT,
is_standard_route BOOLEAN DEFAULT FALSE, -- 是否为常规路线
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
-- 索引
CREATE INDEX idx_routes_peak_id ON routes(peak_id);
CREATE INDEX idx_routes_geometry ON routes USING GIST (path_geometry);
```
#### 1.2.3 `camps` (营地表)
存储攀登路线上的关键营地位置 (Base Camp, C1, C2, etc.)。
```sql
CREATE TABLE camps (
id SERIAL PRIMARY KEY,
route_id INTEGER REFERENCES routes(id) ON DELETE CASCADE,
name VARCHAR(50) NOT NULL, -- 营地名 (e.g., "Camp 4")
elevation_m INTEGER, -- 营地海拔
location GEOMETRY(POINT, 4326), -- 营地坐标
camp_type VARCHAR(20) CHECK (camp_type IN ('BaseCamp', 'AdvancedBaseCamp', 'Camp', 'Summit')),
description TEXT
);
-- 索引
CREATE INDEX idx_camps_route_id ON camps(route_id);
```
---
## 2. Redis 缓存策略 (Cache Strategy)
采用多层缓存结构以提升 API 响应速度和减轻数据库 GIS 计算压力。
### 2.1 键命名规范 (Key Naming Convention)
`{prefix}:{resource}:{identifier}:{field}`
### 2.2 缓存层级与 TTL
| 数据类型 | Redis Key 模式 | 数据结构 | TTL (过期时间) | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| **API 响应** | `api:peaks:list` | String (JSON) | 24 Hours | 所有山峰列表的轻量级 JSON |
| **山峰详情** | `data:peak:{id}` | Hash | 7 Days | 单个山峰的详细信息 |
| **路线 GeoJSON** | `data:route:{id}:geojson` | String (GeoJSON) | 30 Days | 路线的完整几何数据 (体积大,极少变动) |
| **高程查询** | `cache:elev:{lat}_{lon}` | String | 30 Days | 特定坐标的高程缓存 (避免重复栅格计算) |
| **用户会话** | `session:{token}` | String | 2 Hours | 用户登录状态 (如有) |
### 2.3 缓存更新逻辑
1. **Read-Through:** 读取数据时,先查 Redis。命中则返回未命中则查 PostgreSQL写入 Redis 并返回。
2. **Write-Invalidate:** 更新数据库 (如修正路线数据) 时,立即删除对应的 Redis Key (e.g., `DEL data:route:123:geojson`),下次读取时自动重建。
---
## 3. 示例查询 (Example Queries)
### 3.1 查找珠峰附近 50km 内的所有其他 8000 米山峰
```sql
SELECT name_en, elevation_m,
ST_Distance(location::geography, (SELECT location::geography FROM peaks WHERE name_en = 'Everest')) as dist_meters
FROM peaks
WHERE id != (SELECT id FROM peaks WHERE name_en = 'Everest')
AND ST_DWithin(location::geography, (SELECT location::geography FROM peaks WHERE name_en = 'Everest'), 50000);
```
### 3.2 获取某条路线的 3D 长度
```sql
SELECT name, ST_3DLength(path_geometry::geography) as length_meters
FROM routes
WHERE id = 1;
```