【问题标题】:SQLite is very slow when performing .import on a large table在大表上执行 .import 时,SQLite 非常慢
【发布时间】:2020-02-21 11:25:42
【问题描述】:

我正在运行以下命令:

  .mode tabs
  CREATE TABLE mytable(mytextkey TEXT PRIMARY KEY, field1 INTEGER, field2 REAL);
  .import mytable.tsv mytable

mytable.tsv 约为。 6 GB 和 5000 万行。该过程需要很长时间(数小时)才能运行,并且还完全限制了整个系统的性能,我猜是因为临时磁盘 IO。

我不明白为什么它需要这么长时间以及为什么它会如此频繁地颠簸磁盘,当我有足够的可用物理 RAM 时,它可以用于临时写入。

如何改进这个过程?

PS:是的,我确实搜索了以前的问题和答案,但没有找到任何帮助。

【问题讨论】:

  • 您是否尝试省略create table?只是在这之后使用.mode tabs.import mytable.tsv mytable
  • 由于您的 PK 不是整数,因此将其设为 WITHOUT ROWID 表可能会有所帮助。如果可行的话,可能会对 tsv 文件进行排序。
  • 是的,我认为它更快,但我需要索引第一个条目(即主键)。
  • 我怀疑是文本主键。主键必须被索引,这意味着大量的写入。与整数不同,索引文本更复杂。那一栏是什么?可以将其转换为数字。
  • 添加以下设置有很大帮助。再次感谢。 PRAGMA journal_mode = OFF; PRAGMA cache_size = 7500000; PRAGMA synchronous = 0; PRAGMA temp_store = 2;

标签: sqlite


【解决方案1】:

在 Sqlite 中,normal rowid table 使用 64 位整数主键。如果表定义中的 PK 不是单个 INTEGER 列,则将其视为唯一索引,并且插入的每一行都必须更新原始表和该索引,从而使工作加倍(并且在您的情况下有效地增加了一倍的存储需求)。如果您改为将表设为WITHOUT ROWID,则 PK 是真正的 PK,不需要额外的索引表。仅此一项更改就应该将导入数据集所需的时间和数据库大小大致减半。 (如果您在表上有其他索引,或者将该 PK 用作另一个表中的外键,从长远来看可能不值得进行更改,因为它可能会增加这些表所需的空间量很多给定的密钥长度;在这种情况下,请参阅 Schwern 的答案)。

首先对键列上的输入进行排序也有助于大量导入,因为 B 树页面的随机访问和这些页面内的数据移动较少。一切都进入同一个页面,直到它填满并分配一个新页面并完成任何需要的重新平衡。

您还可以打开一些在正常使用时不推荐使用的不安全设置,因为它们可能导致数据丢失或彻底损坏,但如果在导入过程中由于异常停电或其他原因发生这种情况,您可以随时开始超过。特别是将synchronous modejournal type 设置为OFF。这会减少导入过程中的磁盘写入次数。

【讨论】:

    【解决方案2】:

    我的假设是问题出在文本主键上。这需要构建一个大而昂贵的文本索引。

    主键是一个长核苷酸序列(从 20 到 300 个字符),field1 是一个整数(1 到 1500 之间),field2 是一个相对对数比率(大致在 -10 到 +10 之间)。

    文本主键优点少,缺点多。

    • 它们需要大而慢的索引。构建慢、查询慢、插入慢。
    • 更改文本很诱人,这正是您不希望主键做的事情。
    • 任何引用它的表还需要存储和索引添加到膨胀的文本。
    • 由于文本主键,与此表的联接会变慢。

    考虑一下当您创建一个引用该表的新表时会发生什么。

    create table othertable(
      myrefrence references mytable, -- this is text
      something integer,
      otherthing integer
    )
    

    othertable 现在必须存储整个序列的副本,从而使表格膨胀。它现在不是简单的整数,而是有一个文本列,使表格膨胀。而且它必须创建自己的文本索引,使索引膨胀,并减慢连接和插入的速度。

    相反,使用普通的、整数的、自动递增的主键并使序列列唯一(也被索引)。这提供了文本主键的所有优点,而没有任何缺点。

    create table sequences(
      id integer primary key autoincrement,
      sequence text not null unique,
      field1 integer not null,
      field2 real not null
    );
    

    现在对sequences 的引用是一个简单的整数。


    由于 SQLite 导入过程不是很可定制,因此在 SQLite 中将数据有效地导入此表需要几个步骤。

    首先,将您的数据导入一个尚不存在的表中。确保它的标题字段与您所需的列名匹配。

    $ cat test.tsv
    sequence    field1  field2
    d34db33f    1   1.1
    f00bar  5   5.5
    somethings  9   9.9
    
    sqlite> .import test.tsv import_sequences
    

    由于没有发生索引,这个过程应该很快。 SQLite 创建了一个名为import_sequences 的表,其中包含text 类型的所有内容。

    sqlite> .schema import_sequences
    CREATE TABLE import_sequences(
      "sequence" TEXT,
      "field1" TEXT,
      "field2" TEXT
    );
    
    sqlite> select * from import_sequences;
    sequence    field1      field2    
    ----------  ----------  ----------
    d34db33f    1           1.1       
    f00bar      5           5.5       
    somethings  9           9.9       
    

    现在我们创建最终的生产表。

    sqlite> create table sequences(
       ...>   id integer primary key autoincrement,
       ...>   sequence text not null unique,
       ...>   field1 integer not null,
       ...>   field2 real not null
       ...> );
    

    为了提高效率,通常您会在导入之后添加唯一约束,但 SQLite has very limited ability to alter a table 并且不能更改现有列,除非更改其名称。

    现在将导入表中的数据传输到sequences。主键将自动填充。

    insert into sequences (sequence, field1, field2)
    select sequence, field1, field2
    from import_sequences;
    

    因为sequence 必须被索引,这可能不会更快地导入,但它会导致更好和更有效的模式向前发展。如果您想提高效率,请考虑a more robust database

    确认数据正确传入后,删除导入表。

    【讨论】:

    • 使用普通的、整数的、自动递增的主键并使序列列唯一(也被索引)。这就是normal rowid table 在 Sqlite 中的工作方式。对于此导入,它不会获得任何性能方面的 OP。当然,如果他有其他索引或将其用作另一个表中的 FK。
    • @Shawn 是的,这可能不会加快导入速度;我在最后提到了这一点。假设序列必须是唯一的,我看不出有办法绕过昂贵的索引。如果他们正在经历昂贵的进口麻烦,不妨从中获得一个好的模式。如果你不打算建立关系,是否需要主键?
    【解决方案3】:

    以下设置极大地加快了速度。

    PRAGMA journal_mode = OFF
    PRAGMA cache_size = 7500000
    PRAGMA synchronous = 0
    PRAGMA temp_store = 2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-30
      • 1970-01-01
      • 2013-06-27
      • 1970-01-01
      • 1970-01-01
      • 2014-10-12
      • 2016-08-22
      相关资源
      最近更新 更多