Тестирование производительности аналитических запросов в PostgreSQL, ClickHouse и clickhousedb_fdw (PostgreSQL)

В этом исследовании я хотел посмотреть, какие улучшения производительности можно получить, используя источник данных ClickHouse, а не PostgreSQL. Я знаю, какие преимущества производительности при использовании ClickHouse я получаю. Будут ли эти преимущества сохранены, если я получу доступ к ClickHouse из PostgreSQL с помощью внешней оболочки данных (FDW)?


Исследуемыми средами баз данных являются PostgreSQL v11, clickhousedb_fdw и база данных ClickHouse. В конечном счете, из PostgreSQL v11 мы будем запускать различные SQL-запросы, маршрутизируемые через наш clickhousedb_fdw в базу данных ClickHouse. Затем мы увидим, как производительность FDW сравнивается с теми же запросами, выполняемыми в нативном PostgreSQL и нативном ClickHouse.


База данных Clickhouse


ClickHouse — это система управления базами данных на основе колонок с открытым исходным кодом, которая может достигать производительности в 100-1000 раз быстрее, чем традиционные подходы к базам данных, способная обрабатывать более миллиарда строк менее чем за секунду.


Clickhousedb_fdw


clickhousedb_fdw — оболочка внешних данных базы данных ClickHouse, или FDW, является проектом с открытым исходным кодом от Percona. Вот ссылка на репозиторий проекта GitHub.


В марте я написал блог, который рассказывает вам больше о нашем FDW.


Как вы увидите, это обеспечивает FDW для ClickHouse, который позволяет SELECT from, и INSERT INTO, базу данных ClickHouse с сервера PostgreSQL v11.


FDW поддерживает расширенные функции, такие как aggregate и join. Это значительно повышает производительность за счет использования ресурсов удаленного сервера для этих ресурсоемких операций.


Benchmark environment



Benchmark tests


Вместо того, чтобы использовать какой-то набор данных, сгенерированный машиной, для этого теста, мы использовали данные «Производительность по времени, сообщаемая о времени работы оператора» с 1987 по 2018 год. Вы можете получить доступ к данным с помощью нашего скрипта, доступного здесь.


Размер базы данных составляет 85 ГБ, обеспечивая одну таблицу из 109 столбцов.


Benchmark Queries


Вот запросы, которые я использовал для сравнения ClickHouse, clickhousedb_fdw и PostgreSQL.


Q# Query Contains Aggregates and Group By
Q1 SELECT DayOfWeek, count(*) AS c FROM ontime WHERE Year >= 2000 AND Year <= 2008 GROUP BY DayOfWeek ORDER BY c DESC;
Q2 SELECT DayOfWeek, count(*) AS c FROM ontime WHERE DepDelay>10 AND Year >= 2000 AND Year <= 2008 GROUP BY DayOfWeek ORDER BY c DESC;
Q3 SELECT Origin, count(*) AS c FROM ontime WHERE DepDelay>10 AND Year >= 2000 AND Year <= 2008 GROUP BY Origin ORDER BY c DESC LIMIT 10;
Q4 SELECT Carrier, count() FROM ontime WHERE DepDelay>10 AND Year = 2007 GROUP BY Carrier ORDER BY count() DESC;
Q5 SELECT a.Carrier, c, c2, c1000/c2 as c3 FROM ( SELECT Carrier, count() AS c FROM ontime WHERE DepDelay>10 AND Year=2007 GROUP BY Carrier ) a INNER JOIN ( SELECT Carrier,count(*) AS c2 FROM ontime WHERE Year=2007 GROUP BY Carrier)b on a.Carrier=b.Carrier ORDER BY c3 DESC;
Q6 SELECT a.Carrier, c, c2, c1000/c2 as c3 FROM ( SELECT Carrier, count() AS c FROM ontime WHERE DepDelay>10 AND Year >= 2000 AND Year <= 2008 GROUP BY Carrier) a INNER JOIN ( SELECT Carrier, count(*) AS c2 FROM ontime WHERE Year >= 2000 AND Year <= 2008 GROUP BY Carrier ) b on a.Carrier=b.Carrier ORDER BY c3 DESC;
Q7 SELECT Carrier, avg(DepDelay) * 1000 AS c3 FROM ontime WHERE Year >= 2000 AND Year <= 2008 GROUP BY Carrier;
Q8 SELECT Year, avg(DepDelay) FROM ontime GROUP BY Year;
Q9 select Year, count(*) as c1 from ontime group by Year;
Q10 SELECT avg(cnt) FROM (SELECT Year,Month,count(*) AS cnt FROM ontime WHERE DepDel15=1 GROUP BY Year,Month) a;
Q11 select avg(c1) from (select Year,Month,count(*) as c1 from ontime group by Year,Month) a;
Q12 SELECT OriginCityName, DestCityName, count(*) AS c FROM ontime GROUP BY OriginCityName, DestCityName ORDER BY c DESC LIMIT 10;
Q13 SELECT OriginCityName, count(*) AS c FROM ontime GROUP BY OriginCityName ORDER BY c DESC LIMIT 10;
Query Contains Joins
Q14 SELECT a.Year, c1/c2 FROM ( select Year, count()1000 as c1 from ontime WHERE DepDelay>10 GROUP BY Year) a INNER JOIN (select Year, count(*) as c2 from ontime GROUP BY Year ) b on a.Year=b.Year ORDER BY a.Year;
Q15 SELECT a.”Year”, c1/c2 FROM ( select “Year”, count()1000 as c1 FROM fontime WHERE “DepDelay”>10 GROUP BY “Year”) a INNER JOIN (select “Year”, count(*) as c2 FROM fontime GROUP BY “Year” ) b on a.”Year”=b.”Year”;

