-
Notifications
You must be signed in to change notification settings - Fork 741
MySQL存储
hikyuu是一个开源的量化投资研究框架,其MySQL存储后端为大规模金融数据的持久化提供了高效可靠的解决方案。本系统通过MySQLKDataDriver和MySQLBaseInfoDriver两个核心驱动,分别负责K线数据、分时数据、逐笔数据等时序数据的存储,以及股票基础信息、市场信息、权息信息等静态数据的管理。系统采用模块化的数据库设计,将不同频率的K线数据存储在独立的数据库模式(schema)中,并通过精心设计的索引策略和连接池机制,确保了在高并发、大数据量场景下的查询性能和数据一致性。本文档将深入解析该存储系统的实现细节,为用户配置和优化提供全面指导。
hikyuu的MySQL存储后端由两个核心C++类实现:MySQLKDataDriver和MySQLBaseInfoDriver,它们分别继承自KDataDriver和BaseInfoDriver抽象基类。
MySQLKDataDriver专门处理时序数据的读取,其核心方法getKRecordList根据查询类型(按日期或按索引)采用不同的SQL策略。对于按日期查询,它直接使用WHERE date >= start AND date < end条件进行高效过滤。对于按索引查询,为了优化性能,它会判断查询的起始位置:如果在数据集的前半部分,则使用LIMIT start, count;如果在后半部分,则先按倒序查询再反转,以避免MySQL在大偏移量下的性能退化。该驱动还实现了canParallelLoad方法返回true,表明支持并行加载,这对于快速获取多只股票的数据至关重要。
MySQLBaseInfoDriver则负责管理静态基础信息,其关键特性是使用了连接池(ConnectPool<MySQLConnect>)。与MySQLKDataDriver使用单个连接不同,MySQLBaseInfoDriver在初始化时创建一个连接池,所有数据读取操作都从池中获取连接。这不仅提高了并发性能,也增强了系统的稳定性。该驱动提供了丰富的接口,如getAllStockInfo、getStockWeightList和getFinanceInfo,通过batchLoad等批量操作方法,高效地从数据库中加载StockTable、StockWeightTable等映射表的数据。
本节来源
- MySQLKDataDriver.h
- MySQLKDataDriver.cpp
- MySQLBaseInfoDriver.h
- MySQLBaseInfoDriver.cpp
hikyuu的MySQL存储采用了分库分表的设计策略,以适应不同类型和频率的数据。
所有基础信息存储在名为hku_base的主数据库中。
-
market表:存储市场信息,如上海证券交易所(SH)和深圳证券交易所(SZ)。主键为
marketid,包含市场代码、名称、交易时间等字段。 -
stock表:存储所有股票的基础信息,如代码、名称、类型和有效状态。通过
marketid与market表关联。 - stocktypeinfo表:定义了股票类型的元数据,如A股、B股、基金等,包含最小/最大交易数量、价格精度等属性。
- stkweight表:记录股票的权息信息,如送股、转增、分红等,是进行前复权计算的关键数据源。
- coderuletype表:定义了股票代码前缀与股票类型的映射规则,用于根据代码快速判断股票类型。
K线数据根据市场和K线类型(日线、周线、5分钟线等)分布在不同的数据库模式中。
-
表名生成规则:模式名为
{market}_{ktype}(如sh_day、sz_min5),表名为股票代码(如000001)。 -
表结构:所有K线表具有统一的结构,包含
date(主键,BIGINT存储yyyyMMddHHmm格式的时间戳)、open、high、low、close、amount(成交额)和count(成交量)字段。 -
存储引擎:使用
MyISAM引擎,因其在读密集型场景下具有较好的性能,且支持全文索引(尽管此处未使用)。
-
分时数据:存储在
{market}_time模式下,表结构包含date(精确到分钟)、price(价格)和vol(成交量)。 -
逐笔数据:存储在
{market}_trans模式下,表结构包含date(精确到秒)、price、vol和buyorsell(买卖方向)。
erDiagram
market {
INT marketid PK
VARCHAR market UK
VARCHAR name
VARCHAR description
VARCHAR code
BIGINT lastDate
INT openTime1
INT closeTime1
INT openTime2
INT closeTime2
}
stocktypeinfo {
INT id PK
INT type UK
INT precision
DOUBLE tick
DOUBLE tickValue
DOUBLE minTradeNumber
DOUBLE maxTradeNumber
VARCHAR description
}
coderuletype {
INT id PK
INT marketid FK
VARCHAR codepre
INT type FK
VARCHAR description
}
stock {
INT stockid PK
INT marketid FK
VARCHAR code UK
VARCHAR name
INT type FK
INT valid
BIGINT startDate
BIGINT endDate
}
stkweight {
INT id PK
INT stockid FK
BIGINT date
DOUBLE countAsGift
DOUBLE countForSell
DOUBLE priceForSell
DOUBLE bonus
DOUBLE countOfIncreasement
DOUBLE totalCount
DOUBLE freeCount
BOOLEAN suogu
}
market ||--o{ stock : "包含"
market ||--o{ coderuletype : "包含"
stocktypeinfo ||--o{ stock : "定义类型"
stocktypeinfo ||--o{ coderuletype : "定义类型"
stock ||--o{ stkweight : "拥有"
class sh_day {
BIGINT date PK
DOUBLE open
DOUBLE high
DOUBLE low
DOUBLE close
DOUBLE amount
DOUBLE count
}
class sz_min5 {
BIGINT date PK
DOUBLE open
DOUBLE high
DOUBLE low
DOUBLE close
DOUBLE amount
DOUBLE count
}
note as "K线数据模式"
每个市场和K线类型组合
独立的数据库模式
end note
market ||--o{ "K线数据模式" : "生成"
图示来源
- createdb.sql
- MySQLKDataDriver.cpp
hikyuu提供了一套完整的SQL脚本来初始化和升级数据库。
位于hikyuu/data/mysql_upgrade/createdb.sql,该脚本负责创建整个数据库系统的骨架。
-
创建主数据库:
CREATE SCHEMA hku_base; -
创建基础表:定义
market、stock、stocktypeinfo、stkweight、stock和coderuletype等表的结构。 -
插入初始数据:为
market表插入上交所和深交所的信息,为stocktypeinfo和coderuletype表插入各类股票的元数据和代码规则。
位于hikyuu/data/mysql_upgrade/目录下,这些脚本以数字前缀命名,确保按顺序执行。
-
0001.sql:创建
hku_base.version表并插入初始版本号1,用于跟踪数据库版本。 -
0002.sql:将
stocktypeinfo表中的minTradeNumber和maxTradeNumber字段从INT改为DOUBLE,以支持更灵活的交易单位。 -
0003.sql:为
market表添加openTime1,closeTime1,openTime2,closeTime2四个字段,用于存储市场的开盘和收盘时间,并为上交所和深交所设置默认值。
使用方法:
- 确保MySQL服务已启动。
- 使用
mysql -u root -p < createdb.sql命令执行初始化脚本。 - 系统在启动时会自动检测当前数据库版本,并按顺序执行所有高于当前版本的升级脚本(通过
common_mysql.py中的create_database函数实现)。
本节来源
- createdb.sql
- 0001.sql
- 0002.sql
- 0003.sql
- common_mysql.py
hikyuu通过连接池技术显著提升了数据访问的性能和可靠性。
MySQLBaseInfoDriver在_init方法中创建了一个ConnectPool<MySQLConnect>实例。该连接池的构造函数接受连接参数(主机、端口、用户名、密码、数据库名)以及两个关键配置:
-
maxConnect:允许的最大连接数。默认为0,表示不限制,但在生产环境中应根据MySQL服务器的
max_connections参数进行设置,避免资源耗尽。 - maxIdleConnect:允许的最大空闲连接数,默认为100。当连接被归还到池中时,如果空闲连接数超过此值,多余的连接将被关闭,以释放资源。
连接池通过getConnect()方法提供连接。该方法是线程安全的,它首先尝试从空闲队列中获取连接,如果队列为空,则创建一个新连接。当连接的shared_ptr被销毁时,其自定义删除器(ConnectCloser)会自动将连接归还到池中,而不是直接关闭,从而实现了连接的复用。
-
批量操作:
MySQLBaseInfoDriver大量使用batchLoad方法,通过一次查询加载整个表或满足条件的所有记录,减少了网络往返次数。 -
智能索引查询:
MySQLKDataDriver在按索引查询时,会根据索引位置选择正序或倒序查询,避免了LIMIT large_offset, count带来的性能问题。 - MyISAM引擎:对于以读取为主的K线数据,选择MyISAM而非InnoDB,减少了事务和行锁的开销,提高了查询速度。
-
主键优化:K线表以
date作为主键,利用了时间序列数据的有序性,使得范围查询非常高效。
sequenceDiagram
participant Application as 应用程序
participant Driver as MySQLBaseInfoDriver
participant Pool as ConnectPool
participant MySQL as MySQL服务器
Application->>Driver : getAllStockInfo()
Driver->>Pool : getConnect()
alt 连接池中有空闲连接
Pool-->>Driver : 返回空闲连接
else 连接池为空
Pool->>MySQL : 创建新连接
MySQL-->>Pool : 连接成功
Pool-->>Driver : 返回新连接
end
Driver->>MySQL : 执行SELECT * FROM stock
MySQL-->>Driver : 返回所有股票数据
Driver->>Pool : 连接对象销毁
Pool->>Pool : 将连接放回空闲队列
Driver-->>Application : 返回股票列表
图示来源
- MySQLBaseInfoDriver.cpp
- ConnectPool.h
本节来源
- MySQLBaseInfoDriver.cpp
- ConnectPool.h
用户需在配置文件中为MySQL驱动指定连接参数。主要参数包括:
-
host:MySQL服务器地址,默认为
127.0.0.1。 -
port:端口号,默认为
3306。 -
usr:用户名,默认为
root。 - pwd:密码,默认为空。
-
db:数据库名,对于
MySQLBaseInfoDriver,必须设置为hku_base。
hikyuu提供了多个Python脚本用于数据导入,如pytdx_to_mysql.py和em_block_to_mysql.py。这些脚本通常遵循以下流程:
- 连接到MySQL。
- 调用
common_mysql.create_database函数,确保数据库和表结构是最新的。 - 从源(如同花顺、通达信)获取原始数据。
- 将数据转换为hikyuu内部格式。
- 使用
INSERT INTO ... VALUES ...或INSERT INTO ... SELECT ...语句批量写入对应的K线表或基础信息表。
-
利用索引:始终使用
date字段进行范围查询,这是最高效的。 -
避免全表扫描:不要在没有
WHERE条件的情况下查询大表。 -
合理使用连接池:在多线程环境中,
MySQLBaseInfoDriver的连接池能有效管理连接,避免频繁创建和销毁。 -
定期维护:对MyISAM表执行
OPTIMIZE TABLE可以整理碎片,提高性能。
hikyuu的MySQL存储后端是一个设计精良、性能优越的系统。通过MySQLKDataDriver和MySQLBaseInfoDriver的分工协作,实现了时序数据与静态数据的高效管理。其分库分表的架构、基于连接池的并发控制以及针对金融数据特点的SQL优化策略,共同保证了系统在处理海量历史数据时的稳定性和响应速度。用户通过正确配置连接参数并遵循最佳实践,可以充分发挥该存储系统的潜力,为量化研究提供坚实的数据基础。