# 内存优化：修复 K 线缓存仍占用进程内存的问题

## 问题

即使配置了 Redis，K 线数据仍然在进程内存中累积，导致内存持续增长。

## 根本原因

在 `kline_stream.py` 的 `_handle_message_async` 中，即使有 Redis，代码仍然**先更新进程内存 `_kline_cache`**，然后再批量写入 Redis。这导致：

1. **数据双重存储**：进程内存和 Redis 都有数据
2. **批量写入延迟**：在批量写入之前（最多 2 秒），数据一直留在进程内存
3. **写入失败时数据残留**：如果 Redis 写入失败，数据不会从进程内存删除

## 修复方案

### 1. 修改 K 线处理逻辑

**文件**: `trading_system/kline_stream.py`

**修改前**：
- 先更新 `_kline_cache`（进程内存）
- 然后标记需要写入 Redis
- 批量写入 Redis 后删除进程内存中的 key

**修改后**：
- **有 Redis 时**：直接从 Redis 读取当前数据，更新后写回 Redis，**完全不写进程内存**
- **无 Redis 时**：才写进程内存（降级）

### 2. 限制 Redis 写入队列大小

`_redis_write_pending` 字典可能无限增长，添加了大小限制：
- 最多保留 **100 个**待写入项
- 超出时删除最旧的项

### 3. 删除重复定义

修复了代码中重复定义的变量（复制粘贴错误）。

## 修改详情

### `_handle_message_async` 方法

```python
# 修改前：总是先写进程内存
if key not in _kline_cache:
    _kline_cache[key] = []
cache_list = _kline_cache[key]
# ... 更新 cache_list ...
# 然后标记写入 Redis

# 修改后：有 Redis 时只写 Redis
if self._redis_cache:
    # 从 Redis 读取
    existing = await self._redis_cache.get(rkey)
    cache_list = list(existing) if existing else []
    # 更新 cache_list
    # 标记写入 Redis（不写进程内存）
else:
    # 无 Redis 时才写进程内存
    ...
```

## 预期效果

- **进程内存占用大幅降低**：有 Redis 时，K 线数据不再存储在进程内存中
- **内存增长停止**：不再有数据在进程内存中累积

## 需要重启服务

**重要**：修改后需要重启交易服务才能生效。

```bash
# 重启交易服务
pkill -f "python.*trading_system.main"
# 然后重新启动
```

## 验证

重启后，检查内存使用：

```bash
# 查看进程内存
ps aux | grep "trading_system.main" | awk '{print "MEM:", $4"%", "RSS:", $6/1024"MB"}'

# 或使用诊断脚本
cd backend
./检查内存问题.sh
```

正常情况下，有 Redis 时：
- K 线缓存进程内存应该**接近 0**（只有降级时的少量数据）
- 内存占用应该**稳定**，不再持续增长

## 补充：Ticker24h / BookTicker 进程内存（本次一并修复）

**原因**：Leader 进程里 `_ticker_24h_cache`、`_book_ticker_cache` 一直随 WS 消息更新，全市场 200+ 交易对常驻进程内存，且每次写 Redis 前还 `dict(_ticker_24h_cache)` 做完整拷贝，加重内存和分配。

**修改**（`ticker_24h_stream.py` / `book_ticker_stream.py`）：
- **有 Redis 时**：WS 只做「从 Redis 读出 → 合并本批/本条 → 写回 Redis」，**不再更新** `_ticker_24h_cache` / `_book_ticker_cache`。
- 进程内缓存只由 **refresh 循环**（每 2 秒从 Redis 拉一次）回填，并限制最多 **500 条**，避免无限增长。
- **无 Redis 时**：仍只写进程内存，并同样限制 500 条。

这样 Leader 不再在进程里常驻全量 ticker/bookTicker，也不为写 Redis 做整份拷贝。

---

## 其他可能的内存增长源

若重启后内存仍涨，可再查：

1. **WebSocket 消息队列**：是否有任务/消息堆积
2. **日志**：日志文件与 Redis 日志 handler 是否过大
3. **数据库连接**：连接池是否及时释放
4. **其他缓存**：`_price_cache`、`_symbol_info_cache` 等是否有上限并定期淘汰
