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.
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.
comet_change
role
canonical_spec
analysis-storage-refactor
technical-design
openspec
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
封装三级存储逻辑,提供统一读写接口
analysis_db.py
Analysis 数据库连接管理,降级逻辑
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 读取流程
读取请求
↓
检查 Redis 缓存
↓ 命中
返回数据
↓ 未命中
检查 MySQL 可用性
↓ 可用
查询 MySQL → 回填 Redis → 返回
↓ 不可用
降级到 SQLite → 返回
2.2 写入流程
写入请求
↓
删除 Redis 缓存
↓
写入 MySQL( 事务)
↓ 成功
回填 Redis → 返回
↓ 失败
写入 SQLite → 返回
2.3 降级状态检测
惰性检测: 30 秒间隔
Redis 不可用时跳过缓存层
MySQL 不可用时降级到 SQLite
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 缓存键设计
analysis:{table_name}:{id}
analysis:{table_name}:list:{query_hash}
4.2 缓存失效
4.3 缓存回填
读取时未命中,查询 MySQL 后回填
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 查询压力