W tym badaniu chciałem zbadać, jakie poprawy wydajności można osiągnąć, używając źródła danych ClickHouse zamiast PostgreSQL. Znam korzyści wydajnościowe, jakie oferuje ClickHouse. Czy te zalety będą zachowane, jeśli uzyskam dostęp do ClickHouse z PostgreSQL za pomocą zewnętrznej powłoki danych (FDW)?
Badanymi środowiskami baz danych są PostgreSQL v11, clickhousedb_fdw i baza danych ClickHouse. Ostatecznie z PostgreSQL v11 wykonamy różne zapytania SQL, kierowane przez nasz clickhousedb_fdw do bazy danych ClickHouse. Następnie zobaczymy, jak wydajność FDW porównuje się z tymi samymi zapytaniami wykonywanymi w natywnym PostgreSQL i natywnym ClickHouse.
Baza danych Clickhouse
ClickHouse to system zarządzania bazami danych typu kolumnowego z otwartym kodem źródłowym, który może osiągać wydajność od 100 do 1000 razy szybciej niż tradycyjne podejścia do baz danych, zdolny do przetwarzania ponad miliarda wierszy w mniej niż sekundę.
Clickhousedb_fdw
clickhousedb_fdw to zewnętrzna powłoka danych dla bazy danych ClickHouse, lub FDW, będąca projektem z otwartym kodem źródłowym od Percona. .
.
Jak zobaczycie, to udostępnia FDW dla ClickHouse, które pozwala na SELECT from i INSERT INTO bazy danych ClickHouse z serwera PostgreSQL v11.
FDW wspiera zaawansowane funkcje, takie jak agregaty i łączenia. To znacznie zwiększa wydajność, wykorzystując zasoby zdalnego serwera do tych zasobożernych operacji.
Środowisko benchmarkowe
- Serwer Supermicro:
- Procesor Intel® Xeon® CPU E5-2683 v3 @ 2.00GHz
- 2 gniazda / 28 rdzeni / 56 wątków
- Pamięć: 256 GB RAM
- Przechowywanie: Samsung SM863 1.9TB SSD Enterprise
- System plików: ext4/xfs
- OS: Linux smblade01 4.15.0-42-generic #45~16.04.1-Ubuntu
- PostgreSQL: wersja 11
Testy benchmarkowe
Zamiast używać jakiegoś zbioru danych wygenerowanego przez maszynę do tego testu, użyliśmy danych „Wydajność czasu, informująca o czasie pracy operatora” z lat 1987-2018. Możesz uzyskać dostęp do danych .
Rozmiar bazy danych wynosi 85 GB, zapewniając jedną tabelę z 109 kolumnami.
Zapytania benchmarkowe
Oto zapytania, które użyłem do porównania ClickHouse, clickhousedb_fdw i PostgreSQL.
Q#
Zapytanie zawiera agregaty i 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 = 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
Oto wyniki każdego z zapytań przy wykonywaniu w różnych ustawieniach bazy danych: PostgreSQL z indeksami i bez nich, własny ClickHouse i clickhousedb_fdw. Czas jest podawany w milisekundach.
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: Czas wykonania zapytań użytych w benchmarku
Przeglądanie wyników
Wykres pokazuje czas wykonania zapytania w milisekundach, oś X pokazuje numer zapytania z powyższych tabel, a oś Y pokazuje czas wykonania w milisekundach. Wyniki ClickHouse i dane uzyskane z postgres za pomocą clickhousedb_fdw są pokazane. Z tabeli widać, że istnieje ogromna różnica między PostgreSQL a ClickHouse, ale minimalna różnica między ClickHouse a clickhousedb_fdw.

