You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.0 KiB
6.0 KiB
| comet_change | role | canonical_spec | archived-with | status |
|---|---|---|---|---|
| analysis-storage-refactor | technical-design | openspec | 2026-07-04-analysis-storage-refactor | final |
Analysis Storage Refactor - Technical Design
1. 架构概览
1.1 目标架构
┌─────────────────────────────────────────────────────────────┐
│ 应用层 │
│ app/api/futures_analysis.py ←→ app/services/cache.py │
│ ↓ │
│ StorageManager │
│ (storage_manager.py) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────┼─────────────────────┐
↓ ↓ ↓
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Redis │ │ MySQL │ │ SQLite │
│ (缓存) │ │(持久化) │ │ (兜底) │
└─────────┘ └─────────┘ └─────────┘
1.2 核心组件
| 组件 | 职责 |
|---|---|
StorageManager |
提供通用 Redis 缓存方法(cache_get、cache_set、cache_delete) |
analysis_db.py |
Analysis 数据库连接管理,根据后端可用性返回 MySQL 或 SQLite Session |
analysis_models.py |
ORM 模型定义(不变) |
analysis_migration.py |
SQLite → MySQL 数据迁移 |
1.3 表清单
| 表名 | 用途 | 缓存 TTL |
|---|---|---|
futures_analysis |
期货分析报告 | 5 分钟 |
watched_symbols |
用户关注品种 | 10 分钟 |
ai_model_configs |
AI 模型配置 | 10 分钟 |
analysis_settings |
分析设置 | 10 分钟 |
ai_analysis_cache |
AI 分析缓存 | 5 分钟 |
review_dates |
复盘日期 | 1 小时 |
symbol_rankings |
品种排名 | 1 小时 |
trading_plans |
交易计划 | 1 小时 |
symbol_scores_v2 |
V2 品种评分 | 1 小时 |
trading_plans_v2 |
V2 交易计划 | 1 小时 |
sector_heat |
板块热度 | 1 小时 |
review_plans_v2 |
V2 复盘计划 | 1 小时 |
trade_records |
交易记录 | 不缓存 |
trade_import_batches |
交易导入批次 | 不缓存 |
2. 降级策略
2.1 数据库 Session 切换
analysis_db.py 中的 get_analysis_db() 根据后端可用性返回不同 Session:
业务代码 → get_analysis_db()
↓
MySQL 可用?
↓ 是 ↓ 否
MySQL Session SQLite Session
2.2 Redis 缓存层(可选)
对于高频读取场景,业务代码可通过 StorageManager.cache_get/cache_set/cache_delete 使用 Redis:
读取请求
↓
StorageManager.cache_get(key)
↓ 命中
返回数据
↓ 未命中
ORM 查询 MySQL/SQLite → StorageManager.cache_set(key, value) → 返回
2.3 降级状态检测
- 复用
StorageManager.check_mysql()惰性检测 MySQL 可用性 - MySQL 不可用时,
get_analysis_db()返回 SQLite Session - 保留 SQLite 文件
data/futures_analysis.db作为兜底
3. 数据迁移
3.1 迁移时机
应用启动时(lifespan),在 MySQL 表结构初始化后执行。
3.2 迁移逻辑
def migrate_analysis_sqlite_to_mysql():
"""将 SQLite analysis 数据迁移到 MySQL"""
# 1. 检测 MySQL 表是否已有数据
mysql_count = count_mysql_analysis_tables()
if mysql_count > 0:
logger.info("MySQL analysis 表已有数据,跳过迁移")
return
# 2. 从 SQLite 读取数据
sqlite_data = read_sqlite_analysis_data()
# 3. 批量写入 MySQL
write_mysql_analysis_data(sqlite_data)
logger.info("Analysis 数据迁移完成")
3.3 幂等性
- 检测 MySQL 表是否已有数据
- 有数据则跳过迁移
- 迁移失败不影响应用启动
4. 缓存策略
4.1 通用缓存方法
StorageManager 提供三个通用方法:
cache_get(key): 读取 Redis 缓存cache_set(key, value, ttl): 写入 Redis 缓存cache_delete(key): 删除 Redis 缓存
4.2 缓存键设计
由业务代码决定缓存键,推荐格式:
analysis:{table_name}:{id}
analysis:{table_name}:list:{query_hash}
4.3 缓存失效
- 写入时删除相关缓存
- TTL 过期自动失效
4.4 缓存回填
- 读取时未命中,查询 MySQL/SQLite 后回填
- TTL 与表类型对应
5. 兼容性
5.1 ORM 模型
- 保持不变
- 使用
AnalysisBase作为基类
5.2 API 接口
- 保持不变
- 通过
get_analysis_db()依赖注入
5.3 SQLite 文件
- 保留
data/futures_analysis.db - 降级时使用
6. 测试策略
6.1 单元测试
| 场景 | 测试方法 |
|---|---|
| Redis 缓存命中 | Mock Redis,验证不查询 MySQL |
| Redis 缓存未命中 | Mock Redis 返回 None,验证查询 MySQL |
| MySQL 降级到 SQLite | Mock MySQL 不可用,验证查询 SQLite |
| 数据迁移 | 验证 SQLite 数据完整迁移到 MySQL |
6.2 集成测试
- 启动应用,验证 MySQL 表创建
- 执行迁移,验证数据完整性
- 调用 API,验证功能正常
7. 风险与缓解
| 风险 | 缓解措施 |
|---|---|
| 数据迁移失败 | 保留 SQLite 作为降级方案,迁移失败时自动降级 |
| Redis 缓存不一致 | 写入时先删缓存,再写 MySQL,最后回填缓存 |
| 表结构不兼容 | 复用现有 ORM 模型,MySQL 自动创建表结构 |
| 性能下降 | Redis 缓存减少 MySQL 查询压力 |