如何存储73亿行市场数据(优化待读)?

时间:2012-03-22 01:26:57

标签: database

我有一个自1998年以来1000股的1分钟数据的数据集,总共约(2012-1998)*(365*24*60)*1000 = 7.3 Billion行。

大部分时间(99.9%)我只会执行读取请求。

将此数据存储在数据库中的最佳方法是什么?

  • 一张7.3B行的大表?
  • 1000个表(每个股票代码一个),每个7.3M行?
  • 数据库引擎的任何推荐? (我打算使用Amazon RDS的MySQL)

我不习惯处理这么大的数据集,所以这是我学习的绝佳机会。我将非常感谢你的帮助和建议。

编辑:

这是一个示例行:

  

'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,因此我对此非常灵活

13 个答案:

答案 0 :(得分:46)

因此,数据库适用于具有不断变化的大型复杂模式的情况。您只有一个“表”,其中包含一些简单的数字字段。我会这样做:

准备一个C / C ++结构来保存记录格式:

struct StockPrice
{
    char ticker_code[2];
    double stock_price;
    timespec when;
    etc
};

然后计算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);

将mmaped指针强制转换为StockPrice *,然后传递数据填充数组。关闭mmap,现在您将数据放在一个文件中的一个大二进制数组中,以后可以再次进行mmaped。

StockPrice* stocks = (StockPrice*) p;
for (size_t i = 0; i < N; i++)
{
    stocks[i] = ParseNextStock(stock_indata_file);
}
close(file);

您现在可以从任何程序再次将其映射为只读,并且您的数据将随时可用:

file = open("my.data", READ_ONLY);
StockPrice* stocks = (StockPrice*) mmap(file, READ_ONLY);

// do stuff with stocks;

所以现在你可以将它视为内存中的结构数组。您可以根据“查询”的内容创建各种索引数据结构。内核将处理透明地将数据交换到磁盘或从磁盘交换,因此它将非常快。

如果您希望拥有某种访问模式(例如连续日期),最好按顺序对数组进行排序,以便按顺序命中磁盘。

答案 1 :(得分:27)

  

我有一个1分钟数据的数据集,其中1000个股票的最多(99.9%)时间我只会执行读取请求。

存储一次并且读取多次基于时间的数值数据是称为“时间序列”的用例。其他常见的时间序列是物联网中的传感器数据,服务器监控统计,应用事件等。

这个问题在2012年被提出,从那时起,一些数据库引擎一直在开发专门用于管理时间序列的功能。我用InfluxDB得到了很好的结果,这是开源的,用Go编写的,以及MIT许可的。

InfluxDB经过专门优化,可存储和查询时间序列数据。 Much more so than Cassandra,这通常被认为是存储时间序列的好方法:

InfluxDB vs Cassandra query speed

优化时间序列涉及某些权衡。例如:

  

现有数据的更新很少发生,并且永远不会发生有争议的更新。时间序列数据主要是永不更新的新数据。

     

Pro:限制对更新的访问可以提高查询和写入性能

     

Con:更新功能受到严重限制

open sourced benchmarks

  

在所有三个测试中,InfluxDB的性能都超过MongoDB,写入吞吐量提高了27倍,同时使用了84倍的磁盘空间,并且在查询速度方面提供了相对相同的性能。

InfluxDB vs. MongoDB on-disk storage requirements and compression

查询也很简单。如果您的行看起来像<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'

没有ID,没有密钥,也没有连接。你可以做很多interesting aggregations。您没有vertically partition the table as with PostgreSQLcontort your schema into arrays of seconds as with MongoDB。此外,InfluxDB压缩得非常好,而PostgreSQL won't be able to perform any compression on the type of data you have

答案 2 :(得分:26)

告诉我们有关查询和您的硬件环境的信息。

只要您可以利用并行性,我会非常非常想使用NoSQL或类似的东西去Hadoop

<强>更新

好的,为什么?