Table-1: Queries used in benchmark


Query executions


Вот результаты каждого из запросов при выполнении в разных настройках базы данных: PostgreSQL с индексами и без них, собственный ClickHouse и clickhousedb_fdw. Время показывается в миллисекундах.


Q# PostgreSQL PostgreSQL (Indexed) ClickHouse clickhousedb_fdw
Q1 27920 19634 23 57
Q2 35124 17301 50 80
Q3 34046 15618 67 115
Q4 31632 7667 25 37
Q5 47220 8976 27 60
Q6 58233 24368 55 153
Q7 30566 13256 52 91
Q8 38309 60511 112 179
Q9 20674 37979 31 81
Q10 34990 20102 56 148
Q11 30489 51658 37 155
Q12 39357 33742 186 1333
Q13 29912 30709 101 384
Q14 54126 39913 124 1364212
Q15 97258 30211 245 259

Table-1: Time taken to execute the queries used in benchmark


Просмотр результатов


График показывает время выполнения запроса в миллисекундах, ось X показывает номер запроса из таблиц выше, а ось Y показывает время выполнения в миллисекундах. Результаты ClickHouse и данные, полученные из postgres с помощью clickhousedb_fdw, показаны. Из таблицы видно, что существует огромная разница между PostgreSQL и ClickHouse, но минимальная разница между ClickHouse и clickhousedb_fdw.



Этот график показывает разницу между ClickhouseDB и clickhousedb_fdw. В большинстве запросов накладные расходы FDW не так велики и едва ли значительны, кроме Q12. Этот запрос включает в себя объединения и предложение ORDER BY. Из-за предложения ORDER BY GROUP/BY и ORDER BY не опускаются до ClickHouse.


В таблице 2 мы видим скачок времени в запросах Q12 и Q13. Повторюсь, это вызвано предложением ORDER BY. Чтобы подтвердить это, я выполнил запросы Q-14 и Q-15 с предложением ORDER BY и без него. Без предложения ORDER BY время завершения составляет 259 мс, а с предложением ORDER BY — 1364212. Для отладки этого запроса я объясняю оба запроса, а здесь приведены результаты объяснения.


Q15: Without ORDER BY Clause


bm=# EXPLAIN VERBOSE SELECT a."Year", c1/c2 
     FROM (SELECT "Year", count(*)*1000 AS c1 FROM fontime WHERE "DepDelay" > 10 GROUP BY "Year") a
     INNER JOIN(SELECT "Year", count(*) AS c2 FROM fontime GROUP BY "Year") b ON a."Year"=b."Year";

Q15: Query Without ORDER BY Clause


