SQLite: переупорядочить физическое положение строк внутри файла - PullRequest
0 голосов
/ 21 января 2019

Моя проблема в том, что мои запросы слишком медленные.

У меня довольно большая база данных sqlite.Таблица:

CREATE TABLE results (
    timestamp TEXT,
    name TEXT,
    result float,
)

(я знаю, что временные метки как TEXT не оптимальны, но, пожалуйста, игнорируйте это для целей этого вопроса. Я должен исправить это, когда у меня будет время)

«имя» - это категория.Это вычисление содержит результаты вычисления, которое должно быть сделано в каждой отметке времени для всех «имен».Таким образом, вставки выполняются с одинаковыми временными метками, но запросы будут выполняться с одинаковыми именами (то есть, я хочу дать имя, получить его временной ряд), например:

SELECT timestamp,result WHERE name='some_name';

Теперь, как яЯ делаю вещи сейчас, чтобы не иметь индексов, рассчитать все результаты, а затем создать индекс на имя CREATE INDEX index_name ON results (name).Причина в том, что мне не нужен индекс при вставке, но наличие индекса сделает запросы на индекс очень быстрыми.

Но это не так.База данных довольно большая.У него около полумиллиона меток времени, и для каждой метки времени у меня есть около 1000 имен.

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

timestamp1,name1,result
timestamp1,name2,result
timestamp1,name3,result
...
timestamp1,name999,result
timestamp1,name1000,result
timestamp2,name1,result
timestamp2,name2,result
etc...

Я уверен, что запрос с NAME = 'some_name' медленнее, чем если бы строки были физически упорядочены как:

timestamp1,name1,result
timestamp2,name1,result
timestamp3,name1,result
...
timestamp499997,name1000,result
timestamp499998,name1000,result
timestamp499999,name1000,result
timestamp500000,namee1000,result
etc...

Итак, как мне это сделать?скажите SQLite, что порядок, в котором я бы хотел, чтобы строки на диске были не тем, в котором они были записаны?

ОБНОВЛЕНИЕ: я также убежден, что медлительность выбора с таким индексом возникаетисключительно от несмежного доступа к диску.Выполнение SELECT * FROM results WHERE name=<something_that_doesnt_exist> немедленно возвращает ноль результатов.Это говорит о том, что он не находит медленные имена, а читает их с диска.

1 Ответ

0 голосов
/ 21 января 2019

Обычные таблицы sqlite имеют в качестве первичного ключа 64-разрядное целое число (известное как rowid и несколько других псевдонимов). Это определяет порядок, в котором строки хранятся в B * -дереве (которое помещает все фактические данные в страницы конечных узлов). Вы можете изменить это с помощью таблицы БЕЗ ROWID , но для этого требуется явный первичный ключ, который используется для размещения строк в B-дереве. Поэтому, если (name, timestamp) столбцы каждой строки имеют уникальное значение, это означает, что все строки с одинаковыми именами останутся на меньшем наборе страниц, а не разбросаны по всему.

Вы бы хотели, чтобы составной ПК был в таком порядке, если вы ищете определенное имя большую часть времени, поэтому что-то вроде:

CREATE TABLE results (
    timestamp TEXT
  , name TEXT
  , result REAL
  , PRIMARY KEY (name, timestamp)
) WITHOUT ROWID

(И, конечно, не беспокоиться о втором индексе имени.) Компромисс заключается в том, что вставки, вероятно, будут медленнее, так как увеличивается вероятность необходимости разделить страницу в B-дереве.

Некоторые прагмы, на которые стоит обратить внимание, чтобы настроить вещи:

Поскольку у вас нет INTEGER PRIMARY KEY, рассмотрите VACUUM после удаления большого количества строк, если вы когда-либо это сделаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...