首先,请注意我询问了查询。你不能 - 我们当然不能 - 在不知道工作量是什么的情况下回答这些问题。 (我偶然会有一篇关于这个的文章很快出现,但我今天无法将它链接起来。)但问题的规模让我想到要远离Big Old Database,因为

  • 我对类似系统的经验表明,访问将是大顺序(计算某种时间序列分析)或非常灵活的数据挖掘(OLAP)。顺序数据可以按顺序更好,更快地处理; OLAP意味着计算大量的索引,这将占用大量的时间或大量的空间。

  • 但是,如果您正在对OLAP世界中的许多数据进行有效的大规模运行,那么以列为导向的方法可能是最好的。

  • 如果您想进行随机查询,尤其是进行交叉比较,Hadoop系统可能会有效。为什么?因为

    • 您可以在相对较小的商品硬件上更好地利用并行性。
    • 您还可以更好地实现高可靠性和冗余
    • 其中许多问题很自然地适用于MapReduce范例。

但事实是,在我们了解您的工作量之前,不可能说出任何确定的内容。

答案 3 :(得分:15)

好的,所以这有点远离其他答案,但是...我觉得如果你有一个文件系统中的数据(每个文件一个库存,也许)有一个固定的记录大小,你可以得到在数据真的很容易:给定一个特定股票和时间范围的查询,你可以找到正确的位置,获取你需要的所有数据(你将确切知道多少字节),转换将数据转换为您需要的格式(根据您的存储格式,这可能非常快),您就不在了。

我对亚马逊存储一无所知,但如果你没有像直接文件访问这样的东西,你基本上可以有blob - 你需要平衡大blob(记录更少,但可能读取更多数据)你需要每次使用小blob(更多的记录会产生更多的开销,可能会有更多的请求来获取它们,但每次都会返回更少的无用数据)。

接下来你添加缓存 - 我建议给不同的服务器不同的股票来处理 - 你几乎可以从内存中提供服务。如果您能够在足够的服务器上获得足够的内存,请绕过“按需加载”部分,并在启动时加载所有文件。这样可以简化操作,但代价是启动速度较慢(这显然会影响故障转移,除非您能够为任何特定的库存提供两个服务器,这将有所帮助)。

请注意,您不需要存储每个记录的股票代码,日期或分钟 - 因为它们隐含在您正在加载的文件中以及文件中的位置。您还应该考虑每个值所需的准确度,以及如何有效地存储 - 您在问题中给出了6SF,您可以存储20位。可能在64位存储中存储三个20位整数:将其读作long(或任何64位整数值)并使用屏蔽/移位将其恢复为三个整数。当然,您需要知道使用什么规模 - 如果不能使其保持不变,您可以用备用的4位进行编码。

你还没有说过其他三个整数列是什么样的,但是如果你也可以为这三个整数列取得64位,你可以用16个字节存储整个记录。整个数据库只有~110GB,这真的不是很多......

编辑:另一件需要考虑的事情是,可能是股票在周末没有变化 - 或者确实是一夜之间。如果股票市场每天只开放8小时,每周5天,那么你每周只需要40个值而不是16个。此时你的文件中最终只能得到大约28GB的数据...听起来比你最初想的要小很多。在内存中拥有那么多数据非常合理。

编辑:我想我错过了为什么这种方法非常适合的解释:你的大部分数据都有一个非常可预测的方面 - 股票代码, 日期和时间。通过表示一次(作为文件名)并将日期/时间完全隐含在数据的位置中,您将删除大量工作。这有点像String[]Map<Integer, String>之间的区别 - 知道你的数组索引总是从0开始并以1的增量向上移动到数组的长度允许快速访问等等高效存储。

答案 4 :(得分:14)

据我了解,HDF5专门设计了库存数据的时间序列存储作为一个潜在的应用程序。研究员已证明HDF5适用于大量数据:chromosomesphysics

答案 5 :(得分:4)

尝试在Microsoft SQL Server 2012数据库之上创建一个市场数据服务器,这应该适用于OLAP分析,这是一个免费的开源项目:

http://github.com/kriasoft/market-data

答案 6 :(得分:4)