QUERY PLAN                                                      
Hash Join  (cost=2250.00..128516.06 rows=50000000 width=12)  
Output: fontime."Year", (((count(*) * 1000)) / b.c2)  
Inner Unique: true   Hash Cond: (fontime."Year" = b."Year")  
->  Foreign Scan  (cost=1.00..-1.00 rows=100000 width=12)        
Output: fontime."Year", ((count(*) * 1000))        
Relations: Aggregate on (fontime)        
Remote SQL: SELECT "Year", (count(*) * 1000) FROM "default".ontime WHERE (("DepDelay" > 10)) GROUP BY "Year"  
->  Hash  (cost=999.00..999.00 rows=100000 width=12)        
Output: b.c2, b."Year"        
->  Subquery Scan on b  (cost=1.00..999.00 rows=100000 width=12)              
Output: b.c2, b."Year"              
->  Foreign Scan  (cost=1.00..-1.00 rows=100000 width=12)                    
Output: fontime_1."Year", (count(*))                    
Relations: Aggregate on (fontime)                    
Remote SQL: SELECT "Year", count(*) FROM "default".ontime GROUP BY "Year"(16 rows)

Q14: Query With ORDER BY Clause


bm=# EXPLAIN VERBOSE SELECT a."Year", c1/c2 FROM(SELECT "Year", count(*)*1000 AS c1 FROM fontime WHERE "DepDelay" > 10 GROUP BY "Year") a 
     INNER JOIN(SELECT "Year", count(*) as c2 FROM fontime GROUP BY "Year") b  ON a."Year"= b."Year" 
     ORDER BY a."Year";

Q14: Query Plan with ORDER BY Clause


QUERY PLAN 
Merge Join  (cost=2.00..628498.02 rows=50000000 width=12)   
Output: fontime."Year", (((count(*) * 1000)) / (count(*)))   
Inner Unique: true   Merge Cond: (fontime."Year" = fontime_1."Year")   
->  GroupAggregate  (cost=1.00..499.01 rows=1 width=12)        
Output: fontime."Year", (count(*) * 1000)         
Group Key: fontime."Year"         
->  Foreign Scan on public.fontime  (cost=1.00..-1.00 rows=100000 width=4)               
Remote SQL: SELECT "Year" FROM "default".ontime WHERE (("DepDelay" > 10)) 
            ORDER BY "Year" ASC   
->  GroupAggregate  (cost=1.00..499.01 rows=1 width=12)         
Output: fontime_1."Year", count(*)         Group Key: fontime_1."Year"         
->  Foreign Scan on public.fontime fontime_1  (cost=1.00..-1.00 rows=100000 width=4) 
              
Remote SQL: SELECT "Year" FROM "default".ontime ORDER BY "Year" ASC(16 rows)

Вывод


Результаты этих экспериментов показывают, что ClickHouse предлагает действительно хорошую производительность, а clickhousedb_fdw предлагает преимущества производительности ClickHouse из PostgreSQL. Хотя при использовании clickhousedb_fdw есть некоторые накладные расходы, они незначительны и сопоставимы с производительностью, достигнутой при естественном запуске в базе данных ClickHouse. Это также подтверждает, что fdw в PostgreSQL обеспечивает замечательные результаты.


Телеграм чат по Clickhouse https://t.me/clickhouse_ru
Телеграм чат по PostgreSQL https://t.me/pgsql

@chemtech
22.07.2020 12:23 UTC
Первоисточник

Комментарии

@viking_unet
22.07.2020 12:28 UTC
+4

ClickHouse — конечно быстрый. Но статья (понимаю, что перевод) нацелена на популяризацию clickhousedb_fdw его автором (кстати, что на счёт выброса при Q14?) с явным пренебрежением способностями PostgreSQL:


  1. никто так не хранит BigData в PostgreSQL, для этого есть расширения TimescaleDB и pg_pathman как раз для хранения больших временных рядов (логи, данные с датчиков, прочие факты во времени и статистические данные).
  2. индексы тоже можно создавать по разному и разного типа, так что большой индекс btree может вести себя неоднозначно на большой таблице (к примеру, если страницы индекса не помещаются в памяти)
  3. настройки postgres по умолчанию рассчитаны на "запуск даже на кофеварке", их нужно тюнить, хотя бы с помощью PgTune.
  4. для выборки агрегированных хитрых значений могут использоваться дополнительные расширения, к примеру, pipelineDB или другие механизмы.

Конечно, ClickHouse быстрый, и чаще всего разумнее будет не париться, а настроить именно его для хранилища больших данных — чтобы настроить postgres для bigdata, нужно потратить прилично времени, чтобы понять для чего и как это делать. Но бывает так, что таковы правила игры (проекта или легаси-инфраструктура), что есть только postgres и ничего другого быть не может).


