积分系统对现有业务影响分析与隔离方案
版本: v1.0 创建日期: 2026-01-13 评估人: AXBlade Team 风险等级: 中等 ⚠️
目录
执行摘要
核心风险
| 风险类型 | 风险等级 | 影响范围 | 是否可控 |
|---|---|---|---|
| 查询性能下降 | 🟡 中 | trades表、positions表 | ✅ 可控 |
| 数据库连接池竞争 | 🟡 中 | 全局 | ✅ 可控 |
| 交易延迟增加 | 🟠 中高 | 核心交易流程 | ✅ 可控 |
| 数据不一致 | 🔴 高 | 积分vs实际数据 | ✅ 可控 |
| 表锁竞争 | 🟢 低 | positions表 | ✅ 可控 |
关键发现
✅ 好消息:
- 积分系统使用独立表,不修改现有业务表结构
- 仅读取业务表数据,不写入
- 采用异步处理,不阻塞交易流程
- 支持功能开关,可随时禁用
⚠️ 需要注意:
- 会增加对
trades、positions、referral_relations表的读查询 - 后台任务会定期扫描
positions表(每小时) - 与现有系统共享数据库连接池
- TradeEvent处理流程会增加额外逻辑
业务表依赖分析
依赖关系图
┌─────────────────────────────────────────────────────────────────┐
│ 积分系统(只读依赖) │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────┼───────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────┐ ┌──────────────────┐
│ trades │ │positions │ │referral_relations│
│ (只读) │ │ (只读) │ │ (只读) │
└─────────────┘ └──────────┘ └──────────────────┘
│ │ │
│ │ │
▼ ▼ ▼
现有交易系统 现有仓位系统 现有推荐系统
(不受影响) (不受影响) (不受影响)
详细依赖表
| 业务表 | 访问模式 | 访问频率 | 查询字段 | 影响评估 |
|---|---|---|---|---|
| trades | 只读-监听 | 每笔交易 | symbol, price, amount, taker_address, maker_address | 🟡 中等 |
| positions | 只读-批量 | 每小时 | user_address, symbol, size, entry_price, status | 🟢 低 |
| referral_relations | 只读-查询 | 每笔交易 | referee_address, referrer_address | 🟢 低 |
| orders | 无依赖 | 不访问 | - | ✅ 无影响 |
| balances | 无依赖 | 不访问 | - | ✅ 无影响 |
具体访问场景
场景1: 交易积分计算(实时)
// 在 TradeEvent 处理器中
async fn handle_trade_event(trade: TradeEvent) {
// 1. 现有逻辑(不变)
persist_trade(&trade).await?;
update_positions(&trade).await?;
// 2. 新增: 积分计算(异步,不阻塞)
if points_system_enabled() {
tokio::spawn(async move {
let _ = points_service.calculate_trading_points(&trade).await;
let _ = points_service.calculate_referral_points(&trade).await;
});
}
}
查询:
-- 查询用户Tier(带缓存,TTL 60s)
SELECT trading_volume FROM user_points_summary
WHERE user_address = $1 AND epoch_number = $2;
-- 查询推荐关系(带缓存,TTL 300s)
SELECT referrer_address FROM referral_relations
WHERE referee_address = $1;
影响分析:
- ✅ 使用
tokio::spawn异步执行,不阻塞交易流程 - ✅ 带缓存,命中率预计 > 80%
- ⚠️ 每笔交易增加 2次查询(缓存未命中时)
场景2: 持仓积分计算(定时)
// 后台任务,每小时执行一次
async fn calculate_holding_points_batch() {
// 批量查询所有活跃仓位
let positions = sqlx::query!(r#"
SELECT id, user_address, symbol, size, entry_price,
last_calculated_at
FROM positions
WHERE status = 'open'
"#)
.fetch_all(&pool)
.await?;
// 逐个计算积分(不修改positions表)
for position in positions {
calculate_single_holding_points(position).await?;
}
}
影响分析:
- ✅ 仅读取
positions表,不修改 - ✅ 每小时1次,频率低
- ⚠️ 如果有10000个活跃仓位,需要扫描10000行
- 💡 优化: 可添加索引
CREATE INDEX idx_positions_status ON positions(status) WHERE status = 'open';
场景3: PnL积分计算(平仓时)
// 在平仓逻辑中
async fn close_position(position_id: Uuid, realized_pnl: Decimal) {
// 1. 现有逻辑(不变)
update_position_status(position_id, "closed").await?;
update_user_balance(user, realized_pnl).await?;
// 2. 新增: PnL积分(异步)
if points_system_enabled() {
tokio::spawn(async move {
let _ = points_service.calculate_pnl_points(
user_address, position_id, realized_pnl
).await;
});
}
}
影响分析:
- ✅ 异步执行,不阻塞平仓
- ✅ 仅在盈利时计算积分
性能影响评估
查询负载增加
当前负载(假设)
| 表 | 当前QPS | 当前CPU% | 当前连接数 |
|---|---|---|---|
| trades | 50 | 15% | 10 |
| positions | 20 | 8% | 5 |
| referral_relations | 5 | 2% | 2 |