mysql - 如何加快从 <我的语言> 插入 <我的 DBMS> SQL 数据库?

标签 mysql sql sqlite postgresql orm

<分区>

有很多关于加速插入 SQL 数据库的问题,例如 this , this , this , this和(我最喜欢的)this .许多问题伪装成语言相关,但问题通常简化为:

哪些通用技术可以加快从我的 [特定语言] 程序插入到我的 [特定 DBMS] SQL 数据库的速度?

最佳答案

INSERTS 过程中最大的低效来自两个方面:单个数据事务需要时间,并且您的语言和数据库之间的 ORM 适配器并不总是特别高效。

此答案使用 Ruby On Rails 作为其示例语言,但此处展示的技术几乎适用于任何具有底层数据库接口(interface)的高级语言。

结论(TL;DR)

通过下面列出的任一批量插入方法一次插入 500 条记录可以使您的速度提高超过 20 倍。通过调整,这可能会变得更高。

测试

让我们从包含 123001 条记录的内存数组开始。 (这些来自 [City of Toronto Transportation Datasets] 的“trips.txt”文件。1)

dataset = Utilities.load_csv(Rails.root.join("datasets", "trips.txt")

基线:一次插入一个

在 Ruby On Rails 中,您可以:

dataset.each {|record| Trip.create!(record) }

这基本上转化为 123,000 个形式的单独 SQL 调用:

INSERT INTO "trips" (<column_names>) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)  [<column name/value pairs>]
(123000 more times...)

加速 1:包装在交易中

这几乎是一样的,但是将内部循环包装在单个 SQL 事务中:

begin transaction
INSERT INTO "trips" (<column_names>) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)  [<column name/value pairs>]
(123000 more times...)
commit transaction

加速 2:批量插入版本 A

这将原始数组分成 500 个批处理(以避免 SQLite 的限制和潜在的字符串缓冲区大小),其中每个事务具有以下形式:

INSERT INTO trips (comma_separated_column_names) VALUES
    (comma_separated_values_for_row_1),
    (comma_separated_values_for_row_2),
    ...
    (comma_separated_values_for_row_500);
... repeated 246 times

加速 3:批量插入版本 B

一些 DBMS 不支持 Bulk INSERT 版本 A 的语法(特别是旧版本的 SQLite)。以下形式在功能上是相同的,并受到许多数据库的支持(尽管不严格符合 SQL-92):

     INSERT INTO trips (comma_separated_column_names)
          SELECT comma_separated_values_for_row_1
UNION ALL SELECT comma_separated_values_for_row_2
...
UNION ALL SELECT comma_separated_values_for_row_500
...repeated 246 times

加速 4:DBM 特定方法

正如 https://stackoverflow.com/users/20860/bill-karwin 指出的那样, 一个可以

take advantage of vendor-specific bulk-loading commands, for example LOAD DATA INFILE for MySQL, COPY for PostgreSQL, or SQL*Loader for Oracle, etc. Each brand has their own command or tool, so there's no vendor-neutral way of doing this, but these methods often have an order of magnitude better performance, so they should not be overlooked.

虽然这些不是通用技术,但它们在特定情况下很有用。我们没有在下面的测试中对这些进行基准测试。

相对加速

我们在 MySQL、PostgreSQL 和 SQLite 中测试了上面显示的各种技术。下面的数字显示了各种方法相对于基线情况的速度。第一个数字是相对用户+系统时间,(括号)中的数字是相对耗时。

(注意:我选择不显示绝对时间,因为这不是关于哪个数据库最快的讨论——有太多变量无法对此做出明智的声明。如果被插入,我将把这两个代码发布到github 你可以运行你自己的测试并得出你自己的结论。)

MySQL

  • 包裹在交易中:1.2 倍(1.4 倍)
  • 批量插入版本 A:24.3x (19.0x)
  • 批量插入版本 B:24.3 倍(17.1 倍)

PostgreSQL

  • 包裹在交易中:1.2 倍(1.6 倍)
  • 批量插入版本 A:27.2 倍(16.7 倍)
  • 批量插入版本 B:27.2 倍(13.9 倍)

SQLite

  • 包裹在交易中:1.6 倍(2.4 倍)
  • 批量插入版本 A:25.8 倍(24.7 倍)
  • 批量插入版本 B:24.1 倍 (34.1 倍)

测试笔记

Processing environment: 2.66 GHz Intel Core i7, 8GB 1067 MHz DDR3
Operating System: OS X v 10.9.5 
Ruby version: 2.0.0 (64 bit) 
Rails version: 4.0.2 
MySQL: Server version: 5.1.49  
PostgreSQL: psql (9.3.1, server 8.3.14) 
SQLite: SQLite version 3.7.12 2012-04-03 19:43:07

关于mysql - 如何加快从 <我的语言> 插入 <我的 DBMS> SQL 数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21739930/

相关文章:

mysql - Codeigniter 查看 undefined variable 错误

Mysql只选择指定数量的连续空闲时间段的员工

mysql - 如何在值组之间添加换行符/标题?

c++ - VC9下SQLite3的麻烦

java - 将 Sugar ORM 与 UUID 或 Joda-Time 结合使用

sql - 反向外键约束

mysql - 生产系统 Spring 数据库池的最佳方式

带有 bool 值的 MySQL JSON_SEARCH 不起作用

sql - SQL Server 子查询无法选择所需的结果

sql - 只返回符合所有条件的行