数据模型设计基础:行情数据的实体关系
行情数据库的设计,说白了就是搞清楚「谁产生了什么数据,数据长什么样」。我做了这么多年金融IT,见过太多人一上来就建表,结果数据量一上来就崩了。今天咱们聊聊最核心的部分——实体关系与主键设计。
核心实体:股票、合约、交易所
行情数据离不开三个基本实体。你想想看,一条行情记录,必须知道「在哪个交易所」、「哪个品种」、「什么时间」产生的。这三者缺一不可。
| 实体 | 核心属性 | 说明 |
|---|---|---|
| 交易所 | 交易所代码、名称、时区 | 比如SSE、SZSE、CME |
| 股票/合约 | 标的ID、名称、类型、所属交易所 | 股票、期货、期权都算 |
| 行情快照 | 时间戳、开盘价、收盘价、成交量等 | 这是核心事实表 |
关键点:交易所和标的之间是多对一关系。一个交易所下有多个标的,但一个标的只属于一个交易所。这个关系在建模时一定要明确。
我在项目中遇到过有人把交易所代码直接塞进行情表里,结果每次查询都要做字符串匹配,性能惨不忍睹。后来我建议把交易所ID作为外键,用整数关联,查询速度直接提升了3倍。
实体关系图
下面这张图展示了三个核心实体之间的关系。我习惯用这种简洁的方式来表达,比文字直观多了。
复合主键设计:时间戳 + 标的ID
行情表的主键怎么选?我个人的习惯是坚决不用自增ID做主键。为什么?因为行情数据的查询模式决定了——你永远是按「某个标的在某个时间范围」来查的。
推荐方案:使用 (instrument_id, timestamp) 作为复合主键。
这样做的好处很明显:
- 查询效率高:按标的+时间范围查询时,直接走主键索引,不需要回表
- 数据唯一性:同一个标的在同一时刻只能有一条记录,天然防重复
- 存储紧凑:InnoDB的聚簇索引会把数据按主键顺序存储,范围扫描非常快
注意:时间戳的精度要统一。我见过有人用秒级时间戳,有人用毫秒级,结果关联查询时对不上。建议统一使用毫秒级Unix时间戳,或者直接用DATETIME(3)。
实战中的避坑指南
我曾经在一个期货行情项目中,一开始用了自增ID做主键,然后在instrument_id和timestamp上分别建了索引。结果呢?每天几千万条数据写入,索引维护开销巨大,查询还经常要回表。后来改成复合主键,写入速度提升了40%,查询延迟从200ms降到了5ms。
嗯,这里要注意另一个坑:复合主键的字段顺序。我建议把instrument_id放在前面,timestamp放在后面。因为大部分查询是先限定标的,再限定时间范围。如果你反过来,查询效率会大打折扣。
小技巧:如果数据量特别大(比如每天上亿条),可以考虑按时间分区。比如按月分区,每个分区内再用复合主键。这样历史数据归档和清理都方便。
代码示例:建表语句
下面是一个实际项目中用的建表语句,你可以参考一下:
CREATE TABLE market_snapshot (
instrument_id INT UNSIGNED NOT NULL COMMENT '标的ID',
trade_date DATE NOT NULL COMMENT '交易日期',
timestamp_ms BIGINT UNSIGNED NOT NULL COMMENT '毫秒级时间戳',
open_price DECIMAL(16,4) NOT NULL COMMENT '开盘价',
high_price DECIMAL(16,4) NOT NULL COMMENT '最高价',
low_price DECIMAL(16,4) NOT NULL COMMENT '最低价',
close_price DECIMAL(16,4) NOT NULL COMMENT '收盘价',
volume BIGINT UNSIGNED NOT NULL COMMENT '成交量',
amount DECIMAL(20,4) NOT NULL COMMENT '成交额',
PRIMARY KEY (instrument_id, trade_date, timestamp_ms),
INDEX idx_trade_date (trade_date)
) ENGINE=InnoDB
PARTITION BY RANGE (YEAR(trade_date))
SUBPARTITION BY HASH (MONTH(trade_date))
SUBPARTITIONS 12;
这个设计里,我把trade_date单独拎出来做分区键,同时作为复合主键的一部分。这样既保证了按标的+时间的高效查询,又方便做数据生命周期管理。
你想想看,如果不用分区,一张表里堆上几年的数据,光索引就比数据还大。分区后,查询只扫描对应分区的数据,性能完全不是一个量级。
好了,关于实体关系和复合主键设计,今天就聊到这儿。这些经验都是我在实际项目中一点点踩坑踩出来的,希望能帮你少走弯路。