Чрезвычайно медленные простые запросы SQLite с внешнего диска

У меня есть база данных SQLite на внешнем жестком диске, и я хотел бы проанализировать пару тысяч ее подмножеств в R (или Python). Данные представляют собой около 110 миллионов наблюдений за 13 переменными и хранятся (~ 10,5 ГБ) на внешнем диске, потому что на моем компьютере недостаточно места для хранения. Я решил попробовать использовать базу данных SQL, потому что, хотя у меня недостаточно оперативной памяти для работы со всем набором данных в R, мне нужен доступ только к небольшому его проценту в любой момент времени.

Я построил базу данных с помощью RSQLite — по сути, просто добавив несколько фреймов данных с помощью dbWriteTable() — и могу успешно запросить ее. Проблема в том, что даже очень простой запрос к базе данных занимает несколько минут. Например, это займет около 3 минут (либо в R, либо с sqlite3 в Python):

# example in R
library(RSQLite)

# connect to existing database
conn <- dbConnect(RSQLite::SQLite(), EEGdata.db)

# example of simple query
testQuery <- dbSendQuery(conn, "SELECT * FROM Data WHERE Time = 42")

# return data (takes "forever" but does work)
testdf <- dbFetch(testQuery)

Я пробовал несколько разных (но я думаю, эквивалентных) способов выполнения запроса, например, с помощью dbplyr, но все они одинаково выполняются за несколько минут.

Я пробовал использовать транзакции (но я так понимаю, это в основном просто помогает вставлять или изменять информацию?).

Я пытался использовать индекс, который ускоряет запросы, но их выполнение все равно занимает больше минуты.

Я пытался использовать последние версии R/RSQLite, как рекомендовано в: Запрос SQLilte через RSqlite намного медленнее, чем через интерфейс командной строки sqlite3

Я проверил, что общая скорость чтения/записи моего внешнего диска (WD, мой паспорт) не слишком низкая (он передает данные со скоростью около 100 Мбит/с).

Я также пытался использовать sqlite3 через Python и получаю примерно такие же очень медленные запросы.

Я предполагаю, что я мог сделать базу данных как-то неправильно, но непонятно, почему она все еще работает, просто очень плохо. На всякий случай, это пример того, как я создал базу данных:

library(RSQLite)

# open connection to new database
conn <- dbConnect(RSQLite::SQLite(), EEGdata.db)

# loop over data files
for (file in filenames){

  # load file (column names defined elsewhere)
  temp <- read.delim(file = file, sep = '\t', header = F, col.names = columns) 

  # do some cleaning, merging, etc...

  # add to database
  dbWriteTable(conn, "Data", temp, append = TRUE)
}

Поскольку проблема выглядит одинаково как в R, так и в Python, я предполагаю, что есть какая-то проблема с тем, как я создал базу данных SQLite, или с тем, что она хранится на внешнем диске.

Любые идеи о том, что может пойти не так, или что я мог бы попробовать?

РЕДАКТИРОВАНИЕ:

Спасибо за все мысли!

Что касается индексации, как упоминалось @Waldi и @r2evans, это действительно помогает - я не имел в виду, что это не так, или что я не буду использовать индекс. Для самого сложного запроса, который я бы сделал (который по-прежнему фильтруется только по 3 переменным), полное индексирование сокращает время с ~ 6 минут до ~ 1,5 минут. Таким образом, очевидно, что индексы стоит использовать, но они сами по себе, похоже, не объясняют медлительность.

Что касается меньшей версии базы данных (@DavidP), хорошая идея - я только что попробовал несколько вещей и обнаружил, что в этом случае фактический размер базы данных не имеет большого значения для скорости, а скорее размер данных. возвращается из запроса (в частности, количество строк). Но даже в небольших случаях это кажется медленным — например, возврат всего 900 строк со всеми полностью проиндексированными занимает 1,3 секунды. Что, насколько я могу судить, все еще довольно медленно для простого SQL-запроса.

Что касается скорости HD (@Waldi и @dnoeth) - я не хотел верить, что это просто аппаратная проблема, но это определенно возможно. Я посмотрю, смогу ли я найти где-нибудь SSD, чтобы проверить это, и отчитаюсь.

