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.

4.7 KiB

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.

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 作为缓存,写入失败不影响数据持久化
  • 同步双写保证数据一致性,避免异步延迟导致的数据不一致

备选方案:

  • 异步双写: 性能更好,但可能出现数据不一致
  • 仅写 MySQLRedis 通过缓存未命中回填: 首次读取性能差

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 一键部署
  • [取舍] 同步双写性能略低于异步双写 → 换取数据一致性保证
  • [取舍] 三级降级增加代码复杂度 → 换取系统高可用性