SQLite очень медленно работает при импорте больших таблиц - PullRequest
1 голос
/ 16 января 2020

Я запускаю следующее:

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

mytable.tsv составляет ок. 6 ГБ и 50 миллионов строк. Процесс занимает очень много времени (часов) и полностью снижает производительность всей системы, я полагаю, из-за временного дискового ввода-вывода.

Я не понимаю, почему это занимает так много времени и почему он так сильно перебивает диск, когда у меня достаточно свободного физического ОЗУ, которое он может использовать для временной записи.

Как мне улучшить этот процесс?

PS: Да, я действительно искал предыдущий вопрос и ответ, но ничего, что я нашел, не помогло.

Ответы [ 3 ]

2 голосов
/ 17 января 2020

Я предполагаю, что проблема заключается в текстовом первичном ключе. Это требует построения большого и дорогого текстового индекса.

Первичный ключ - это длинная нуклеотидная последовательность (от 20 до 300 символов), field1 - это целое число (от 1 до 1500), а field2 - это Относительное логарифмическое отношение (примерно от -10 до +10).

Текстовые первичные ключи имеют мало преимуществ и много недостатков.

  • Они требуют больших, медленных индексов. Медленно строить, медленно запрашивать, медленно вставлять.
  • Заманчиво изменить текст, именно то, что вы не хотите, чтобы первичный ключ делал.
  • Любая таблица, ссылающаяся на него, также требует хранения и индексация текста при добавлении в bloat.
  • Соединения с этой таблицей будут выполняться медленнее из-за первичного ключа текста.

Рассмотрим, что происходит, когда вы создаете новую таблицу, которая ссылается на эту.

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

Поскольку индексирование не происходит, этот процесс должен go довольно быстро. 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 имеет очень ограниченную возможность изменять таблицу и не может изменять существующий столбец, кроме как для изменения его имени.

Теперь передайте данные из таблицы импорта в sequences. Первичный ключ будет заполнен автоматически.

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

Поскольку индекс sequence должен быть проиндексирован, это может не ускорить импорт, но это приведет к гораздо лучшей и более эффективной схеме в будущем. Если вы хотите повысить эффективность, рассмотрите более надежную базу данных .

После того, как вы подтвердите правильность данных, удалите таблицу импорта.

1 голос
/ 17 января 2020

В Sqlite обычная таблица строк использует 64-разрядный целочисленный первичный ключ. Если в определении таблицы есть PK, то это не один столбец INTEGER, который вместо этого обрабатывается как уникальный индекс, и каждая вставленная строка должна обновлять как исходную таблицу, так и этот индекс, удваивая работу (А в дело эффективно удваивает требования к хранению). Если вместо этого вы сделаете свою таблицу WITHOUT ROWID, то PK является истинным PK и не требует дополнительной индексной таблицы. Одно только это изменение должно примерно вдвое сократить время, необходимое для импорта набора данных, и размер базы данных. (Если у вас есть другие индексы в таблице или вы используете этот PK в качестве внешнего ключа в другой таблице, это может не стоить вносить изменения в долгосрочной перспективе, так как это увеличит объем пространства, необходимого для этих таблиц, потенциально много, учитывая длину ваших ключей, в этом случае см. ответ Шверна) данные на этих страницах. Все идет на одну и ту же страницу, пока не заполнится, не будет выделена новая и не произведена необходимая перебалансировка.

Вы также можете включить некоторые небезопасные настройки, которые при обычном использовании не рекомендуются, поскольку они могут привести к потеря данных или прямое повреждение, но если это произойдет во время импорта из-за странного отключения электроэнергии или чего-то еще, вы всегда можете просто начать все сначала. В частности, для синхронного режима и типа журнала установлено значение OFF. Это приводит к уменьшению количества записей dis c в процессе импорта.

0 голосов
/ 21 февраля 2020

Следующие настройки помогли значительно ускорить процесс.

PRAGMA journal_mode = OFF
PRAGMA cache_size = 7500000
PRAGMA synchronous = 0
PRAGMA temp_store = 2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...