【问题标题】:How to store 7.3 billion rows of market data (optimized to be read)?如何存储73亿行行情数据(优化可读)?
【发布时间】:2012-04-06 14:21:41
【问题描述】:

我有一个数据集,包含自 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,所以我很灵活

【问题讨论】:

  • 描述预期的典型查询
  • “我认为你应该使用 MongoDB,因为它是网络规模的。”
  • 你可能想要一张大表,按股票代码分区。
  • 数据集很大!您可能想四处搜索数据挖掘和分析,看看您能找到什么。
  • 单表的“标准 RDBMS”还不够吗? (我只处理数百万但“为我工作”。不妨试试看。记得根据需要索引/集群/分区。)

标签: database


【解决方案1】:

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

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

更新

好吧,为什么?

首先,请注意我询问了这些查询。你不能——我们当然不能——在不知道工作量是什么样的情况下回答这些问题。 (顺便说一句,我很快就会有一篇关于此的文章,但我今天无法链接它。)但问题的规模让我考虑离开大型旧数据库,因为

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

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

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

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

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

【讨论】:

  • “NoSQL”在这里有什么优势?为什么不是传统关系型数据库中的单个大表? (使用正确的索引等)每个人都会选择“NoSQL”、“NoSQL”、“NoSQL”,但是...... 为什么
  • 我不得不说我的建议也是使用 Apache Accumulo 的 NoSQL 方法(这是个人偏好)。数据集很小(对于 Accumulo)和所需的查询类型似乎非常适合使用其分布式迭代器堆栈。
  • 感谢您的扩展答案。我可以 +1。
  • 有时这里的一些 cmets 只是让我感到困惑。 '-1 用于没有意义的数据库?整个答案是反对传统数据库。
【解决方案2】:

据我了解,HDF5 专门设计用于股票数据的时间序列存储作为一种潜在应用。堆垛机同行已经证明 HDF5 适合处理大量数据:chromosomesphysics

【讨论】:

  • +1 表示特定解决方案。我喜欢,但是喜欢 SQL DQL(在大多数情况下)及其提供的灵活性......不确定 HDF5 需要什么才能摆脱“分层视图”。
【解决方案3】:

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

我对 Amazon 存储一无所知,但如果您没有直接文件访问之类的功能,则基本上可以有 blob - 您需要平衡大 blob(记录较少,但读取的数据可能比您每次都需要)带有小 blob(更多的记录会带来更多的开销,并且可能会有更多的请求来获取它们,但每次返回的无用数据更少)。

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

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

您还没有说其他三个整数列是什么样的,但是如果您也可以为这三个列使用 64 位,那么您可以将整个记录存储在 16 个字节中。整个数据库只有大约 110GB,这真的不是很多...

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

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

【讨论】:

  • 这又取决于他如何使用数据。如果他的查询是全面提取特定数据(股票代码明智),那么这将导致读取每个文件并具有特定的日期编码以从每个文件中提取正确的数据。或者,如果他想要每周表现最好的股票,那么在这种设置下必须阅读所有记录并进行比较,那将是一场噩梦。如果没有这些信息,我们只能猜测这是用于固定存储 - 可能作为一个批量 DW 将在某个时候提供报告 DW(ETL 源)。
  • @Wolf5370:是的,我们当然需要知道查询将是什么,但我们至少从问题中得到了一些迹象:'大多数查询会像“给我 AAPL 的价格在 2012 年 4 月 12 日 12:15 和 2012 年 4 月 13 日 12:52' 之间。很高兴知道 其他 查询是什么,以及相对频率和性能要求。
  • @JonSkeet 它确实取决于工作量,但我对这种系统有一些领域知识,而且很少只是“在一个范围内选择一只股票”:更多的是“选择在这个范围内这个投资组合中的股票,计算β,然后尝试这个可能的股票列表,看看β是什么。”这就是为什么它驱使你走向类似 OLAP 的东西。
  • @CharlieMartin:好吧,我只是按照问题所述进行。但是,如果您基本上可以在内存中获取所有信息(通过几台服务器),那么它仍然很容易 - 向每个服务器询问投资组合中的相关股票,然后将结果放在一起。我认为我关于使用数据的已知方面(每分钟一次,但不是在周末或过夜)的观点在显着降低将数据全部存储在内存中的难度方面仍然有用。
  • 这个讨论让我想起了 Fred Brooks 的名言“表示是编程的本质”以及 Bentley 的“Programming Pearls”中的相关问题。
【解决方案4】:

让我建议您看一下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]

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