Дополнительная информация для @Parfait (и других): '''

sqlite> .schema Data
CREATE TABLE `Data` (
  `Item` INTEGER,
  `Amplitude` REAL,
  `Electrode` INTEGER,
  `Time` INTEGER,
  `Subject` TEXT,
  `Hstatus` TEXT,
  `Word` TEXT,
  `Zipf_freq` REAL,
  `OLD` REAL,
  `Concreteness` REAL,
  `Visual_complexity` REAL,
  `Letter_number` REAL,
  `Morpheme_number` REAL
);
CREATE INDEX time_idx ON Data (Time);
CREATE INDEX idx ON Data (Hstatus, Electrode, Time);

'''

Что касается отказа от использования SELECT * - я пытался выбрать только одну переменную, а не все, и это не улучшило скорость. (а в этом приложении я уже вырезал ненужную мне информацию, поэтому кроме индексных переменных нужно выделить все).


person kwinsler    schedule 28.09.2020    source источник
comment
Индекс на Time кажется разумным: вы не заметили никаких улучшений? Узким местом, вероятно, является скорость чтения на внешнем HD.   -  person Waldi    schedule 28.09.2020
comment
Да, добавление индекса не дает большого улучшения (по крайней мере, не в желаемом масштабе минут). Я согласен, что это, кажется, указывает на узкое место на HD, но из того, что я могу сказать (из тестирования с приложением - Black Magic), скорость чтения с моего HD составляет около 100 МБ, что не должно быть таким узким местом.   -  person kwinsler    schedule 28.09.2020
comment
Пробовали ли вы создать файл базы данных меньшего размера (некоторое подмножество исходных файлов) и сравнить производительность? Если база вдвое меньшего размера занимает столько же времени, мне кажется, что дело скорее в диске. Я не так хорошо знаком с sqlite, нужно ли читать весь файл базы данных или большой его набор для запуска базы данных? Это может снизить производительность вашего одиночного запроса.   -  person DavidP    schedule 28.09.2020
comment
скорость чтения с моего жесткого диска составляет около 100 Мбит/с Похоже, это жесткий диск. Ну, это, вероятно, последовательное чтение больших блоков, но шаблон доступа СУБД обычно не последовательный, а случайный (даже если вы единственный пользователь, блоки данных, вероятно, не в последовательном порядке). Переход на SSD значительно увеличивает количество операций ввода-вывода в секунду.   -  person dnoeth    schedule 28.09.2020
comment
С другой стороны, несмотря на то, что SQLite — гигантская и стабильная система, новичок DuckDB продемонстрировал некоторые преимущества в скорости по сравнению с SQLite. Удобно, что он может работать нативно с DBI (хотя может работать и независимо), поэтому ваш код не должен требовать изменений (только перенос данных из файла в файл).   -  person r2evans    schedule 28.09.2020
comment
Покажите пожалуйста схему таблицы. С интерфейсом командной строки: .schema Data. В качестве первого совета избегайте SELECT * FROM и явно выбирайте нужные столбцы. Возможно, вы даже сможете локализовать проблемный столбец.   -  person Parfait    schedule 28.09.2020
comment
Спасибо всем за ваши мысли! Я добавил больше информации и ответов в качестве редактирования вопроса.   -  person kwinsler    schedule 29.09.2020
comment
Вы создали многостолбцовый индекс для (Hstatus, Electrode, Time), добавили ли вы его в свой запрос? (Индекс работает только в том случае, если запрос включает столбцы в некоторой способности фильтрации.)   -  person r2evans    schedule 29.09.2020
comment
Да, на практике мои запросы будут фильтроваться по этим трем столбцам. С многостолбцовым индексом запрос по-прежнему занимает около 1,5 минут.   -  person kwinsler    schedule 29.09.2020
comment
Вам нужно использовать транзакции. Фиксируйте только после добавления большого количества строк, это значительно увеличит скорость записи   -  person mvp    schedule 29.09.2020


Ответы (1)


(кроме внешнего жесткого диска, который определенно является узким местом). У меня была аналогичная проблема, и она была связана с самой базой данных sqlite. Вы можете переключиться на DuckDB и использовать тот же код запроса. Для меня это было более чем в 10 раз быстрее для файла немного большего размера со 100 миллионами строк. https://duckdb.org/docs/api/r

person HCAI    schedule 26.02.2021