首先,一年中没有365个交易日,假期52周末(104)=说250 x实际营业时间像有人说的那样开市,并且使用该符号作为主键不是一个好主意,因为符号改变,使用带符号(char)的k_equity_id(数字),因为符号可以像这个A或GAC-DB-B.TO,然后在价格信息的数据表中,你有,所以你的由于14年来每个符号只有约170万行,估计有73亿美元的数量远远超过计算。

k_equity_id k_date k_minute

和EOD表(将比其他数据查看1000倍)

k_equity_id k_date

其次,不要将OHLC按分钟数据存储在与EOD表相同的数据库表中(一天结束),因为任何想要在一年内查看pnf或折线图的人都没有兴趣在分钟信息。

答案 7 :(得分:3)

我建议你看看apache solr,我认为这对你的特定问题是理想的。基本上,您首先索引数据(每行都是“文档”)。 Solr针对搜索进行了优化,并在日期上本地支持范围查询。您的名义查询,

"Give me the prices of AAPL between April 12 2012 12:15 and April 13 2012 12:52"

会转换为:

?q=stock:AAPL AND date:[2012-04-12T12:15:00Z TO 2012-04-13T12:52:00Z]

假设“stock”是股票名称,“date”是从索引输入数据的“date”和“minute”列创建的“DateField”。 Solr非常灵活,我真的不能说出足够好的东西。因此,例如,如果您需要维护原始数据中的字段,您可能会找到一种方法来动态创建“DateField”作为查询(或过滤器)的一部分。

答案 8 :(得分:3)

您应该将慢速解决方案与内存模型中的简单优化进行比较。未压缩它适用于256 GB RAM服务器。快照适合32 K,您只需在日期时间和库存上进行位置索引。然后你可以创建专门的快照,因为打开一个通常等于关闭前一个。

[edit]为什么你认为使用数据库(rdbms或nosql)是有意义的?这些数据不会改变,它适合内存。这不是dbms可以增加值的用例。

答案 9 :(得分:2)

我认为任何主要的RDBMS都会处理这个问题。在原子级别,一个具有正确分区的表似乎是合理的(如果修复了,则根据您的数据使用情况进行分区 - 这可能是符号或日期)。

您还可以研究构建聚合表,以便在原子级别之上更快地访问。例如,如果您的数据是在某天,但您经常在wekk甚至月份级别获取数据,那么可以在聚合表中预先计算。在某些数据库中,这可以通过缓存视图完成(不同数据库解决方案的各种名称 - 但基本上是原子数据的视图,但一旦运行,视图就会被缓存/加固到固定临时表中 - 查询子顺序匹配查询。可以间隔删除它以释放内存/磁盘空间。)

我想我们可以帮助您更多地了解数据使用情况。

答案 10 :(得分:2)

如果您有硬件,我建议MySQL Cluster。您可以获得熟悉的MySQL / RDBMS接口,并获得快速并行写入。由于网络延迟,读取速度将比常规MySQL慢,但由于MySQL Cluster和NDB存储引擎的工作方式,您可以实现查询和读取的并行化。

确保你有足够的MySQL群集机器和足够的内存/ RAM用于每一个 - MySQL Cluster是一个面向内存的数据库架构。

Redis,如果你不介意读/写的键值/ NoSQL接口。确保Redis有足够的内存 - 读写速度超快,你可以用它进行基本查询(非RDBMS),但也是一个内存数据库。

正如其他人所说,了解更多有关您将要运行的查询的信息会有所帮助。

答案 11 :(得分:2)

您需要存储在columnar table / database中的数据。像Vertica和Greenplum这样的数据库系统是列式数据库,我相信SQL Server现在允许使用列式表。这些对于SELECT非常大的数据集非常有效。它们也可以有效地导入大型数据集。

免费的柱状数据库是MonetDB

答案 12 :(得分:1)

如果您的用例是简单读取行而没有聚合,则可以使用Aerospike集群。它在内存数据库中支持文​​件系统以实现持久性。它也是SSD优化的。

如果您的用例需要汇总数据,请转到具有日期范围分片的Mongo数据库群集。你可以在分片中加入年度vise数据。