背景介绍
目前所有股票及期货的行情数据都存储在Mysql数据库里,数据引擎采用Mysql默认的InnoDB,日线数据采用单表存储,分钟及Tick数据采用按日期分表存储,每行存储单个时间切片上的相关行情数据,数据库按日更新,通常仅发生15:30后的一段固定时间内,数据库查询则主要用于量化策略的回测,可能发生在任何时间段,目前需在不改变Mysql存储架构的前提下尽量优化查询效率,因股票与期货的行情在存储结构上基本一致,故仅讨论股票的情况(查询需求通常为查询单只或多只股票在N日的日线、分钟及Tick数据,股票代码列添加了普通索引)。
1、存储介质优化
首先考虑数据体量,分析能否用内存代替硬盘存储行情数据(目前服务器总体内存约为32G)。对于日线数据,因数据量本身较小,访问速度基本在秒级甚至以内可以完成,无太大必要进行优化;对于分钟数据,其数据量虽大但内存仍可容纳(不进行压缩的情况下股票单日50M,一年可达12.5G),因此可用内存代替进行缓存;对于Tick数据,由于其数据量过大(不进行压缩的情况下股票单日2G,半年就达250G),远超出内存的承受能力,故不在此方案考虑范围内;
1.1、利用临时表/内存表替代原始表
1)Mysql临时表的优势在于同名临时表可以直接覆盖原始表,可无缝实现表的转换,并且不需要时也非常方便还原为原始表,但问题在于其仅针对创建临时表的会话可见,无法实现全局访问,因而放弃;
2)Mysql内存表可实现全局访问,但需单独建立实体表, 同时,还需调整Mysql系统变量max_heap_table_size至适当水平。
1.2、原始数据压缩
同时,为尽可能降低内存占用,可以对数据进行压缩,以目前的分钟表举例:
字段名称 | 字段类型 |
---|---|
StockCode | char(6) |
TradingTime | datetime |
OpenPrice | decimal(8,2) |
HighPrice | decimal(8,2) |
LowPrice | decimal(8,2) |
ClosePrice | decimal(8,2) |
TradeVolume | bigint(20) |
TradeAmount | bigint(20) |
1)StockCode字段可调整为SMALLINT类型,因全A股票到目前为止仍不超过4000只,远小于SMALLINT的最大值,将其由小到大分别对应不同ID,则只需再单独建立一张将股票代码与ID的映射表即可,6字节可由此压缩为2字节;
2)TradingTime字段由于股票每日仅交易240分钟,因此可同理映射为TINYINT,8字节因此压缩为1字节;
3)对价格相关字段进行浮点整形化处理,可将10字节的DECIMAL压缩为4字节的INT,若采用以日线开盘价为基准的增量压缩,可压缩为3字节的MEDIUMINT;
4)对于成交量及成交额,可采用丢失一些精度的方式将其由BIGINT压缩为INT。
2、队列缓存优化
对于数据量较大的Tick数据,由于无法直接基于Mysql自身特性对效率产生太大提升,因此,我们在行情模块中采用异步逐步读取的方式避免初始化时间过长的问题,同时也降低了内存的消耗。例如,回测程序请求查询120日的Tick数据,则我们可先加载最早的10日,由于回测程序是沿时间序列顺序访问,可根据程序当前的访问进度,逐步清空之前已访问过的历史数据并添加之后的行情数据,始终保证整体数据量始终不超过10日。
网友评论