Ten wykres pokazuje różnicę między ClickhouseDB a clickhousedb_fdw. W większości zapytań koszty FDW nie są tak duże i są ledwo zauważalne, z wyjątkiem Q12. To zapytanie zawiera połączenia i klauzulę ORDER BY. Z powodu klauzuli ORDER BY GROUP/BY i ORDER BY nie opadają do ClickHouse.
W tabeli 2 widzimy skok czasu w zapytaniach Q12 i Q13. Powtarzam, jest to spowodowane użyciem klauzuli ORDER BY. Aby to potwierdzić, wykonałem zapytania Q-14 i Q-15 z klauzulą ORDER BY i bez niej. Bez klauzuli ORDER BY czas zakończenia wynosi 259 ms, a z klauzulą ORDER BY — 1364212. Dla debugowania tego zapytania wyjaśniam oba zapytania, a tutaj przedstawione są wyniki wyjaśnienia.
Q15: Bez klauzuli ORDER BY
bm=# WYJAŚNIJ SZCZEGÓŁOWO WYBIERZ a."Rok", c1/c2
Z (WYBIERZ "Rok", count(*)*1000 JAKO c1 Z fontime GDY "DepDelay" > 10 GRUPUJ PO "Rok") a
WEWNĘTRZNE DOŁĄCZENIE (WYBIERZ "Rok", count(*) AS c2 Z fontime GRUPUJ PO "Rok") b NA a."Rok"=b."Rok";Q15: Zapytanie bez klauzuli ORDER BY
PLAN ZAPYTU
Połączenie Hash (koszt=2250.00..128516.06 wierszy=50000000 szerokość=12)
Wyjście: fontime."Rok", (((count(*) * 1000)) / b.c2)
Unikalny Wewnętrzny: prawda Warunek Hash: (fontime."Rok" = b."Rok")
-> Skanowanie Zdalne (koszt=1.00..-1.00 wierszy=100000 szerokość=12)
Wyjście: fontime."Rok", ((count(*) * 1000))
Relacje: Agregacja na (fontime)
Zdalne SQL: WYBIERZ "Rok", (count(*) * 1000) Z "default".ontime GDY (("DepDelay" > 10)) GRUPUJ PO "Rok"
-> Hash (koszt=999.00..999.00 wierszy=100000 szerokość=12)
Wyjście: b.c2, b."Rok"
-> Skanowanie Podzapytania na b (koszt=1.00..999.00 wierszy=100000 szerokość=12)
Wyjście: b.c2, b."Rok"
-> Skanowanie Zdalne (koszt=1.00..-1.00 wierszy=100000 szerokość=12)
Wyjście: fontime_1."Rok", (count(*))
Relacje: Agregacja na (fontime)
Zdalne SQL: WYBIERZ "Rok", count(*) Z "default".ontime GRUPUJ PO "Rok"(16 wierszy)Q14: Zapytanie z klauzulą ORDER BY
bm=# WYJAŚNIJ SZCZEGÓŁOWO WYBIERZ a."Rok", c1/c2 Z (WYBIERZ "Rok", count(*)*1000 JAKO c1 Z fontime GDY "DepDelay" > 10 GRUPUJ PO "Rok") a
WEWNĘTRZNE DOŁĄCZENIE (WYBIERZ "Rok", count(*) jako c2 Z fontime GRUPUJ PO "Rok") b NA a."Rok"= b."Rok"
ZAMÓW PO a."Rok";Q14: Plan zapytania z klauzulą ORDER BY
PLAN ZAPYTU
Połączenie Merge (koszt=2.00..628498.02 wierszy=50000000 szerokość=12)
Wyjście: fontime."Rok", (((count(*) * 1000)) / (count(*)))
Unikalny Wewnętrzny: prawda Warunek Merge: (fontime."Rok" = fontime_1."Rok")
-> Agregacja Grupowa (koszt=1.00..499.01 wierszy=1 szerokość=12)
Wyjście: fontime."Rok", (count(*) * 1000)
Klucz grupujący: fontime."Rok"
-> Skanowanie Zdalne na public.fontime (koszt=1.00..-1.00 wierszy=100000 szerokość=4)
Zdalne SQL: WYBIERZ "Rok" Z "default".ontime GDY (("DepDelay" > 10))
ZAMÓW PO "Rok" ASC
-> Agregacja Grupowa (koszt=1.00..499.01 wierszy=1 szerokość=12)
Wyjście: fontime_1."Rok", count(*) Klucz grupujący: fontime_1."Rok"
-> Skanowanie Zdalne na public.fontime fontime_1 (koszt=1.00..-1.00 wierszy=100000 szerokość=4)
Zdalne SQL: WYBIERZ "Rok" Z "default".ontime ZAMÓW PO "Rok" ASC(16 wierszy)Wnioski
Wyniki tych eksperymentów pokazują, że ClickHouse oferuje naprawdę dobrą wydajność, a clickhousedb_fdw przynosi korzyści wydajności ClickHouse z PostgreSQL. Chociaż korzystanie z clickhousedb_fdw wiąże się z pewnymi kosztami, są one niewielkie i porównywalne z wydajnością osiąganą podczas naturalnego uruchamiania w bazie danych ClickHouse. Potwierdza to również, że fdw w PostgreSQL zapewnia znakomite wyniki.
Telegramowy czat o Clickhouse
Telegramowy czat o PostgreSQL
Źródło: habr.com
