金融行情-K线存储设计
起源
最近在设计行情服务的数据存储(快照、K线、ticks)全量上市到当前的数据,使用了redis存储最近的K线,mysql存储历史所有K线、influxBD存储ticks,redis存储快照信息,通过实际请求发现,获取数据速度并没有达到预期,通过检查发现只要是在mysql中获取的历史K线就慢了一个数量级。
恰巧在最近看了Taobao FileSystem,思考是否可以将行情所产生的数单做小文件存储呢,热点数据映射到文件中,非热点数据直接写入到磁盘中,扩展品种的化只需要将映射文件resize一下,和在目录下添加指定品种的文件就好了。
目录结构
## 历史数据目录结构
|-- quote_histroy_data
|-- day
| |--000001.day
|-- h1
| |--000001.h1
| |--000001.index
|-- h4
| |--000001.h4
| |--000001.index
|-- m1
| |--000001.m1
| |--000001.index
|-- m5
| |--000001.m5
| |--000001.index
|-- ticks
| |--000001.ticks
| |--000001.index
|-- week
| |--000001.week
|-- year
| |--000001.year
## 热点数据目录结构
|-- quote_hot_data
|--day.map
|--h1.map
|--h4.map
|--m1.map
|--m5.map
|--week.map
|--year.map
|--snapshot.map
设计原因:每个品种在同一天下所产生的K线数据是固定
数据结构
单根K线数据结构:
struct kline{
uint64_t timestamp;
uint64_t open;
uint64_t heigh;
uint64_t low;
uint64_t close;
uint64_t volume;
}
// sizeof(kline) = 48字节
根据沪深市场举例:每天交易时间为4小时,假设现有品种为8000个,每天产生的数据大小如下:
m1 :8000 * 4 * 60 * 48 = 737.28(MB)
m5 :8000 * 4 * 12 * 48 = 147.456(MB)
h1 :8000 * 4 * 48 = 12.288(MB)
h4 :8000 * 48 = 3.072(MB)
day :8000 * 48 = 3.072(MB)
根据上面的梳理,map的结构就可以定义成这样子:
# m1 周期
| head | kline * 4 * 60 |
| head | kline * 4 * 60 |
...
| head | kline * 4 * 60 |
# m5 周期
| head | kline * 4 * 12 |
| head | kline * 4 * 12 |
...
| head | kline * 4 * 12 |
由于不活跃的品种有可能一分钟都不来一个ticks,那么生成的K线自然有快有慢,基于这个原因,我们得用一个head来表示当前品种生成到第几根K线。
head结构如下:
struct head{
uint16_t count; // 当前已生成根数
uint16_t total; // 当前品种在该周期中K线总数
}
通过上面两个结构体就可以将一个周期中所有的K线记录下来,在通过map做一个映射
struct klineindex {
head* h1;
head* h4;
head* m1;
head* m5;
head* day;
head* week;
head* year;
}
std::map<std::string, klineindex> m_klineIndex;