在sqlite3中更快的批量插入?

scu*_*bbl 40 c++ sqlite insert bulk

我有一个大约30000行数据的文件,我想加载到sqlite3数据库.有没有比为每行数据生成插入语句更快的方法?

数据以空格分隔,并直接映射到sqlite3表.是否有任何类型的批量插入方法用于向数据库添加卷数据?

如果它没有内置,有没有人设计出一些狡猾的好方法呢?

我应该先问一下,有没有一种C++方法可以从API中做到这一点?

Jav*_*ier 57

  • 在事务中包装所有INSERT,即使只有一个用户,它也要快得多.
  • 用准备好的陈述.

  • PRAGMA 日志模式=内存;可能对某些人有帮助 (2认同)

ram*_*jan 35

您想使用该.import命令.例如:

$ cat demotab.txt
44      92
35      94
43      94
195     49
66      28
135     93
135     91
67      84
135     94

$ echo "create table mytable (col1 int, col2 int);" | sqlite3 foo.sqlite
$ echo ".import demotab.txt mytable"  | sqlite3 foo.sqlite

$ sqlite3 foo.sqlite
-- Loading resources from /Users/ramanujan/.sqliterc
SQLite version 3.6.6.2
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from mytable;
col1    col2
44      92
35      94
43      94
195     49
66      28
135     93
135     91
67      84
135     94
Run Code Online (Sandbox Code Playgroud)

请注意,此批量加载命令不是SQL,而是SQLite的自定义功能.因此,它有一个奇怪的语法,因为我们将它传递echo给交互式命令行解释器,sqlite3.

在PostgreSQL中,等价物是COPY FROM:http: //www.postgresql.org/docs/8.1/static/sql-copy.html

在MySQL中它是LOAD DATA LOCAL INFILE:http: //dev.mysql.com/doc/refman/5.1/en/load-data.html

最后一件事:记住小心的价值.separator.在进行批量插入时,这是一个非常常见的问题.

sqlite> .show .separator
     echo: off
  explain: off
  headers: on
     mode: list
nullvalue: ""
   output: stdout
separator: "\t"
    width:
Run Code Online (Sandbox Code Playgroud)

您应该在执行之前将分隔符显式设置为空格,制表符或逗号.import.

  • `.show`而不是`.show .separator`为我工作 (4认同)
  • 查看 SQLite 的 shell.c 的代码,.import 只是在事务中使用了准备好的语句。 (3认同)
  • 这很棒,而且非常快。20 分钟缩短到 3 秒。 (2认同)

小智 20

您还可以尝试调整一些参数以获得额外的速度.特别是你可能想要的PRAGMA synchronous = OFF;.

  • pragma synchronous = OFF是一个坏主意 - 它几乎不会影响批量插入的性能,并且你的数据库在电源故障时会被破坏.更好的想法是将插入包装在事务中. (23认同)
  • 在TRANSACTION中包装INSERTS并使用PRAGMA journal_mode = MEMORY; 将阻止INSERT在事务结束之前命中磁盘. (12认同)
  • 请注意,MEMORY会在电源故障时损坏数据库 (4认同)
  • PRAGMA journal_mode = WAL; 一次允许多个写入者,您最终可以使用线程来写入数据。请注意,在预写日志激活的情况下,断电后数据库不会损坏。 (3认同)

ast*_*tef 19

我已经测试了这里的答案中提出的一些编译指示

  • synchronous = OFF
  • journal_mode = WAL
  • journal_mode = OFF
  • locking_mode = EXCLUSIVE
  • synchronous = OFF+ locking_mode = EXCLUSIVE+journal_mode = OFF

这是我在事务中插入不同数量的数字:

增加批量大小可以给你带来真正的性能提升,而关闭日志、同步、获取独占锁将带来微不足道的收益。大约 110k 的点显示随机后台负载如何影响您的数据库性能。

此外,值得一提的是,这journal_mode=WAL是默认值的一个很好的替代方案。它提供了一些增益,但不会降低可靠性。

C# 代码。

  • 我在一个项目中注意到的一件事是,如果可能的话,应将批次限制在单个表中。如果您处于事务内部并在循环中更新表 a,然后更新表 b,则这将比循环两次(一次更新表 a,然后再次更新表 b)慢得多。 (2认同)

pax*_*977 18

  • 提高PRAGMA default_cache_size 到一个更大的数字.这将增加内存中缓存的页面数.

  • 将所有插入包装到单个事务中,而不是每行一个事务.

  • 使用编译的SQL语句来执行插入操作.
  • 最后,如前所述,如果您愿意放弃完整的ACID合规性,请设置PRAGMA synchronous = OFF;.


Han*_*ger 10

RE:"有没有更快的方法为每行数据生成插入语句?"

第一:通过使用SQLITE3的裁剪下来到2条SQL语句的虚拟表API

create virtual table vtYourDataset using yourModule;
-- Bulk insert
insert into yourTargetTable (x, y, z)
select x, y, z from vtYourDataset;
Run Code Online (Sandbox Code Playgroud)

这里的想法是您实现一个C接口,它读取您的源数据集并将其作为虚拟表呈现给SQlite,然后一次性从源到目标表执行SQL复制.它听起来比实际更难,我用这种方式测量了巨大的速度改进.

第二:利用此处提供的其他建议,即编译指示设置和使用交易.

第三:也许看看你是否可以取消目标表上的一些索引.这样,sqlite将为插入的每一行更新索引


Fla*_*ken 6

一个好的折衷方案是将您的 INSERTS 包裹在 BEGIN; 之间。和结束;关键字即:

BEGIN;
INSERT INTO table VALUES ();
INSERT INTO table VALUES ();
...
END;
Run Code Online (Sandbox Code Playgroud)


小智 5

没有办法批量插入,但有一种方法可以将大块写入内存,然后将它们提交到数据库.对于C/C++ API,只需:

sqlite3_exec(db,"BEGIN TRANSACTION",NULL,NULL,NULL);

...(INSERT语句)

sqlite3_exec(db,"COMMIT TRANSACTION",NULL,NULL,NULL);

假设db是您的数据库指针.