## Context

当前系统使用 SQLite 作为唯一存储后端，所有行情数据（`MarketData`）以 JSON 字符串形式存储在磁盘文件中。每次刷新行情时，系统需要从数据源采集数据、写入 SQLite，读取时再反序列化。这种架构在高频读写场景下存在性能瓶颈。

系统包含以下核心实体：
- `MarketData`: 行情 K 线数据（高频读写，数据量大）
- `SymbolTimestamp`: 合约数据时间戳（中频读写）
- `ScheduledTask`: 定时任务配置（低频读写）
- 用户认证、交易复盘等低频业务表

约束条件：
- 单机部署，需要本地运行 Redis
- 需要保证系统可用性，即使部分存储组件故障
- 现有 SQLite 数据需要迁移或兼容

## Goals / Non-Goals

**Goals:**
- 引入 Redis 作为热数据缓存层，提升行情数据读取性能（TTL 30 天）
- 引入 MySQL 作为主要持久化存储，替代 SQLite 承担日常读写
- 保留 SQLite 作为最终兜底方案，保证系统可用性
- 实现三级降级策略：Redis → MySQL → SQLite
- 刷新接口双写：数据同步写入 Redis 和 MySQL
- 启动时初始化多存储后端，检测可用性

**Non-Goals:**
- 不迁移用户认证、交易复盘等低频业务表（保持 SQLite）
- 不改造前端页面
- 不统一 API 接口规范（留给后续 change）
- 不实现分布式部署或集群方案

## Decisions

### 1. 存储分层策略

**决策**: 采用 Redis（缓存）+ MySQL（持久化）+ SQLite（兜底）三级架构。

**理由**:
- Redis 提供亚毫秒级读取性能，适合行情数据高频访问
- MySQL 提供可靠的持久化能力和事务支持，替代 SQLite 成为主存储
- SQLite 保留作为最终兜底，保证极端情况下系统仍可运行
- 三级降级保证系统可用性，避免单点故障

**备选方案**:
- Redis + PostgreSQL: PostgreSQL 功能更强大，但部署复杂度更高，MySQL 更轻量
- 纯 Redis: 无法满足持久化需求，数据丢失风险高
- 纯 MySQL: 缺少缓存层，读取性能提升有限

### 2. 数据访问层设计

**决策**: 新增 `StorageManager` 抽象层，封装三级降级逻辑。

**理由**:
- 业务代码无需关心底层存储实现
- 降级策略集中管理，便于维护和测试
- 可通过配置切换存储后端，支持渐进式迁移

**备选方案**:
- 直接在业务代码中实现降级逻辑: 代码重复，难以维护
- 使用 ORM 插件: 灵活性不足，难以实现复杂的降级策略

### 3. 双写一致性策略

**决策**: 刷新接口采用同步双写，先写 MySQL 再更新 Redis。

**理由**:
- MySQL 作为持久化主存储，必须先保证数据落盘
- Redis 作为缓存，写入失败不影响数据持久化
- 同步双写保证数据一致性，避免异步延迟导致的数据不一致

**备选方案**:
- 异步双写: 性能更好，但可能出现数据不一致
- 仅写 MySQL，Redis 通过缓存未命中回填: 首次读取性能差

### 4. Redis 缓存策略

**决策**: 行情数据 TTL 30 天，缓存未命中时从 MySQL 读取并回填。

**理由**:
- 30 天覆盖大部分行情数据的活跃访问周期
- 缓存未命中回填保证读取性能
- TTL 过期后自动清理，避免内存无限增长

**备选方案**:
- 永不过期: 内存占用不可控
- 更短 TTL（如 1 天）: 缓存命中率低，频繁回源 MySQL

### 5. 降级检测机制

**决策**: 启动时检测 Redis/MySQL 可用性，运行时捕获连接异常触发降级。

**理由**:
- 启动时检测可提前发现问题，输出明确的错误日志
- 运行时降级保证系统可用性，避免服务中断
- 降级状态可通过日志监控，便于运维排查

**备选方案**:
- 仅启动时检测: 无法处理运行中服务故障
- 健康检查接口: 增加复杂度，当前场景不需要

### 6. 数据迁移策略

**决策**: 新增 MySQL 表结构，启动时从 SQLite 迁移历史数据到 MySQL。

**理由**:
- 保证历史数据不丢失
- 一次性迁移，后续直接读写 MySQL
- SQLite 保留作为兜底，不删除

**备选方案**:
- 手动迁移: 容易遗漏，风险高
- 双写过渡期: 复杂度高，当前场景不需要

## Risks / Trade-offs

- **[风险]** Redis 和 MySQL 同时故障 → **[缓解]** SQLite 兜底，系统仍可运行
- **[风险]** 双写一致性：MySQL 写入成功但 Redis 写入失败 → **[缓解]** Redis 写入失败不影响持久化，下次读取时从 MySQL 回填
- **[风险]** Redis 内存占用过高 → **[缓解]** TTL 30 天自动清理，监控内存使用
- **[风险]** MySQL 部署复杂度增加 → **[缓解]** 使用 docker-compose 一键部署
- **[取舍]** 同步双写性能略低于异步双写 → 换取数据一致性保证
- **[取舍]** 三级降级增加代码复杂度 → 换取系统高可用性
