Почему Solr намного быстрее, чем Postgres?
Недавно я переключился с Postgres на Solr и увидел, что наши запросы ускоряются примерно в 50 раз. Запросы, которые мы выполняем, включают несколько диапазонов, и наши данные - это списки транспортных средств. Например: "Найти все автомобили с пробегом < 50 000, $5000 <цена <$ 10 000, марка = Mazda..."
Я создал индексы для всех соответствующих столбцов в Postgres, так что это должно быть довольно справедливое сравнение. Рассматривая план запроса в Postgres, хотя он все еще использовал только один индекс, а затем сканировал (я полагаю, потому что он не мог использовать все различные индексы).
Насколько я понимаю, Postgres и Solr используют неопределенно похожие структуры данных (B-деревья), и они оба кешируют данные в памяти. Поэтому мне интересно, откуда такая большая разница в производительности.
Какие различия в архитектуре могут объяснить это?
5 ответов
Во-первых, Solr не использует B-деревья. Индекс Lucene (базовая библиотека, используемая Solr) состоит из сегментов, доступных только для чтения. Для каждого сегмента Lucene поддерживает словарь терминов, который состоит из списка терминов, которые появляются в сегменте, отсортированы лексикографически. Поиск термина в этом словаре терминов осуществляется с помощью бинарного поиска, поэтому стоимость поиска по одному термину составляет O(log(t))
где т - количество членов. Наоборот, использование индекса стандартной СУБД стоит O(log(d))
где d - количество документов. Когда много документов имеют одинаковое значение для некоторого поля, это может быть большой победой.
Более того, коммиттер Lucene Уве Шиндлер несколько лет назад добавил поддержку очень эффективных запросов числового диапазона. Для каждого значения числового поля Lucene хранит несколько значений с различной точностью. Это позволяет Lucene очень эффективно выполнять запросы диапазона. Поскольку ваш вариант использования, похоже, много использует запросы числовых диапазонов, это может объяснить, почему Solr намного быстрее. (Для получения дополнительной информации, прочитайте javadocs, которые очень интересны и дайте ссылки на соответствующие исследовательские работы.)
Но Solr может сделать это только потому, что не имеет всех ограничений, которые есть в СУБД. Например, Solr очень плохо обновляет один документ за раз (он предпочитает пакетные обновления).
Вы не особо много говорили о том, что вы сделали для настройки вашего экземпляра PostgreSQL или ваших запросов. Нет ничего необычного в том, чтобы ускорить запрос PostgreSQL в 50 раз за счет настройки и / или повторения запроса в формате, который лучше оптимизирует.
Только на этой неделе был рабочий отчет, который кто-то написал с использованием Java и нескольких запросов таким образом, который, исходя из того, как далеко он продвинулся за четыре часа, мог занять примерно месяц. (Нужно было выбрать пять разных таблиц, каждая с сотнями миллионов строк.) Я переписал его, используя несколько CTE и оконную функцию, чтобы он выполнялся менее чем за десять минут и генерировал желаемые результаты прямо из запроса. Это 4400-кратное ускорение.
Возможно, лучший ответ на ваш вопрос не имеет ничего общего с техническими подробностями о том, как можно выполнять поиск по каждому продукту, но больше связан с простотой использования для вашего конкретного случая использования. Очевидно, что вы смогли найти быстрый способ поиска с Solr с меньшими трудностями, чем PostgreSQL, и он может не привести ни к чему большему.
Я привожу короткий пример того, как в PostgreSQL может быть выполнен текстовый поиск по нескольким критериям, и как несколько небольших изменений могут существенно повлиять на производительность. Для простоты и простоты я просто запускаю " Войну и мир" в текстовой форме в тестовой базе данных, где каждый "документ" представляет собой одну текстовую строку. Подобные методы могут быть использованы для произвольных полей с использованием hstore
тип или JSON
столбцы, если данные должны быть определены свободно. Там, где есть отдельные столбцы с собственными индексами, преимущества использования индексов, как правило, гораздо больше.
-- Create the table.
-- In reality, I would probably make tsv NOT NULL,
-- but I'm keeping the example simple...
CREATE TABLE war_and_peace
(
lineno serial PRIMARY KEY,
linetext text NOT NULL,
tsv tsvector
);
-- Load from downloaded data into database.
COPY war_and_peace (linetext)
FROM '/home/kgrittn/Downloads/war-and-peace.txt';
-- "Digest" data to lexemes.
UPDATE war_and_peace
SET tsv = to_tsvector('english', linetext);
-- Index the lexemes using GiST.
-- To use GIN just replace "gist" below with "gin".
CREATE INDEX war_and_peace_tsv
ON war_and_peace
USING gist (tsv);
-- Make sure the database has statistics.
VACUUM ANALYZE war_and_peace;
После настройки для индексации я показываю несколько поисков с количеством строк и временем для обоих типов индексов:
-- Find lines with "gentlemen".
EXPLAIN ANALYZE
SELECT * FROM war_and_peace
WHERE tsv @@ to_tsquery('english', 'gentlemen');
84 строки, суть: 2,006 мс, джин: 0,194 мс
-- Find lines with "ladies".
EXPLAIN ANALYZE
SELECT * FROM war_and_peace
WHERE tsv @@ to_tsquery('english', 'ladies');
184 строки, суть: 3,549 мс, джин: 0,328 мс
-- Find lines with "ladies" and "gentlemen".
EXPLAIN ANALYZE
SELECT * FROM war_and_peace
WHERE tsv @@ to_tsquery('english', 'ladies & gentlemen');
1 строка, суть: 0,971 мс, джин: 0,104 мс
Теперь, так как индекс GIN был примерно в 10 раз быстрее индекса GiST, вы можете задаться вопросом, почему кто-то использует GiST для индексации текстовых данных. Ответ в том, что GiST обычно быстрее поддерживать. Поэтому, если ваши текстовые данные очень изменчивы, индекс GiST может выиграть при общей загрузке, тогда как индекс GIN выиграет, если вас интересует только время поиска или рабочая нагрузка, предназначенная в основном для чтения.
Без индекса вышеупомянутые запросы занимают от 17,943 мс до 23,397 мс, так как они должны сканировать всю таблицу и проверять совпадение в каждой строке.
Индексированный по GIN поиск строк как с "дамами", так и с "джентльменами" выполняется в 172 раза быстрее, чем сканирование таблиц в одной и той же базе данных. Очевидно, что преимущества индексации были бы более значительными при использовании больших документов, чем те, которые использовались для этого теста.
Настройка, конечно, разовая вещь. С триггером для поддержания tsv
столбец, любые сделанные изменения будут мгновенно доступны для поиска без изменения каких-либо настроек.
При медленном запросе PostgreSQL, если вы показываете структуру таблицы (включая индексы), проблемный запрос и результаты работы EXPLAIN ANALYZE
По вашему запросу кто-то почти всегда может определить проблему и предложить, как заставить ее работать быстрее.
ОБНОВЛЕНИЕ (9 декабря '16)
Я не упомянул то, что использовал для получения предыдущих версий, но исходя из даты, вероятно, это был основной выпуск 9.2. Я просто наткнулся на этот старый поток и попробовал его снова на том же оборудовании, используя версию 9.6.1, чтобы посмотреть, помогает ли какой-либо из промежуточных вариантов настройки производительности этому примеру. Запросы только для одного аргумента только увеличивали производительность примерно на 2%, но при поиске строк с "леди" и "джентльменами" скорость удваивалась примерно до 0,053 мс (т. Е. 53 микросекунды) при использовании индекса GIN (инвертированного).
Solr предназначен в первую очередь для поиска данных, а не для хранения. Это позволяет отказаться от большей части функциональности, требуемой от RDMS. Так что (или, скорее, lucene) концентрируется исключительно на индексации данных.
Как вы, без сомнения, обнаружили, Solr позволяет выполнять поиск и извлекать данные из его индекса. Это последняя (дополнительная) возможность, которая приводит к естественному вопросу... "Могу ли я использовать Solr в качестве базы данных?"
Ответ квалифицированный да, и я отсылаю вас к следующему:
- https://stackru.com/questions/5814050/solr-or-database
- Использование поискового индекса Solr в качестве базы данных - это "неправильно"?
- Для хранителя Solr это новая база данных
Мое личное мнение таково, что Solr лучше всего рассматривать как доступный для поиска кеш между моим приложением и данными, хранящимися в моей базе данных. Таким образом, я получаю лучшее из обоих миров.
Это самое большое отличие состоит в том, что индекс Lucene/Solr подобен базе данных с одной таблицей без какой-либо поддержки реляционных запросов (JOIN). Помните, что индекс обычно предназначен только для поддержки поиска, а не для того, чтобы быть основным источником данных. Таким образом, ваша база данных может быть в "третьей нормальной форме", но индекс будет полностью нормализован и будет содержать в основном только те данные, которые необходимо найти.
Другая возможная причина, как правило, заключается в том, что базы данных страдают от внутренней фрагментации: им нужно выполнять слишком много полуслучайных задач ввода-вывода при больших запросах.
Это означает, например, что, учитывая архитектуру индексов баз данных, запрос приводит к индексам, которые, в свою очередь, приводят к данным. Если данные для восстановления широко распространены, результат займет много времени, и похоже, что это происходит в базах данных.
Пожалуйста, прочитайте это и это.
Solr (Lucene) создает инвертированный индекс, где поиск данных происходит довольно быстро. Я читал, что PostgreSQL также имеет аналогичные возможности, но не уверен, что вы использовали это.
Наблюдаемые вами различия в производительности также можно объяснить "что ищут?", "Каковы пользовательские запросы?"