我有一个自1998年以来1000股的1分钟数据的数据集,总数(2012-1998)*(365*24*60)*1000 = 7.3 Billion在行数左右.
大多数(99.9%)的时间我只会执行读取请求.
将此数据存储在数据库中的最佳方法是什么?
我不习惯处理这么大的数据集,所以这是我学习的绝佳机会.我将非常感谢您的帮助和建议.
编辑:
这是一个示例行:
'XX',20041208,938,43.7444,43.7541,43.735,43.7444,35116.7,1,0,0
第1列是股票代码,第2列是日期,第3列是分钟,其余是开 - 高 - 低 - 收盘价,成交量和3个整数列.
大多数查询都会像"在2012年4月12日12:15到2012年4月13日12:52之间给我AAPL的价格"
关于硬件:我打算使用Amazon RDS,所以我很灵活
And*_*zos 47
因此,数据库适用于具有不断变化的大型复杂模式的情况.您只有一个"表",其中包含一些简单的数字字段.我会这样做:
准备一个C/C++结构来保存记录格式:
struct StockPrice
{
char ticker_code[2];
double stock_price;
timespec when;
etc
};
Run Code Online (Sandbox Code Playgroud)
然后计算sizeof(StockPrice [N]),其中N是记录数.(在64位系统上)它应该只有几百个演出,适合50美元的硬盘.
然后将文件截断为该大小并将mmap(在linux上,或在Windows上使用CreateFileMapping)截断到内存中:
//pseduo-code
file = open("my.data", WRITE_ONLY);
truncate(file, sizeof(StockPrice[N]));
void* p = mmap(file, WRITE_ONLY);
Run Code Online (Sandbox Code Playgroud)
将mmaped指针转换为StockPrice*,并将数据传递填满数组.关闭mmap,现在您将数据放在一个文件中的一个大二进制数组中,以后可以再次进行mmaped.
StockPrice* stocks = (StockPrice*) p;
for (size_t i = 0; i < N; i++)
{
stocks[i] = ParseNextStock(stock_indata_file);
}
close(file);
Run Code Online (Sandbox Code Playgroud)
您现在可以从任何程序再次将其映射为只读,您的数据将随时可用:
file = open("my.data", READ_ONLY);
StockPrice* stocks = (StockPrice*) mmap(file, READ_ONLY);
// do stuff with stocks;
Run Code Online (Sandbox Code Playgroud)
所以现在你可以将它视为内存中的结构数组.您可以根据"查询"的内容创建各种索引数据结构.内核将处理透明地交换数据到磁盘或从磁盘交换数据,因此它将非常快.
如果您希望具有某种访问模式(例如连续日期),则最好按顺序对数组进行排序,以便它按顺序命中磁盘.
Cha*_*tin 27
告诉我们有关查询和您的硬件环境的信息.
只要您可以利用并行性,我会非常想使用Hadoop或类似的东西去NoSQL.
更新
好的,为什么?
首先,请注意我询问了查询.你不能 - 我们当然不能 - 在不知道工作量是什么的情况下回答这些问题.(我会共同顺带对这个即将出现的文章,但今天我不能链接.)但是,规模的问题让我想从大的旧数据库,因为移开
我对类似系统的经验表明,访问将是大顺序(计算某种时间序列分析)或非常灵活的数据挖掘(OLAP).顺序数据可以按顺序更好,更快地处理; OLAP意味着计算大量的索引,这些索引要么花费大量的时间,要么占用大量的空间.
但是,如果您正在对OLAP世界中的许多数据进行有效的大规模运行,那么以列为导向的方法可能是最好的.
如果您想进行随机查询,尤其是进行交叉比较,Hadoop系统可能会有效.为什么?因为
但事实是,在我们了解您的工作量之前,不可能说出任何明确的结论.
Dan*_*scu 27
我有一个1000分钟的1分钟数据的数据集[...]大多数(99.9%)的时间我将只执行读取请求.
存储一次并读取多次基于时间的数字数据是称为"时间序列"的用例.其他常见的时间序列是物联网中的传感器数据,服务器监控统计,应用事件等.
这个问题在2012年被提出,从那时起,一些数据库引擎一直在开发专门用于管理时间序列的功能.我使用InfluxDB获得了很好的结果,这是开源的,用Go编写的,以及MIT许可的.
InfluxDB经过专门优化,可存储和查询时间序列数据.比Cassandra更为重要,Cassandra经常被认为是存储时间序列的最佳选择:
优化时间序列涉及某些权衡.例如:
对现有数据的更新很少发生,并且永远不会发生有争议的更新.时间序列数据主要是永不更新的新数据.
Pro:限制对更新的访问可以提高查询和写入性能
Con:更新功能受到严重限制
在开源基准测试中,
在所有三个测试中,InfluxDB的性能都超过了MongoDB,写入吞吐量提高了27倍,同时使用了84倍的磁盘空间,并且在查询速度方面提供了相对相同的性能.
查询也很简单.如果您的行看起来像<symbol, timestamp, open, high, low, close, volume>,使用InfluxDB,您可以存储,然后轻松查询.比如,对于最后10分钟的数据:
SELECT open, close FROM market_data WHERE symbol = 'AAPL' AND time > '2012-04-12 12:15' AND time < '2012-04-13 12:52'
Run Code Online (Sandbox Code Playgroud)
没有ID,没有密钥,也没有连接.你可以做很多有趣的聚合.你不必表格垂直分区为PostgreSQL的,或扭曲你的模式成秒阵列与MongoDB的.此外,InfluxDB压缩得非常好,而PostgreSQL将无法对您拥有的数据类型执行任何压缩.
Jon*_*eet 15
好的,所以这有点远离其他答案,但是...我觉得如果你有一个文件系统中的数据(每个文件一个库存,也许)有一个固定的记录大小,你可以得到数据非常容易:根据特定股票和时间范围的查询,您可以寻找正确的位置,获取所需的所有数据(您将确切知道多少字节),将数据转换为您需要的格式(可以根据你的存储格式非常快,你离开了.
我对亚马逊存储一无所知,但是如果你没有像直接文件访问这样的东西,你基本上可以有blob - 你需要平衡大blob(更少的记录,但可能读取的数据超过你需要的数据)时间)小blob(更多记录提供更多开销,可能有更多请求获取它们,但每次返回的无用数据更少).
接下来你添加缓存 - 我建议给不同的服务器不同的股票来处理 - 你几乎可以从内存中提供服务.如果您能够在足够的服务器上获得足够的内存,请绕过"按需加载"部分,并在启动时加载所有文件.这样可以简化操作,但代价是启动速度较慢(这显然会影响故障转移,除非您能够为任何特定的库存提供两台服务器,这会很有帮助).
请注意,您不需要为每条记录存储股票代码,日期或分钟 - 因为它们隐含在您正在加载的文件中以及文件中的位置.您还应该考虑每个值所需的准确度,以及如何有效地存储 - 您在问题中给出了6SF,您可以存储20位.可能在64位存储中存储三个20位整数:将其读取为long(或任何64位整数值)并使用屏蔽/移位将其恢复为三个整数.当然,你需要知道使用什么规模 - 如果你不能使它保持不变,你可以用备用的4位编码.
您还没有说过其他三个整数列是什么样的,但是如果您也可以为这三个整数列取得64位,那么您可以将整个记录存储为16个字节.整个数据库只有~110GB,这真的不是很多......
编辑:另一件需要考虑的事情是,假设股票周末没有变化 - 或者确实是一夜之间.如果股票市场每天只开放8小时,每周5天,那么你每周只需要40个值而不是16个.此时你的文件中最终只能得到大约28GB的数据...听起来比你最初想的要小很多.在内存中拥有那么多数据是非常合理的.
编辑:我想我错过了为什么这种方法很适合的解释:你的大部分数据都有一个非常可预测的方面 - 股票代码,日期和时间.通过表达一次(作为文件名)的股票代码并将日期/时间完全隐含在数据的位置,您将删除大量工作.这有点像a String[]和a 之间的区别Map<Integer, String>- 知道你的数组索引总是从0开始并以1的增量向上增加到数组的长度允许快速访问和更高效的存储.