第28章 系统优化:数据库优化、网络优化、代码优化、架构优化
做市系统跑起来容易,跑得稳、跑得快,那是另一回事。
我记得刚入行那会儿,带我的老工程师跟我说过一句话:「你的策略再好,底层系统扛不住,一切都是零。」当时我不太理解,直到有一次,我们的做市系统在高频行情下直接卡死,数据库连接池爆满,网络延迟飙到几百毫秒,订单根本发不出去。那叫一个惨。
所以这一章,咱们聊聊系统优化。说白了,就是四个维度:数据库、网络、代码、架构。每个维度都有坑,也有对应的解法。
28.1 数据库优化:别让IO成为瓶颈
做市系统里,数据库主要存什么?订单、成交、持仓、风控参数。这些数据读写频繁,尤其是订单状态更新,每秒可能上千次。
我见过不少团队,上来就用MySQL默认配置,结果跑几天就慢成蜗牛。为什么?因为没做优化。
28.1.1 索引优化
索引不是越多越好。我曾经在一个项目里,看到有人给每列都加了索引,结果写入速度慢得离谱。正确的做法是:
- 高频查询字段加索引:比如订单ID、交易对、时间戳
- 避免冗余索引:联合索引能覆盖的,就别单独建
- 定期分析慢查询日志:找出真正需要优化的SQL
核心原则:索引是给查询用的,不是给写入用的。写入频繁的表,索引越少越好。
28.1.2 连接池配置
数据库连接池,很多人直接配个默认值就完事了。但做市系统不一样,连接数太少会排队,太多会撑爆数据库。
我个人习惯这样配:
# HikariCP 配置示例
maximumPoolSize: 50
minimumIdle: 10
connectionTimeout: 3000
idleTimeout: 600000
maxLifetime: 1800000
注意,连接池大小不是越大越好。我踩过坑,配了200个连接,结果数据库CPU直接飙到100%。后来发现,连接数超过CPU核心数的两倍,反而会降低吞吐量。
28.1.3 读写分离与缓存
做市系统里,读多写少的情况很常见。比如查询历史成交、持仓快照。这时候,读写分离就很有用。
- 主库写:订单、成交、风控
- 从库读:历史查询、报表、监控
- Redis缓存:行情快照、账户余额、实时持仓
避坑指南:我曾经把实时持仓也放Redis,结果宕机后数据全丢了。后来改成Redis+MySQL双写,Redis只做缓存,MySQL做持久化。嗯,稳多了。
28.2 网络优化:延迟就是金钱
做市交易,网络延迟直接决定你能不能抢到单。1毫秒的差距,可能就是盈利和亏损的分水岭。
28.2.1 物理层面
说白了,就是离交易所越近越好。很多做市商直接把服务器托管在交易所机房,或者同城的光纤直连。
- 同机房部署:延迟可以控制在0.1ms以内
- 光纤直连:比公网稳定,抖动小
- 避免跨洲通信:光速限制,物理距离没法突破
28.2.2 协议层面
TCP还是UDP?这是个经典问题。
TCP可靠,但三次握手、拥塞控制,延迟高。UDP快,但丢包重传得自己实现。
我个人的经验是:
- 行情订阅用UDP:丢几个包无所谓,实时性优先
- 订单发送用TCP:必须保证送达,丢单的代价太大
小技巧:TCP也可以优化。比如禁用Nagle算法、调整TCP缓冲区大小、使用SO_REUSEPORT。这些参数调好了,延迟能降30%以上。
28.2.3 应用层面
网络优化不只是底层的事。应用层也能做很多:
- 连接复用:别每次请求都新建连接,用连接池
- 批量发送:多个小包合并成大包,减少网络开销
- 异步非阻塞:用Netty或类似框架,别用阻塞IO
28.3 代码优化:细节决定成败
代码优化,很多人觉得是微优化,没什么大用。但你想想看,一个做市系统每秒处理几万笔订单,每笔订单省0.1微秒,整体就是几毫秒的提升。这可不是小数目。
28.3.1 减少对象创建
Java里,new对象是有成本的。GC一触发,整个系统都得停一下。
我见过一个项目,每次处理行情都new一个对象,结果GC频率高得吓人。后来改成对象池,性能直接翻倍。
// 不推荐:每次创建新对象
public Order parseOrder(String raw) {
return new Order(raw);
}
// 推荐:复用对象
private final ObjectPool<Order> pool = new ObjectPool<>(1000);
public Order parseOrder(String raw) {
Order order = pool.borrow();
order.parse(raw);
return order;
}
28.3.2 避免锁竞争
锁是性能杀手。做市系统里,很多操作需要并发,但锁一多,性能就下来了。
- 用无锁数据结构:比如ConcurrentHashMap、AtomicLong
- 减少锁粒度:分段锁、读写锁
- 尽量用CAS:比synchronized轻量
注意:CAS也不是万能的。高并发下,CAS自旋会消耗CPU。我遇到过CAS自旋导致CPU飙到90%的情况,后来改成分段锁才解决。
28.3.3 算法与数据结构
选对数据结构,比优化代码重要得多。
| 场景 | 推荐数据结构 | 原因 |
|---|---|---|
| 订单簿 | 跳表(SkipList) | 有序、插入删除快 |
| 行情快照 | 环形缓冲区 | 无锁、高性能 |
| 风控检查 | 布隆过滤器 | 快速判断是否存在 |
28.4 架构优化:从单体到分布式
系统规模大了,单体架构肯定扛不住。架构优化,说白了就是拆。
28.4.1 微服务化
把做市系统拆成多个服务:行情服务、订单服务、风控服务、清算服务。每个服务独立部署,独立扩容。
- 好处:某个服务挂了,不影响其他服务
- 坏处:服务间通信有延迟,需要做好容错
28.4.2 事件驱动架构
做市系统里,很多操作是事件驱动的。比如行情更新 -> 策略计算 -> 下单 -> 成交反馈。用消息队列解耦,是个好办法。
我常用的方案:Kafka做事件总线,每个服务订阅自己感兴趣的事件。这样,行情服务只管发行情,策略服务只管算策略,互不干扰。
28.4.3 多活部署
做市系统不能宕机。一旦宕机,损失的是真金白银。
多活部署,就是多个机房同时提供服务。一个机房挂了,流量自动切到另一个。
- 同城双活:延迟低,但容灾能力有限
- 异地多活:容灾能力强,但数据同步复杂
避坑指南:我曾经做过异地多活,结果数据同步延迟导致订单重复。后来改成「主备模式」,主库写,备库只读,故障时手动切换。虽然不够自动化,但至少不会出数据问题。
28.5 知识体系总览
下面这张图,把本章的核心逻辑串起来了。你可以把它当作优化 checklist:
系统优化不是一锤子买卖。数据库、网络、代码、架构,每个维度都需要持续关注。我个人的习惯是,每上线一个新功能,都会跑一遍性能测试,看看有没有新的瓶颈出现。
嗯,优化这件事,没有终点。但只要方向对了,每一步都是进步。