Я читаю Hadoop: полное руководство Тома Уайта.В главе 13.6 «HBase vs RDMS» он сказал, что если у вас много данных, даже простые запросы, такие как получение 10 последних элементов, чрезвычайно дороги, и им пришлось переписывать их, используя python и PL / SQL.
Heв качестве примера приводит следующий запрос:
SELECT id, stamp, type FROM streams
WHERE type IN ('type1','type2','type3','type4',...,'typeN')
ORDER BY stamp DESC LIMIT 10 OFFSET 0;
И говорит: «Планировщик запросов СУБД обрабатывает этот запрос следующим образом:
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, который выполнял heapsort. ... Впочти во всех случаях это превзошло собственную реализацию SQL и стратегию планировщика запросов ...
Ожидаемые результаты и экспериментальные результаты
Я не мог себе представитьнабор данных, который вызовет такие проблемы, что вам нужно написать pl / python для правильного выполнения такого простого запроса. Поэтому я немного поиграл об этой проблеме и придумалg наблюдения:
Производительность такого запроса ограничена 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войти n)?