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


84

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

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

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

  • 1个有7.3B行的大表?
  • 1000张表(每个股票代号一个),每个表有730万行?
  • 对数据库引擎有什么建议吗?(我打算使用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,因此在此方面我很灵活


5
描述预期的典型查询
William Pursell 2012年

10
“我认为您应该使用MongoDB,因为它是Web规模的。”
ta.speot。是2012年

8
您可能想要一张大桌子,按股票代码划分。
ta.speot。是2012年

1
数据集巨大!您可能需要四处搜索数据挖掘和分析以查看发现的内容。
Mike Purcell 2012年

2
仅仅具有一个表的“标准RDBMS”不足以解决这个问题吗?(我只交易数百万,但“为我工作”。也可以尝试一下,看看。请记住根据需要对索引/集群/分区进行索引。)

Answers:


30

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

只要您可以利用并行性,我就会非常想使用Hadoop或类似的工具来使用NoSQL

更新资料

好吧为什么

首先,请注意我询问了有关查询。您不能-并且我们当然不能-在不知道工作量如何的情况下回答这些问题。(我会共同顺带对这个即将出现的文章,但今天我不能链接。)但是,规模的问题让我想从大的旧数据库,因为走开

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

  • 但是,如果您要进行的工作实际上是在OLAP世界中处理大量数据,那么最好使用面向列的方法。

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

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

但是事实是,除非我们了解您的工作量,否则无法说出确定的任何内容。


7
“ NoSQL”在这里有什么优势?为什么不使用传统RDBMS中单个大表?(具有正确的索引等)每个人都使用“ NoSQL”,“ NoSQL”,“ NoSQL”,但是……为什么

5
我不得不说,我的建议也是使用Apache Accumulo的NoSQL方法(这是个人喜好)。数据集较小(对于Accumulo),并且所需的查询类型似乎非常适合使用其分布式迭代器堆栈进行。
Binary Nerd

感谢您的扩展答案。我可以+1。

1
有时这里的一些评论只会使我感到困惑。-1在没有意义的地方使用数据库?整个答案都反对传统的数据库。
查理·马丁

51

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

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

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

然后计算sizeof(StockPrice [N]),其中N是记录数。(在64位系统上)应该只有几百个演出,并且适合50美元的HDD。

然后将文件截短至该大小并进行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,现在您将数据保存在一个大二进制数组中的文件中,以后可以再次进行mmap。

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;

因此,现在您可以将其当作内存中的结构数组对待。您可以根据“查询”的内容来创建各种索引数据结构。内核将透明地交换数据到磁盘或从磁盘交换数据,因此会非常快。

如果希望具有某种访问模式(例如,连续日期),则最好按该顺序对阵列​​进行排序,这样它将按顺序命中磁盘。


11
花几百美元将其放在SSD而不是硬盘上。随机读取大约快一百倍。或在公羊上花费10K。再快一百倍
Stephan Eggermont

1
@Andrew Tomazos感谢老兄,这是“答案”
Pavneet_Singh

1
StockPrice sizeof将为char [4] = 4个字节int = 4个字节short = 2个字节float = 4个字节float = 4个字节float = 4个字节float = 4个字节float = 4个字节int = 4个字节int = 4个字节int = 4字节------------ 42个字节,约3066亿字节=〜285.5435013771057 GB内存...祝你好运
ZagNut 2016年

3
@ZagNut:如果您的意思是需要300GB的物理内存,那是不正确的-mmap不会将整个内容复制到内存中,而是根据需要将其分页移入/移出(与交换文件的方式相同) 。
Andrew Tomazos

33

我有1000分钟的大部分时间(99.9%)的一分钟数据集,我将只执行读取请求。

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

在2012年提出了这个问题,此后,多个数据库引擎一直在开发专门用于管理时间序列的功能。我使用InfluxDB取得了不错的效果,它是开源的,用Go编写并获得MIT许可的。

InfluxDB经过专门优化,可以存储和查询时间序列数据。比Cassandra更为重要,后者经常被吹捧为存储时间序列:

InfluxDB vs Cassandra查询速度

时间序列的优化涉及某些折衷。例如:

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

优点:限制访问更新可提高查询和写入性能

缺点:更新功能受到严重限制

开源基准测试中

在所有三个测试中,InfluxDB的性能均优于MongoDB,写入吞吐量提高了27倍,而磁盘空间却减少了84倍,并且在查询速度方面提供了相对相等的性能。

InfluxDB与MongoDB磁盘存储要求和压缩

查询也很简单。如果您的行看起来像<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,没有键,也没有要建立的联接。您可以进行很多有趣的聚合。你不必表格垂直分区为PostgreSQL的,或扭曲你的模式成秒阵列与MongoDB的。另外,InfluxDB的压缩效果非常好,而PostgreSQL将无法对您拥有的数据类型执行任何压缩


17

好的,所以这与其他答案略有不同,但是...对我来说,如果您将数据存储在具有固定记录大小的文件系统中(也许每个文件有一个库存),则可以获取数据非常容易:给定特定库存和时间范围的查询,您可以找到合适的位置,获取所需的所有数据(确切知道多少字节),将数据转换为所需的格式(可以会很快(具体取决于您的存储格式),然后您就离开了。

我对Amazon存储一无所知,但是如果您没有直接文件访问之类的东西,那么您基本上可以拥有blob-您需要平衡大的blob(更少的记录,但读取的数据可能比每个所需的数据都要多时间较小)(记录越多,开销越大,可能需要更多的请求,但每次返回的无用数据更少)。

接下来,您添加缓存-我建议例如为不同的服务器提供不同的库存以进行处理-您几乎可以从内存中进行服务。如果您可以在足够多的服务器上负担足够的内存,请跳过“按需加载”部分,而仅在启动时加载所有文件。这将简化事情,但以较慢的启动为代价(这显然会影响故障转移,除非您可以为任何特定的库存始终拥有两台服务器,这将有所帮助)。

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

您没有说其他三个整数列是什么样子,但是如果您也能摆脱这三个整数列的64位,则可以将整个记录存储在16个字节中。整个数据库只有〜110GB,这并不是很多...

编辑:要考虑的另一件事是,大概在周末或实际上在一夜之间股票没有变化。如果股市每天仅开放8小时,每周开放5天,那么您每周只需要40个值,而不是168个。那时,您的文件中最终可能只有大约28GB的数据...听起来比您最初想的要小得多。在内存中存储这么多数据是非常合理的。

编辑:我想我已经错过了为什么这种方法很合适的解释:您对数据的很大一部分(股票行情,日期和时间)有非常可预测的方面。通过表达的股票一度(作为文件名)和离开的日期/时间处于全隐位置数据的,要删除的是一大堆的工作。这有点像aString[]和a之间的区别Map<Integer, String>-知道您的数组索引始终从0开始,以1的增量递增,直到数组的长度允许快速访问和更有效的存储。


同样,这取决于他如何使用数据。如果他的查询是全面提取特定数据(明智的股票代号),那么这将导致读取每个文件并具有特定的日期编码以从每个文件中提取正确的数据。或者,如果他想要每周表现最好的股票,那将是一场噩梦,因为这种设置不得不读取所有记录进行排序和比较。没有这些信息,我们只能猜测这是用于固定存储的-可能是作为批量DW,它将在某个时候馈送报告DW(ETL源)。
Wolf5370'3

2
@ Wolf5370:是的,我们当然需要知道查询的内容,但是从问题中我们至少可以看到一些提示:“大多数查询都类似于“给我2012年4月12日12:15至2012年4月13日12:52'这将是很好知道的东西。其他的查询会,以及相对的频率和性能要求。
乔恩斯基特

@JonSkeet实际上取决于工作量,但是我对这种系统有一定的专业知识,而且很少会“仅选择一个范围内的一只股票”:更多的是“选择此范围内该组合的股票,计算β,然后尝试此可能的库存列表,然后查看β是什么。” 这就是为什么它会带您走向类似于OLAP的原因。
查理·马丁

2
@CharlieMartin:好吧,我只是按照问题的意思去做。但是,如果您基本上可以将其全部存储在内存中(通过几台服务器),那么这仍然很容易-向每台服务器询问投资组合中的相关股票,然后将结果汇总在一起。我认为,使用数据的已知方面(每分钟一次,而不是周末或整夜)的观点仍然很有用,因为它可以大大降低将所有数据存储到内存中的难度。
乔恩·斯基特

这次讨论使我想起了弗雷德·布鲁克斯(Fred Brooks)的名言:“代表是编程的本质”以及本特利的“编程珍珠”中的相关问题。
CS

14

据我了解,HDF5是专门针对股票数据的时间序列存储而设计的,这是一种潜在的应用。资深的堆垛机已证明HDF5可处理大量数据:染色体物理学


2
+1为特定解决方案。但是,我确实喜欢SQL DQL(在大多数情况下)及其提供的灵活性...不确定使用HDF5退出“分层视图”需要什么。

4

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

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


是的 不确定该特定项目是否适用,但是肯定会建议OP考虑使用OLAP或数据仓库事实表结构,这两种方法(有时一起使用)都旨在解决这种具有大量行的数据。这实际上取决于他们打算执行哪种分析。
AaronLS 2014年

4

首先,一年中没有365个交易日,假期52个周末(104)= 250 x如某人所说的开放一天的实际小时数,并且使用该符号作为主键不是一个好主意由于符号发生变化,因此请使用带有符号(char)的k_equity_id(数字),因为符号可以像这样A或GAC-DB-B.TO,然后在价格信息数据表中,您拥有7.3的估算值由于14年内每个符号只有170万行,因此大大超过了10亿。

k_equity_id k_date k_minute

对于EOD表(将被视为其他数据的1000倍)

k_equity_id k_date

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


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”作为查询(或过滤器)一部分的方法。


您还可以使用Amazon EC2上设置您的Solr实例... lucidimagination.com/blog/2010/02/01/...
aliasmrchips

3
SOLR非常适合搜索,但是您仍然需要将数据存储在某个地方,以便填充索引。
Mike Purcell 2012年

真正。我假设Victor P在某处有数据,并且需要对其进行索引。这将需要额外的资源...但是,所有建议的方法也都需要这样做。
aliasmrchips 2012年

@aliasmrchips:我认为InfluxDB方法做得更好-它既有效存储(高吞吐量,压缩率比Mongo高80倍),而且查询容易。
Dan Dascalescu

3

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

您还可以研究构建聚合表,以便在原子级别上更快地访问。例如,如果您的数据是全天的,但是您经常会以每周或什至一个月的时间获取数据,则可以在汇总表中预先计算。在某些数据库中,这可以通过缓存视图来完成(不同数据库解决方案的名称各不相同-但基本上是原子数据视图,但是一旦运行,该视图就被缓存/强化到固定的临时表中-进行查询以查询后续匹配查询。可以定期删除它以释放内存/磁盘空间)。

我想我们可以在数据使用方面为您提供一些帮助。


3

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

[edit]为什么您认为完全使用数据库(rdbms或nosql)有意义?此数据不会更改,并且可以存储在内存中。这不是dbms可以增加价值的用例。


实际上,有多种原因,其中最重要的是,如果您有256 GB的内存,那么如果有一些临时空间,操作系统等空间会很好。然后是检查点,日志记录和容错之类的问题-一旦开始计算任何中间结果,您就需要管理存储。我同意RDBMS并不是最佳选择-但是绝对需要比“将大数组加载到内存”更聪明的东西。
查理·马丁

对于接近静态的数据,检查点,日志记录和容错功能极其简单。这听起来像是适用于prevayler风格解决方案的理想选择
Stephan Eggermont

同样,在没有更好地了解应用程序的情况下,无法确定地说什么,但是总的来说,该应用程序并非如您所想的那样静态,因为您想要维护结果集并且因为您要进行昂贵的计算,检查点和预先计算的部分结果。
查理·马丁

2

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

但是,请确保您有足够的MySQL Cluster计算机和足够的内存/ RAM-MySQL Cluster是一个高度面向内存的数据库体系结构。

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

就像其他人所说的那样,更多地了解将要运行的查询将有所帮助。


2

您将需要将数据存储在列式表/数据库中。Vertica和Greenplum等数据库系统都是列式数据库,我相信SQL Server现在允许列式表。这些对于SELECT从非常大的数据集读取非常有效。它们还可以有效地导入大型数据集。

一个免费的列式数据库是MonetDB


1

如果您的用例是简单读取行而不进行聚合,则可以使用Aerospike集群。它位于内存数据库中,并具有文件系统的持久性支持。它还对SSD进行了优化。

如果您的用例需要聚合的数据,请选择具有日期范围分片的Mongo数据库集群。您可以在分片中合并年度虎钳数据。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.