Я читаю Hadoop: окончательный путеводитель Тома Уайта. В главе 13.6 "HBase vs RDMS" он сказал, что если у вас много данных, даже простые запросы, такие как получение 10 последних элементов, чрезвычайно дороги, и им пришлось переписать их с помощью python и PL/SQL.
В качестве примера он приводит следующий запрос:
SELECT id, stamp, type FROM streams
WHERE type IN ('type1','type2','type3','type4',...,'typeN')
ORDER BY stamp DESC LIMIT 10 OFFSET 0;
И говорит: "Планировщик запросов RDBMS рассматривает этот запрос следующим образом:
MERGE (
SELECT id, stamp, type FROM streams
WHERE type = 'type1' ORDER BY stamp DESC,
...,
SELECT id, stamp, type FROM streams
WHERE type = 'typeK' ORDER BY stamp DESC
) ORDER BY stamp DESC LIMIT 10 OFFSET 0;
Проблема в том, что мы после только 10 лучших идентификаторов, но запрос планировщик фактически материализует все слияние, а затем лимиты на конец..... Мы действительно зашли так далеко, как написать пользовательский PL/Python scriptкоторый выполнял хаппорт.... В почти во всех случаях это превзошло встроенная реализация SQL и стратегия планировщиков запросов...
Ожидаемые перфорименты и экспериментальные результаты
Я не мог себе представить набор данных, который вызовет такие проблемы, которые вы должны написать pl/python, чтобы сделать такой простой запрос. Поэтому я немного поболтал об этой проблеме и придумал следующие наблюдения:
Производительность такого запроса ограничена O (KlogN). Потому что это можно перевести так:
SELECT * FROM (
SELECT id, stamp, type FROM streams
WHERE type = 'type1' ORDER BY stamp DESC LIMIT 10,
UNION
...,
SELECT id, stamp, type FROM streams
WHERE type = 'typeK' ORDER BY stamp DESC LIMIT 10
) t ORDER BY stamp DESC LIMIT 10;
(обратите внимание на "LIMIT 10" в каждом запросе. Кстати, я знаю, что я не могу ограничивать и упорядочивать союзы, но я не разделял выборки для удобства чтения)
Каждый подзапрос должен выполняться так же быстро, как поиск правильной позиции в индексе O (logN) и возврат 10 элементов. Если мы повторим, что K раз, мы получаем O (KlogN).
И даже если планировщик запросов настолько плохой, что он не может оптимизировать первый запрос, мы всегда можем перевести его в запрос с объединениями и получить желаемую производительность без записи чего-либо в pl/python.
Чтобы дважды проверить мои вычисления, я выполнил запросы выше одного postgresql, заполненного 9 000 000 тестовых записей. Результаты подтвердили мои ожидания, что оба запроса были довольно быстрыми 100 мс для первого запроса и 300 мс для второго (одно с объединениями).
Итак, если запрос выполняется в 100 мс для 9 000 000 (logn = 23) записей, то для 9 000 000 000 (logn = 33) записей он должен работать в 140 мс.
Вопросы
- Вы видите недостатки в рассуждениях выше?
- Представляете ли вы набор данных, где вам нужно будет переписать такой запрос, как указано выше в файле pl/python?
- Вы видите какую-либо ситуацию, когда такой запрос не будет работать в O (K log n)?