在数据库中存储数千万条记录的最佳选择是什么?
What are the best options to store tens of millions of records in a database?
我在为某个交易员工作,他想建立一个相对庞大的股票报价档案以供进一步分析。我们可以访问信息提供者,并且数据始终具有相同的结构:代码名称、时间戳和 OHLC,因此它适合关系数据库方法。
到目前为止,我在 Rails(Ruby 2.6、Rails 6.0)和 PostgreSQL 上使用 Ruby 制作了一个工作原型。它可以非常快地从提供者那里获取数据,但是将数百万条记录存储到数据库中却非常慢。我从 ActiveRecord 风格切换到纯 SQL ,它使速度快了 3 倍,但存储少量所需数据的过程仍然很长。
所以我需要以某种方式提高原型的性能,但不确定具体要做什么。我当然可以使用 Redis 之类的东西,但无论如何我都需要将数据存储到持久存储中。
我该怎么办?
这个问题可能会被标记,但无论如何我都会给你一个答案:
ActiveRecord 不一定是慢的。 ActiveModel 很慢。
ActiveModel 将您的结果集和数据库类型映射到 ruby 友好的对象,并将您的数据(字符串类型的东西)转换为 inserting/updating 的数据库类型。
如果您正在渲染 JSON,请确保您使用的是 C 渲染器 OJ 或类似软件:https://github.com/ohler55/oj — 它对较大的数据集和响应有很大影响。
如果您愿意将结果集作为可以循环遍历的散列,则可以避免使用 connection#exec_query
:
攻击 ActiveModel
stocks_query = Stock.all
results = Stock.connection.exec_query(stocks_query.to_sql).to_a
results.each do |result|
puts result
end
如果您要插入数百万行,甚至更新数百万行,您可能应该使用正确的工具来完成这项工作:raw SQL。但这变得笨拙,你喜欢 ruby,所以你可以使用像 https://github.com/zdennis/activerecord-import 这样的东西,它允许你写 ruby,它会处理剩下的。
几千万条记录并不算多。 PostgreSQL 可能是您最好的选择,而且不会引入大量不必要的复杂性。您只需要了解使用 Rails 与数据库层通信的瓶颈,以及如何解决这些瓶颈。确保您的数据库也已正确编制索引。如果您不确定,https://github.com/plentz/lol_dba 是一个很好的起点。
大多数 DBMS 支持您可以利用的 BULK INSERT
,或者更好的是您可以使用 PG 的 COPY
命令,该命令针对加载大量行进行了优化。当然,这意味着写原始 SQL.
如果出于某种原因你不想使用 COPY
而你更愿意使用 INSERT
你的行,那么在 PG 中使用 [= 禁用自动提交可能是一个明智的做法15=] 然后在插入每一行后提交——类似于:
BEGIN;
INSERT INTO table (column1, column2, …)
VALUES
(value1, value2, …),
(value1, value2, …) ,...;
COMMIT;
这里的想法是首先删除所有索引和外键约束(如果有的话),然后在完成后将它们放回去。这应该加快速度。
此外,如果您可以(并且它是安全的)在文件中准备好数据(不确定您还可以如何确保在数据库准备好下一行时数据已准备好插入而不会使事情复杂化) .
PG 也有 guide for things like this. There's more info on these answers。
我在为某个交易员工作,他想建立一个相对庞大的股票报价档案以供进一步分析。我们可以访问信息提供者,并且数据始终具有相同的结构:代码名称、时间戳和 OHLC,因此它适合关系数据库方法。 到目前为止,我在 Rails(Ruby 2.6、Rails 6.0)和 PostgreSQL 上使用 Ruby 制作了一个工作原型。它可以非常快地从提供者那里获取数据,但是将数百万条记录存储到数据库中却非常慢。我从 ActiveRecord 风格切换到纯 SQL ,它使速度快了 3 倍,但存储少量所需数据的过程仍然很长。 所以我需要以某种方式提高原型的性能,但不确定具体要做什么。我当然可以使用 Redis 之类的东西,但无论如何我都需要将数据存储到持久存储中。 我该怎么办?
这个问题可能会被标记,但无论如何我都会给你一个答案:
ActiveRecord 不一定是慢的。 ActiveModel 很慢。
ActiveModel 将您的结果集和数据库类型映射到 ruby 友好的对象,并将您的数据(字符串类型的东西)转换为 inserting/updating 的数据库类型。
如果您正在渲染 JSON,请确保您使用的是 C 渲染器 OJ 或类似软件:https://github.com/ohler55/oj — 它对较大的数据集和响应有很大影响。
如果您愿意将结果集作为可以循环遍历的散列,则可以避免使用 connection#exec_query
:
stocks_query = Stock.all
results = Stock.connection.exec_query(stocks_query.to_sql).to_a
results.each do |result|
puts result
end
如果您要插入数百万行,甚至更新数百万行,您可能应该使用正确的工具来完成这项工作:raw SQL。但这变得笨拙,你喜欢 ruby,所以你可以使用像 https://github.com/zdennis/activerecord-import 这样的东西,它允许你写 ruby,它会处理剩下的。
几千万条记录并不算多。 PostgreSQL 可能是您最好的选择,而且不会引入大量不必要的复杂性。您只需要了解使用 Rails 与数据库层通信的瓶颈,以及如何解决这些瓶颈。确保您的数据库也已正确编制索引。如果您不确定,https://github.com/plentz/lol_dba 是一个很好的起点。
大多数 DBMS 支持您可以利用的 BULK INSERT
,或者更好的是您可以使用 PG 的 COPY
命令,该命令针对加载大量行进行了优化。当然,这意味着写原始 SQL.
如果出于某种原因你不想使用 COPY
而你更愿意使用 INSERT
你的行,那么在 PG 中使用 [= 禁用自动提交可能是一个明智的做法15=] 然后在插入每一行后提交——类似于:
BEGIN;
INSERT INTO table (column1, column2, …)
VALUES
(value1, value2, …),
(value1, value2, …) ,...;
COMMIT;
这里的想法是首先删除所有索引和外键约束(如果有的话),然后在完成后将它们放回去。这应该加快速度。
此外,如果您可以(并且它是安全的)在文件中准备好数据(不确定您还可以如何确保在数据库准备好下一行时数据已准备好插入而不会使事情复杂化) .
PG 也有 guide for things like this. There's more info on these answers。