【讨论】:

  • 您也可以使用 Amazon EC2 来设置您的 solr 实例...lucidimagination.com/blog/2010/02/01/…
  • SOLR 非常适合搜索,但您仍然需要将数据存储在某处,以便填充索引。
  • 是的。我假设 Victor P 在某处有数据,需要对其进行索引。这将需要额外的资源......但是,所有建议的方法也都可以。
  • @aliasmrchips:我认为InfluxDB approach 做得更好——它既能高效存储(高吞吐量,比 Mongo 好 80 倍的压缩率),又能轻松查询。
【解决方案5】:

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

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

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

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

    • 实际上,有几个原因,尤其是如果您有 256 GB 的内存,如果有一些空间用于临时空间、操作系统等,那就太好了。然后是检查点、日志记录和容错等问题——一旦你开始计算任何中间结果,你就需要管理存储。我同意 RDBMS 不是最佳选择——但绝对需要比“将大数组加载到内存”更智能的东西。
    • 检查点、日志记录和容错对于近乎静态的数据来说非常简单。这听起来很适合 prevayler 风格的解决方案
    • 同样,如果没有对应用程序有更好的了解,就无法确定,但总的来说,应用程序并不像您想象的那样静态,因为您想要维护结果集并且因为您再次使用检查点和预先计算的部分结果进行昂贵的计算。
    【解决方案7】:

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

    请确保您有足够的 MySQL Cluster 机器和足够的内存/RAM 供每台机器使用 - MySQL Cluster 是一个高度面向内存的数据库架构。

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

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

    【讨论】:

      【解决方案8】:

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

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

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

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

      然后将文件截断为该大小并将其映射到内存中(在 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;
      

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

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

      【讨论】:

      • 花几百把它放在SSD而不是硬盘上。随机读取大约快一百倍。或者在 ram 上花费 10K。再快一百倍
      • @Andrew Tomazos 谢谢老兄,这是“答案”
      • StockPrice sizeof 为 char[4] = 4 bytes int = 4 bytes short = 2 bytes float = 4 bytes float = 4 bytes float = 4 bytes float = 4 bytes float = 4 bytes int = 4 bytes int = 4 bytes int = 4 bytes ------------ 42 bytes 大约 3066 亿字节 = ~ 285.5435013771057 GB 内存...祝你好运
      • @ZagNut:如果您的意思是您需要 300GB 的物理内存,那么这是不正确的 - mmap 不会将整个内容复制到内存中,它会根据需要将其分页/分页(在与交换文件相同的方式)。
      【解决方案9】:

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

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

      【讨论】:

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

      首先,一年中没有 365 个交易日,节假日 52 个周末 (104) = 比如说 250 x 市场开盘的实际时间,就像有人说的那样,使用符号作为主键不是一个好主意,因为符号改变,使用带有符号 (char) 的 k_equity_id (数字),因为符号可以像这样 A 或 GAC-DB-B.TO ,然后在您的价格信息数据表中,您有,所以您的73 亿的估计值被大大高估了,因为 14 年来每个符号只有大约 170 万行。

      k_equity_id k_date k_分钟

      对于 EOD 表(将比其他数据高 1000 倍查看)

      k_equity_id k_date

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

      【讨论】:

        【解决方案11】:

        我有一个包含 1000 只股票的 1 分钟数据的数据集 [...] 大多数 (99.9%) 我只会执行 读取 请求。

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

        这个问题是在 2012 年提出的,从那时起,几个数据库引擎一直在开发专门用于管理时间序列的功能。我使用 InfluxDB 取得了很好的成果,它是开源的、用 Go 编写的并获得 MIT 许可。

        InfluxDB 专门针对存储和查询时间序列数据进行了优化。 Much more so than Cassandra,经常被吹捧为存储时间序列的好方法:

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

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

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

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

        open sourced benchmarks

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

        查询也很简单。如果你的行看起来像&lt;symbol, timestamp, open, high, low, close, volume&gt;,你可以使用 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

        【讨论】:

        【解决方案12】:

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

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

        【讨论】:

          【解决方案13】:

          您需要将数据存储在columnar table / database 中。像 Vertica 和 Greenplum 这样的数据库系统是柱状数据库,我相信 SQL Server 现在允许使用柱状表。对于来自非常大的数据集的SELECTing,这些非常有效。它们在导入大型数据集方面也很有效。

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

          【讨论】:

            猜你喜欢
            • 2013-08-17
            • 2012-10-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-04-15
            • 2011-11-12
            • 2019-05-27
            相关资源
            最近更新 更多