Было бы здорово увидеть сравнение этих монстров не "из коробки", а на максимуме своих возможностей)) вот это был бы обзор. В статье есть ссылка на исходный датасет. Если у кого-то дойдут руки до сравнения — то это будет хороший материал для статьи!

@yleo
22.07.2020 21:12 UTC
+2

TL;DR — ClickHouse будет "как тузик грелку" PostgreSQL во всех своих целевых сценариях использования, ибо именно для этого CH был сделан. Т.е. чем ближе совокупность "данные + запросы" к назначению CH, тем ближе буду результаты сравнительных бенчмарков с PG к "как тузик грелку" (в два и более раз).


Такое моё утверждение может показаться слишком сильным и категоричным, но чем больше кто-либо будет погружаться во внутреннее устройство PG и CH, тем больше будет соглашаться. Причем это нисколько не принижает достоинства PG, просто не каждым микроскопом удобно забивать шурупы ;)


Если совсем грубо и коротко, то в своих целевых сценариях CH почти не делает лишнего и очень эффективно использует железо (SIMD, последовательное чтение, сжатие и т.п.). В таких сценариях Постгресу (мягко говоря) крайне сложно тягаться с CH, оставаясь при этом именно Постресом (сохраняя ACID поверх MVCC путем COW/ShadowPaging). Со стороны CH при этом отсутствуют транзакции и удаление/изменение данных в привычном для PG понимании. Т.е. CH тут быстрее, в частности, потому что много не позволяет.


Какого-либо пренебрежения к "возможностям PG" я не вижу. Точнее говоря, в моем понимании, посыл автора не в "принижении PG", а в демонстрации возможностей FDW. Другими словами, главным я вижу не "насколько PG медленнее CH" (в целевых для CH сценариях это неизбежно), а то что Clickhousedb_fdw позволяет через готовый PG-интерфейс (ODBC и т.д.) в дополнение к PG получить также и часть мощности CH. Причем во многих случаях с относительно небольшой наценкой.




Проседание Q14 показывает текущее положение дел в Clickhousedb_fdw, т.е. не готовность к production. Грубо говоря, Clickhousedb_fdw (пока) умеет "перебрасывать" на сторону CH только относительно простые join-ы (complex join pushdown NOT supported).


Поэтому, чуть более сложный join будет выполняться на стороне PG. Другими словами, если Clickhousedb_fdw не полностью справляется с трансляцией запроса, то сначала будут выполнены подзапросы на стороне CH, затем эти данные будут загружены внутрь PG и уже после будут join-ится. Проседание Q14 как раз хорошо показывает последствия.


Реализация complex join pushdown сводиться к волокитной трансляции из PG-диалекта SQL в CH-SQL. Это местами очень занудная, а местами нетривиальная задача. Причем если делать по-хорошему, то следует обеспечить разумно-оптимальную трансляцию "экс-территориальных" join-ов, когда в одном запросе используются как таблицы из CH, так и из PG, а желательно и из другого FDW. Короче, пока ребята из Percona это отложили, и (мне) понятно почему.

@chemtech
22.07.2020 13:55 UTC
+2

Статья перевод. На счет Q14 от себя ничего не могу сказать.


  1. Никто так не хранит BigData в PostgreSQL. Не обязательно должна быть BigData. Я в одной команде в живую видел как делали аналитику на чистом PostgreSQL. На счет TimescaleDB согласен, если там временные ряды. Но логи врят ли могу назвать временными рядами. Для данных с датчиков лучше использовать Prometheus/VictoriaMetrics

2-3-4 Согласен


Вы же можете сделать сравнение ClickHouse и PostgreSQL на максимуме своих возможностей. Сообщество будет вам благодарно.

@algotrader2013
22.07.2020 22:08 UTC
0

Вчитался в каждый запрос, но, увы, нет ни одного, который бы был вызовом для планировщика. А полтора года назад он был никакой и умел только банальные групбаи быстро считать, и сваливался в 1 поток, как только надо было двинутся на шаг вперёд.
Интересно, с того времени изменилось что-то? Есть ли бенчмарки СН на реально сложных запросах?

@edo1h
23.07.2020 00:26 UTC
0

а можно примеры этих самых запросов?
и ИМХО сложные запросы — это совсем не то, подо что разрабатывался CH.

23.07.2020 20:39 UTC
0

Банально, join большой колоночой таблицы самой на себя. Частый кейс, когда надо добрать фичей события за счет аггрегации аналогичных событий, предшествующих ему