上一篇讲了三方存管为什么让客户资金不进券商自己的账,这篇落到表结构上。新人看券商系统的库表,第一个困惑往往是:为什么"客户"和"资金账户"是两张表?为什么同一个客户会冒出好几个"账号"?答案是券商系统里存的不是一个账户,而是三种不同主体视角下的三套身份:客户在券商这里的身份(客户号)、客户的钱(资金账号)、客户在中登的持仓身份(股东代码)。三层分开建模不是历史包袱,是业务关系的真实形状。
三层各自是什么
客户(Client):自然人在券商的客户档案。存证件类型与号码、姓名、联系方式、风险测评等级、适当性标签、职业信息这些"人"的属性。客户号是券商内部对一个人的唯一编号,开户时分配,终身不变(销户重开也会尽量沿用历史客户号,方便稽核查轨迹)。
资金账户(Fund Account):客户的"钱包",券商内部账本的记账单元。每笔资金变动(冻结、扣款、利息、红利入账)都挂在资金账号上。资金账户是券商自建的实体,中登和银行都不知道它的存在——银行只知道"券商客户保证金汇总账户"这个法人账户,券商负责在内部把它分摊到每个客户的资金账户,这就是上一篇说的"券商台账"。
证券账户(Securities Account):中登(中国结算)体系里的持仓身份,对外表现为股东代码——沪市一个 10 位 A 开头代码,深市一个 10 位数字代码。证券账户不是券商的实体,是登记公司的实体;券商只是"托管"它下面的持仓。客户下单时必须指定用哪个股东代码,因为成交回报和清算都是按股东代码来的。
三层之间的基数关系要注意:一个客户可以有多个资金账户(普通户、信用户、期权户、衍生品户各一本账,多币种时再按币种拆),一个资金账户下可以挂多个证券账户(沪 A、深 A 至少各一个,加上封闭式基金、场内基金账户)。开户流程的本质就是建这三条记录并把它们绑起来。
一码通改变了什么
2014 年之前,沪深股东账户各管各的,一个人可以在多家券商开多户(一人最多 20 户的时代)。2014 年 10 月中登推出一码通:一个 12 位的总账户号,把同一个人名下的所有证券子账户(沪 A、深 A、基金、B 股……)挂到一棵树上;2016 年收紧为一人三户——一个一码通下最多三个沪 A 账户。
对表结构的影响有两处。一是客户表要加一码通字段,而且这个字段的权威来源是中登,券商建档时要向中登实时接口查询/注册,不是自己编。二是销户和休眠治理变成了跨券商的事:中登按一码通统计全市场的账户数,券商侧的"客户号"无法表达这个口径,监管报送的账户类报表基本都要过一码通映射。建模时建议客户表存一码通号,证券账户表同时存一码通号和股东代码,用一码通做客户身份归集的"外部锚点"。
表设计的几个关键决策
主键用代理键还是业务键? 资金账号和股东代码本身就是全局唯一的业务标识,可以直接做主键;客户号也是。实务上核心表通常再加一个无意义的内部 id 做物理主键,业务键建唯一索引——原因是历史系统迁移、并券商(合并重组时两套客户号体系并存)时业务键会撞车,留一个内部键有腾挪空间。但对外接口(柜面、报盘、对账文件)永远用业务键。
资金账户表放不放余额? 放。尽管复式记账要求"余额可由流水推导"(复式记账那篇展开),但交易路径上每一次下单都要查可用资金,从流水现算是不现实的。标准做法是余额作为缓存列存在资金账户表上,由事务内的流水写入同步维护,日终再用"期初余额 + 当日发生额 = 期末余额"做全量校验。缓存列和账本的偏离是对账要抓的第一类差错。
状态字段怎么设计? 账户状态不是单个 status 字段能扛的:正常/休眠/销户是生命周期状态;限制买入/限制卖出/限制取款是交易控制状态;司法冻结是法务状态。建议拆成独立标志位或状态组,因为组合是真实存在的——一个休眠户可以同时被司法冻结。每个状态组要有自己的变更审计流水,"谁、什么时候、为什么把账户限制了"是监管检查的高频问题。
适当性字段放哪层? 风险测评结果(C1~C5)是人的属性,放客户表;但创业板、科创板、北交所、可转债这些权限是按股东代码开通的交易权限,放证券账户表或独立的权限表。混淆这两层是常见 bug 源:客户风险等级够了不代表股东代码开了权限,下单校验要查的是后者。
查询路径与索引
交易时段的热查询就三类:按资金账号取余额(下单校验)、按股东代码取持仓(卖出校验)、按客户号拉名下所有账户(柜面视图)。前两个是主键查询,没有问题;第三个要求资金账户表和证券账户表都在客户号上有索引。批量清算按资金账号分片扫描(账务性能那篇讲分片),所以资金账号本身要设计得散列友好——连续递增的账号会让哈希分片不均,行业里常见做法是在账号里嵌入营业部代码,按账号区间天然分片。
还有一条容易忽视的:证件号码的索引和加密。按证件号查客户是开户查重和反洗钱名单筛查的热路径,但证件号属于敏感个人信息,落库要加密。加密了就没法直接建索引,常见解法是额外存一列证件号的哈希(加盐)用于等值查询,明文字段加密存储。监管调取时再走解密流程并留痕。
一张容易漏的表:关联关系表
三层之外,生产库通常还有一张账户关联表(fund_acct × secu_acct × 权限 × 席位)。因为"哪个资金账户为哪笔委托出钱、用哪个股东代码报盘、走哪个交易席位"不是三层表各自能表达的,是一个三元组关系。极速柜台和普通柜台并存时,同一个资金账户可能有两个通道的席位映射,路由就靠这张表。设计上把它当配置表管理(有生效日期、有审批流水),不要做成隐式约定——"系统 A 默认用席位 X"这种约定在切换通道时会咬人。
三层模型看起来朴素,但券商系统演进史上的大坑——银证通时代的客户混同、一人多户时代的休眠户堆积、一码通切换时的身份归集——全都是这三层关系没建干净留下的债。表结构定型的机会不多,开户量上来之后每一次结构调整都是伤筋动骨的数据迁移,值得在设计期多花些时间。