第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:

做市系统优化四维模型 系统优化 数据库优化 网络优化 代码优化 架构优化 索引优化 连接池配置 读写分离+缓存 物理层面(同机房) 协议层面(TCP/UDP) 应用层面(连接复用) 减少对象创建 避免锁竞争 算法与数据结构 微服务化 事件驱动架构 多活部署 优化目标:低延迟、高吞吐、高可用 四个维度缺一不可,需要持续迭代

系统优化不是一锤子买卖。数据库、网络、代码、架构,每个维度都需要持续关注。我个人的习惯是,每上线一个新功能,都会跑一遍性能测试,看看有没有新的瓶颈出现。

嗯,优化这件事,没有终点。但只要方向对了,每一步都是进步。


交易系统化学习资料 微信Strategy888888