SQLite导入选项卡文件:.import是每行执行一次插入还是使用事务对它们进行分组?

Pab*_*cia 4 sqlite

我从选项卡文件导入数百万行,而SQLite .import .mode选项卡非常慢.我有三个索引,所以可能是索引的缓慢.但首先我要检查.import是否将批次/所有这些行分组到一个提交中.我无法找到.import如何工作的文档.有人知道吗?如果索引是问题(我之前遇到过mysql问题)如何在.import结束时禁用它并重新索引?

[更新1]

关注@sixfeetsix评论.

我的架构是:

CREATE TABLE ensembl_vf_b36 (
        variation_name  varchar(20),
        chr     varchar(4),
        start   integer,
        end     integer,
        strand  varchar(5),
        allele_string    varchar(3),
        map_weight      varchar(2),
        flags           varchar(50),
        validation_status       varchar(100),
        consequence_type        varchar(50)
);
CREATE INDEX pos_vf_b36_idx on ensembl_vf_b36 (chr, start, end);
Run Code Online (Sandbox Code Playgroud)

数据:

rs35701516      NT_113875       352     352     1       G/A     2       NULL    NULL    INTERGENIC
rs12090193      NT_113875       566     566     1       G/A     2       NULL    NULL    INTERGENIC
rs35448845      NT_113875       758     758     1       A/C     2       NULL    NULL    INTERGENIC
rs17274850      NT_113875       1758    1758    1       G/A     2       genotyped       cluster,freq    INTERGENIC
Run Code Online (Sandbox Code Playgroud)

此表中有15_608_032个条目

这些是统计数据

 $  time sqlite3 -separator '   ' test_import.db '.import variations_build_36_ens-54.tab ensembl_vf_b36'

real    29m27.643s
user    4m14.176s
sys     0m15.204s
Run Code Online (Sandbox Code Playgroud)

[更新2]

@sixfeetsix有一个很好的答案,如果你正在读这个,你也会感兴趣

在sqlite3中更快的批量插入?

Sqlite3:插入时禁用主键索引?

[update3] 30分钟 - > 4分钟的解决方案

即使所有的优化(参见接受的答案)仍然需要将近30分钟,但如果索引未使用并在末尾添加,则总时间为4分钟:

-- importing without indexes:
       real    2m22.274s
       user    1m38.836s
       sys     0m4.850s

 -- adding indexes
     $  time sqlite3 ensembl-test-b36.db < add_indexes-b36.sql

     real    2m18.344s
     user    1m26.264s
     sys     0m6.422s
Run Code Online (Sandbox Code Playgroud)

小智 6

我相信,随着越来越多的记录被添加,构建索引的确很慢.根据您拥有的RAM,您可以告诉sqlite使用足够的内存,以便所有这些索引构建活动都在内存中完成(即没有所有I/O,否则会发生内存较少).

对于15M记录,我要说你应该将缓存大小设置为500000.

您还可以告诉sqlite将其事务日志保留在内存中.

最后,您可以将synchronous设置为OFF,以便sqlite永远不会等待写入提交到磁盘.

使用这个我能够将导入15M记录所需的时间除以5(14分钟缩小到2.5),随机GUID的记录分为5列,使用三个中间列作为索引:

b40c1c2f    912c    46c7    b7a0    3a7d8da724c1
9c1cdf2e    e2bc    4c60    b29d    e0a390abfd26
b9691a9b    b0db    4f33    a066    43cb4f7cf873
01a360aa    9e2e    4643    ba1f    2aae3fd013a6
f1391f8b    f32c    45f0    b137    b99e6c299528
Run Code Online (Sandbox Code Playgroud)

所以试试这个我建议你把所有的说明放在一些文件中,比如import_test:

pragma journal_mode=memory;
pragma synchronous=0;
pragma cache_size=500000;
.mode tabs
.import variations_build_36_ens-54.tab ensembl_vf_b36
Run Code Online (Sandbox Code Playgroud)

然后尝试一下:

time sqlite3 test_import.db < import_test
Run Code Online (Sandbox Code Playgroud)

编辑

这是回答Pablo(OP)的回答后的评论(它很长时间以作为评论):我(受过教育的)猜测是这样的:

  1. 因为.import本身不是sql,它对事务没有多少麻烦,我甚至倾向于认为它写得更快,即使你在一个"正常"事务中完成了所有这些操作; 和,
  2. 如果你有足够的内存来分配,并按照我的建议你设置你的环境,这里真正的(时间)生猪就是读取平面文件,然后写出数据库的最终内容,因为两者之间发生的事情发生得非常快; 也就是说,当你将这些潜在的收益与在磁盘I/O上花费的(可能)不可压缩的时间进行比较时,通过优化它没有太多时间来获得.

如果我错了,虽然我很高兴听到为什么为了我自己的利益.

编辑2

我在.import期间在索引到位之​​间进行了比较测试,并在.import完成后立即添加了索引.我使用相同的技术生成由分割随机UUID组成的15M记录:

import csv, uuid
w = csv.writer(open('bla.tab', 'wb'), dialect='excel-tab')
for i in xrange(15000000):
    w.writerow(str(uuid.uuid4()).split('-'))
Run Code Online (Sandbox Code Playgroud)

然后我测试了使用之前和之后创建的索引导入(此处创建了索引):

pragma journal_mode=memory;
pragma synchronous=0;
pragma cache_size=500000;
create table test (f1 text, f2 text, f3 text, f4 text, f5 text);
CREATE INDEX test_idx on test (f2, f3, f4);
.mode tabs
.import bla.tab test
Run Code Online (Sandbox Code Playgroud)

所以这里是在之前添加索引的时间:

[someone@somewhere ~]$ time sqlite3 test_speed.sqlite < import_test 
memory

real   2m58.839s
user   2m21.411s
sys    0m6.086s
Run Code Online (Sandbox Code Playgroud)

并在以下添加索引:

[someone@somewhere ~]$ time sqlite3 test_speed.sqlite < import_test 
memory

real   2m19.261s
user   2m12.531s
sys    0m4.403s
Run Code Online (Sandbox Code Playgroud)

你看到"用户"时间差(~9s)如何不考虑全时差(~40s)?对我来说,这意味着在之前创建索引时会发生一些额外的I/O,所以我认为所有内容都是在没有额外I/O的内存中完成的.

结论:创建索引后,您将获得更好的导入时间(就像Donal提到的那样).

  • +1:您还可以尝试在插入所有行期间没有索引任何列,之后添加索引(因此它会立即构建所有列而不是零碎).除了你的建议之外,这听起来都很合理. (2认同)
  • @Pablo:我更新了我的答案以涵盖你的意见. (2认同)