从这一篇开始换个切面:不再按业务 subsystem 拆,而是看支撑所有业务的底座。第一个问题是数据库——券商核心系统(集中交易、清算、估值共用一套库或同构多库)的技术栈选择高度趋同:Oracle + 存储过程 + 定时批量作业,二十年不变。这不是惯性,是一组约束条件下的理性均衡。
为什么是 Oracle:历史与结构双重锁定
时间上,券商核心交易系统的大规模建设窗口在 1998–2005 年(营业部联网、集中交易改造),那个时间点上能承载金融级 OLTP 的商业数据库几乎只有 Oracle。选择本身是被时代决定的,但留下来是结构性的:
- RAC 共享存储集群:多节点读写同一数据,切换不停机。对交易时段 7×5 高可用、批量时段全速跑批的双重要求,RAC 的成熟度在很长时间里没有替代品;
- 物化视图与分区表:批量作业生态的基石(见下);
- 生态锁定: surrounding 的备份、监控、DBA 人才、容灾方案全部围绕 Oracle 沉淀,迁移成本是整个知识体系的迁移。
2010 年后国产化的压力(《国产化改造》篇展开)开始在周边系统试水,核心库仍是最后一个动的——不是不敢,是一次切换失败的代价(清算日中断)远超 license 费。
表结构特征:流水与余额分离
核心库所有表可归为三类,设计模式非常稳定:
流水表(append-only)。委托、成交、资金变动、划转指令——全部只插入不更新。特征:
- 按交易日(或月份)范围分区,归档和删除按分区交换(exchange partition),不动数据;
- 主键含当日序列号,日终清算按分区全表扫描无压力;
- 量级:中型券商委托流水一年 5–10 亿行,保留十年在线可查(《清算数据归档》)。
append-only 的意义不只是性能:流水是审计依据,不可变。任何"修改"通过冲正流水实现(《错账处理》篇的红冲蓝补),这和会计凭证不可 UPDATE 是同一逻辑。
余额表(每账户每科目一行)。资金余额、持仓余额——每客户每币种一行、每证券每账户一行。特征:
- 行数 = 客户数 × 维度数,远小于流水表,但单行热点极端:全公司的清算备付金、客户保证金汇总行,日终批量时被所有清算任务读写;
- 更新全部走主键定位 + 行锁,
UPDATE ... WHERE acct_id = ? AND currency = ?,锁粒度必须精确到行——某段代码忘了索引导致行锁升级表锁,是批量时段最经典的事故形态。
关系与参数表。客户信息、证券信息、费率参数——量小读多,全部缓存在应用内存,变更靠参数热更新机制下发(《风控参数的热更新》的同款思路)。
存储过程:性能换可维护性
核心系统里存储过程承担两类职责,接受度完全不同:
批量清算主流程。日终几百个清算任务,每个任务一个(或一组)存储过程:取数、算、更新余额,全在库内完成。优势是真实的——数据不出库,省掉百万级行传输的网络往返,单任务从分钟级降到秒级;且每一步在单事务里,失败回滚干净。这是存储过程合理的使用场景:吞吐优先、逻辑稳定(清算规则十年不变)、DBA 和业务共同维护。
业务逻辑下沉。交易时段的风控校验、费用计算也写进存储过程——这是公认的反模式:业务规则在数据库里,改一次规则要动库对象,无法单元测试,发版要 DBA 半夜执行,回滚靠备份。2010 年代后的新核心逐步把这些逻辑外移到应用层,存储过程只留清算跑批。
判断标准一句话:逻辑变化频率高于发版频率的,不许进存储过程。
并发与热点:备付金行的串行化
批量时段的并发模型是"分片并行 + 全局串行点":按客户哈希分 N 片并行清算(《账务性能》篇),但跨片共享的行——客户保证金汇总、备付金账户——必须串行更新,否则分片事务互相死锁。实现上用一张"全局锁表":更新备付金前先 SELECT ... FOR UPDATE 锁该行的锁记录,任务间排队。代价是全局串行点的吞吐决定清算总时长,优化方向是把汇总动作从逐笔改批量(任务内先攒汇总值,结束一次性更新——牺牲实时汇总精度换吞吐,日终批量场景完全可接受)。
演进路径:读写分离与报表库
在不换掉 Oracle 的前提下,核心库的减负路线非常清晰:
核心库 (OLTP) ──T+1 同步──> 报表库 ──> 监管报送 / 经营分析 / 客户查询
│
└─── 归档库(冷数据低成本存储,十年流水在线可查)
核心库只留交易时段和批量时段必需的读写;一切分析类、查询类负载(监管报表、风控指标、客服查询)全部走 T+1 报表库,同步用 CDC 或物化视图日志。这条拆分的收益立竿见影:核心库 IO 降一半,报表 SQL 随便跑不再影响清算——《清算性能优化》篇讲的"SQL 战场",一半的解法就是别让报表 SQL 出现在核心库上。再往后才是分库分表和信创替换,但那是另一个量级的工程了。