Являются ли RDBMS такими плохими, как описано в Hadoop: полное руководство? - PullRequest
11 голосов
/ 27 ноября 2010

Я читаю 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)?

Ответы [ 4 ]

6 голосов
/ 27 ноября 2010

Их утверждение о том, что планировщик запросов RDMBS использует это решение для запроса, неверно, по крайней мере для Postgresql 9.0, и я должен представить его и для других платформ.Я провел быструю проверку с похожим запросом:

explain select * from client_attribute where client_attribute_type_code in ('UAG', 'RFR', 'IPA', 'FVD') order by client_attribute_id desc limit 10;

                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.00..0.93 rows=10 width=85)
   ->  Index Scan Backward using client_attribute_pkey on client_attribute  (cost=0.00..15516.47 rows=167234 width=85)
         Filter: (client_attribute_type_code = ANY ('{UAG,RFR,IPA,FVD}'::bpchar[]))
(3 rows)

Здесь client_attribute_id индексируется, так что он работает именно так, как нужно - идет назад по индексу, применяет фильтр и останавливается, когда выходной поток достигает предела.

Если столбец упорядочения не проиндексирован, требуется сканирование и сортировка таблицы, но только одно сканирование таблицы:

explain analyze select * from client_attribute where client_attribute_type_code in ('UAG', 'RFR', 'IPA', 'FVD') order by updated desc limit 10;

                                                              QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=13647.00..13647.03 rows=10 width=85) (actual time=180.961..180.964 rows=10 loops=1)
   ->  Sort  (cost=13647.00..14065.09 rows=167234 width=85) (actual time=180.960..180.961 rows=10 loops=1)
         Sort Key: updated
         Sort Method:  top-N heapsort  Memory: 26kB
         ->  Seq Scan on client_attribute  (cost=0.00..10033.14 rows=167234 width=85) (actual time=0.010..106.791 rows=208325 loops=1)
               Filter: (client_attribute_type_code = ANY ('{UAG,RFR,IPA,FVD}'::bpchar[]))

При этом используется динамическая сортировка для поддержки первых 10 результатов в течение всего процесса.последовательное сканирование, которое звучит так же, как решение, которое они написали сами.

4 голосов
/ 27 ноября 2010

Я не думаю, что Том Уайт говорит, что реляционные базы данных "плохие"; они не оптимальны для нереляционных данных, не основанных на множествах.

Давно известно, что графы глубинных объектов плохо подходят для реляционных баз данных. Они обычно встречаются в таких задачах, как CAD-представление геометрических данных, где сборки состоят из сборок сборок деталей. Цепочки ссылок действительно очень длинные.

Объектные и графические базы данных были решениями такого рода проблем, так как я знал о них в начале 90-х годов.

Реляционные базы данных превосходны для реляционных данных на основе множеств. Но все данные не попадают в эту категорию. Вот почему NoSQL приобретает популярность.

Я думаю, что это тот пример, который вы приводите.

1 голос
/ 27 ноября 2010

При использовании SQL или NoSQL производительность будет ужасной, если вы неправильно проектируете свои запросы.

Я бы исправил этот пример, добавив проверку на отметку времени в предложении where. Если у вас много данных, вы, вероятно, можете предположить, что самые последние 10 записей относятся к последней минуте - так зачем пытаться читать и сортировать все за последний месяц?

Я мог бы так же легко придумать тот же пример, чтобы NoSQL выглядел плохо, заявив, что, поскольку по умолчанию вы можете находить записи только по идентификатору, вам потребуется загрузить весь набор данных, чтобы найти нужную запись, игнорируя возможность установки создать различные вторичные / пользовательские индексы, которые позволят вам повысить производительность SQL для важных запросов.

1 голос
/ 27 ноября 2010

СУБД предназначена для запросов, о которых вы не задумывались.Как только вы точно определите, чего хотите, вы можете применить наиболее оптимальное решение.

...