2010-11-26 10 views
11

Я читаю Hadoop: окончательный путеводитель Тома Уайта. В главе 13.6 «HBase vs RDMS» он сказал, что если у вас много данных, даже простые запросы, такие как получение 10 последних элементов, чрезвычайно дороги, и им пришлось переписать их с помощью python и PL/SQL.Являются ли RDBMS плохой, как описано в Hadoop: окончательное руководство?

Он дает следующий запрос в качестве примера:

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 , который выполнил хапсорт. ... В почти во всех случаях это обогнал родной реализации SQL и стратегия планировщика запроса ...

Ожидаемой perforamnce и expermiental результатов

Я не мог себе представить набор данных что вызовет такие проблемы, которые вы должны написать 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)?
+0

Нет, это не так. Какая база данных запрашивает полную таблицу один раз для каждого элемента в фильтре поля, объединяет все записи вместе, заказывает их, а затем делает ограничение в конце? – MkV 2010-11-27 00:41:01

ответ

6

Их утверждение о том, что планировщик запросов 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 индексируется, так что это делает именно так, как desired- идет обратно через индекс, применяет фильтр и останавливается, когда выходной сигнал достигает предел.

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

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

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

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

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

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

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

+3

То, что он, по-видимому, говорит, что планировщики запросов в RDBMSes плохие, лучше писать его на Python, но используя составленный пример, который не является репрезентативным для реальных планировщиков запросов, используемых фактическими RDBMS. – MkV 2010-11-27 00:23:08

1

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

1

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

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

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