Zabbix to system monitorowania. Jak każda inna system, stoi przed trzema głównymi problemami wszystkich systemów monitorowania: zbieranie i przetwarzanie danych, przechowywanie historii oraz jej czyszczenie.
Etapy pozyskiwania, przetwarzania i zapisu danych zajmują czas. Trochę, ale w dużym systemie może to prowadzić do znacznych opóźnień. Problem przechowywania to kwestia dostępu do danych. Są one wykorzystywane do raportów, inspekcji i triggerów. Opóźnienia w dostępie do danych również wpływają na wydajność. Gdy bazy danych rosną, nieaktualne dane muszą być usuwane. Usunięcie jest operacją obciążającą, która również zużywa część zasobów.

Problemy z opóźnieniami przy zbieraniu i przechowywaniu w Zabbixie są rozwiązywane za pomocą cache'owania: różnych typów cache'ów, cache'owania w bazie danych. Aby rozwiązać trzeci problem, cache'owanie nie jest odpowiednie, dlatego w Zabbixie zastosowano TimescaleDB. O tym opowie Andriej Guścin — inżynier wsparcia technicznego . W wsparciu Zabbix Andriej pracuje od ponad 6 lat i bezpośrednio zajmuje się wydajnością.
Jak działa TimescaleDB, jaką wydajność może zapewnić w porównaniu do zwykłego PostgreSQL? Jaką rolę odgrywa Zabbix dla baz danych TimescaleDB? Jak zacząć od zera i jak migrować z PostgreSQL, a jaka konfiguracja daje lepszą wydajność? O tym wszystkim poniżej.

Wyzwania wydajności
Każdy system monitorowania napotyka określone wyzwania wydajności. Opowiem o trzech z nich: zbieraniu i przetwarzaniu danych, przechowywaniu, czyszczeniu historii.
Szybkie zbieranie i przetwarzanie danych. Dobra system monitorowania musi szybko pozyskiwać wszystkie dane i przetwarzać je zgodnie z wyrażeniami triggerów — według swoich kryteriów. Po przetworzeniu system musi również szybko zapisać te dane w bazie danych, aby później móc je wykorzystać.
Przechowywanie historii. Dobra system monitorowania musi przechowywać historię w bazie danych i zapewniać wygodny dostęp do metryk. Historia jest potrzebna do wykorzystywania jej w raportach, wykresach, triggerach, wartościach progowych oraz obliczanych elementach danych do powiadomień.
Czyszczenie historii. Czasami nadchodzi dzień, kiedy nie musisz przechowywać metryk. Po co ci dane zebrane pięć lat temu, miesiąc czy dwa: niektóre węzły zostały usunięte, niektóre hosty lub metryki są już niepotrzebne, ponieważ stały się przestarzałe i przestały być zbierane. Dobrze zaprojektowany system monitorowania powinien przechowywać dane historyczne i okresowo je usuwać, aby baza danych nie rozrosła się.
Usuwanie przestarzałych danych to ważna kwestia, która w znaczący sposób wpływa na wydajność bazy danych.
Cache'owanie w Zabbixie
W Zabbixie pierwsze i drugie wywołania są realizowane za pomocą cache'owania. Do zbierania i przetwarzania danych używana jest pamięć operacyjna. W celu przechowywania danych historia znajduje się w wyzwalaczach, wykresach i obliczanych elementach danych. Po stronie bazy danych istnieje pewne cache'owanie dla głównych zapytań, na przykład dla wykresów.
Cache'owanie po stronie samego serwera Zabbix to:
- ConfigurationCache;
- ValueCache;
- HistoryCache;
- TrendsCache.
Przyjrzyjmy się im bliżej.
ConfigurationCache
To główny cache, w którym przechowujemy metryki, hosty, elementy danych, wyzwalacze – wszystko, co jest potrzebne do wstępnego przetwarzania i zbierania danych.

Wszystko to jest przechowywane w ConfigurationCache, aby uniknąć zbędnych zapytań do bazy danych. Po uruchomieniu serwera aktualizujemy ten cache, tworzymy i okresowo aktualizujemy konfiguracje.
Zbieranie danych
Schemat jest dość rozbudowany, ale jego głównym elementem są zbieracze. To różne „pollery” – procesy zbierania. Odpowiadają za różne rodzaje zbierania danych: zbierają dane przez SNMP, IPMI i przesyłają je do wstępnego przetwarzania.
Zbieracze są zaznaczone pomarańczową linią.
W Zabbixie istnieją obliczeniowe elementy danych agregacyjnych, które są potrzebne do agregacji sprawdzeń. Jeśli je mamy, pobieramy dane dla nich bezpośrednio z ValueCache.
PreProcessing HistoryCache
Wszystkie zbieracze używają ConfigurationCache, aby otrzymywać zadania. Następnie przekazują je do wstępnego przetwarzania.

Wstępne przetwarzanie wykorzystuje ConfigurationCache do otrzymywania kroków przetwarzania. Przetwarza dane na różne sposoby.
Po przetworzeniu danych za pomocą wstępnego przetwarzania, zapisujemy je w HistoryCache, aby je przetworzyć. Na tym kończy się zbieranie danych i przechodzimy do głównego procesu w Zabbixie – history syncer, ponieważ jest to monolityczna architektura.
Uwaga: Wstępne przetwarzanie to dość wymagająca operacja. Od wersji 4.2 zostało przeniesione na proxy. Jeśli masz bardzo duży Zabbix z wieloma elementami danych i wysoką częstotliwością zbierania, znacznie to ułatwia pracę.
ValueCache, cache historii i trendów
History syncer to główny proces, który atomowo przetwarza każdy element danych, czyli każdą wartość.
History syncer pobiera wartości z HistoryCache i sprawdza w Configuration obecność wyzwalaczy do obliczeń. Jeśli są, to wykonuje obliczenia.
History syncer tworzy zdarzenie, eskalację, aby zrealizować powiadomienia, jeśli jest to wymagane przez konfigurację, i zapisuje. Jeśli istnieją wyzwalacze do dalszego przetwarzania, zapamiętuje tę wartość w ValueCache, aby uniknąć ponownego dostępu do tabeli historii. W ten sposób ValueCache wypełnia się danymi niezbędnymi do obliczeń wyzwalaczy, obliczanych elementów.
History syncer zapisuje wszystkie dane w bazie danych, a ta — na dysku. Proces przetwarzania na tym się kończy.

Cache w bazie danych
Po stronie bazy danych istnieją różne cache, gdy chcesz oglądać wykresy lub raporty dotyczące zdarzeń:
Innodb_buffer_poolpo stronie MySQL;shared_bufferspo stronie PostgreSQL;effective_cache_sizepo stronie Oracle;shared_poolpo stronie DB2.
Jest jeszcze wiele innych cache, ale to są główne dla wszystkich baz danych. Pozwalają one na trzymanie w pamięci operacyjnej danych, które często są potrzebne do zapytań. Każdy z nich ma swoje technologie do tego.
Wydajność bazy danych jest krytycznie ważna
Zabbix serwer ciągle zbiera dane i je zapisuje. Przy restarcie również odczytuje z historii, aby wypełnić ValueCache. Używa skryptów i raportów Zabbix API, które są oparte na interfejsie Web. Zabbix API zwraca się do bazy danych i pozyskuje niezbędne dane do wykresów, raportów, list zdarzeń i aktualnych problemów.

Do wizualizacji — Grafana. Wśród naszych użytkowników to popularne rozwiązanie. Umożliwia bezpośrednie wysyłanie zapytań przez Zabbix API, zarówno w BD, jak i tworzy pewną konkurencję do uzyskiwania danych. Dlatego wymagana jest bardziej precyzyjna i dobra konfiguracja BD, aby spełniać szybkie wydawanie wyników i testowania.
Housekeeper
Trzecim wyzwaniem wydajności w Zabbix jest czyszczenie historii za pomocą Housekeeper. Przestrzega on wszystkich ustawień — w elementach danych określono, ile dni przechowywać dynamikę zmian (trendów).
TrendsCache obliczamy na bieżąco. Gdy pojawiają się dane, agregujemy je za jeden godzinę i zapisujemy w tabelach do dynamiki zmian trendów.
Housekeeper uruchamia się i usuwa informacje z BD zwykłymi „selectami”. To nie zawsze jest wydajne, co można zauważyć na wykresach wydajności procesów wewnętrznych.

Czerwony wykres pokazuje, że History syncer jest cały czas zajęty. Pomarańczowy wykres u góry to Housekeeper, który nieustannie działa. Czeka na bazę danych, aby usunęła wszystkie wiersze, które zdefiniował.
Kiedy należy wyłączyć Housekeepera? Na przykład, jeśli mamy „Item ID” i trzeba usunąć ostatnie 5 tysięcy wierszy z określonego okresu. Oczywiście odbywa się to na podstawie indeksów. Jednak zwykle zestaw danych jest bardzo duży, a baza danych nadal odczytuje z dysku i ładuje do pamięci podręcznej. To zawsze bardzo kosztowna operacja dla bazy danych i w zależności od rozmiarów bazy może prowadzić do problemów z wydajnością.

Housekeepera można po prostu wyłączyć. W interfejsie internetowym jest opcja w „Administracja ogólna” dla Housekeepera. Wyłączamy wewnętrzny housekeeping dla historii trendów, a on już tym nie zarządza.
Housekeeper został wyłączony, wykresy się wyrównały — jakie mogą być w tym przypadku problemy i co może pomóc w rozwiązaniu trzeciego wezwania wydajności?
Partitioning — sekcjonowanie lub partycjonowanie
Zwykle partycjonowanie konfiguruje się w różny sposób w każdej relacyjnej bazie danych, którą wymieniłem. Każda ma swoją technologię, ale są podobne ogólnie. Tworzenie nowej partycji często prowadzi do określonych problemów.
Zwykle partycje konfiguruje się w zależności od „setup” — ilości danych, które są generowane w ciągu jednego dnia. Z reguły partycjonowanie ustawia się na cały dzień, to minimum. Dla trendów nowa partycja to minimum 1 miesiąc.
Wartości mogą się zmieniać w przypadku bardzo dużego „setup”. Jeśli mały „setup” to do 5 000 nvps (nowych wartości na sekundę), średni to od 5 000 do 25 000, a duży to powyżej 25 000 nvps. To duże i bardzo duże instalacje, które wymagają starannej konfiguracji samej bazy danych.
W bardzo dużych instalacjach odcinek jednego dnia może nie być optymalny. Widziałem w MySQL partycje o pojemności 40 GB i większej na dzień. To bardzo duża ilość danych, która może prowadzić do problemów, więc należy ją zmniejszyć.
Co daje Partitioning?
Sekcjonowanie tabel. Często są to oddzielne pliki na dysku. Plan zapytań wybiera jedną partycję w sposób bardziej optymalny. Zwykle partycjonowanie odbywa się w oparciu o zakres — dla Zabbixa też jest to prawda. Używamy tam „timestamp” — czas od początku epoki. U nas to zwykłe liczby. Ustalasz początek i koniec dnia — to jest partycja.
Szybkie usuwanie — USUŃWybierany jest jeden plik/subtabela, a nie zbiór wierszy do usunięcia.
Wyraźnie przyspiesza pobieranie danych. SELECT — wykorzystuje jedną lub więcej partycji, a nie całą tabelę. Jeśli odwołujesz się do danych sprzed dwóch dni, są one wybierane z bazy danych szybciej, ponieważ trzeba załadować do cache i wydobyć tylko jeden plik, a nie dużą tabelę.
Często wiele baz danych również przyspiesza INSERT — wstawki do tabeli podrzędnej.
TimescaleDB
Dla wersji 4.2 zwróciliśmy uwagę na TimescaleDB. Jest to rozszerzenie dla PostgreSQL z natywnym interfejsem. Rozszerzenie skutecznie działa z danymi szeregów czasowych, nie tracąc przy tym zalet relacyjnych baz danych. TimescaleDB automatycznie partycjonuje również.
W TimescaleDB istnieje pojęcie hipertabela (hypertable), którą tworzysz. Znajdują się w niej fragmenty — partycje. Fragmenty to automatycznie zarządzane kawałki hipertabeli, które nie wpływają na inne fragmenty. Dla każdego fragmentu obowiązuje własny zakres czasowy.

TimescaleDB vs PostgreSQL
TimescaleDB działa naprawdę efektywnie. Producenci rozszerzenia twierdzą, że używają bardziej odpowiedniego algorytmu przetwarzania zapytań, w szczególności <code>wstawek</code>. Gdy rozmiary wstawek datasetu rosną, algorytm utrzymuje stałą wydajność.

Po przekroczeniu 200 milionów wierszy PostgreSQL zazwyczaj zaczyna mocno tracić wydajność, aż do zera. TimescaleDB pozwala efektywnie wstawiać «wstawki» przy dowolnej ilości danych.
Instalacja
Instalacja TimescaleDB jest wystarczająco prosta dla wszystkich pakietów. W wszystko jest opisane szczegółowo — zależy od oficjalnych pakietów PostgreSQL. TimescaleDB można również zbudować i skompilować ręcznie.
Dla bazy danych Zabbix po prostu aktywujemy rozszerzenie:
echo "CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;" | sudo -u postgres psql zabbix Aktywujesz rozszerzenie i tworzysz je dla bazy danych Zabbix. Ostatnim krokiem jest stworzenie hipertabeli.
Migracja tabel historii do TimescaleDB
Dla tego jest specjalna funkcja create_hypertable:
SELECT create_hypertable(‘history’, ‘clock’, chunk_time_interval => 86400, migrate_data => true);
SELECT create_hypertable(‘history_unit’, ‘clock’, chunk_time_interval => 86400, migrate_data => true);
SELECT create_hypertable(‘history_log’, ‘clock’, chunk_time_interval => 86400, migrate_data => true);
SELECT create_hypertable(‘history_text’, ‘clock’, chunk_time_interval => 86400, migrate_data => true);
SELECT create_hypertable(‘history_str’, ‘clock’, chunk_time_interval => 86400, migrate_data => true);
SELECT create_hypertable(‘trends’, ‘clock’, chunk_time_interval => 86400, migrate_data => true);
SELECT create_hypertable(‘trends_unit’, ‘clock’, chunk_time_interval => 86400, migrate_data => true);
UPDATE config SET db_extension=’timescaledb’, hk_history_global=1, hk_trends_global=1 Funkcja ma trzy parametry. Pierwszy — tabela w bazie danych, dla której należy utworzyć hipertabelę. Drugi — pole, na podstawie którego należy utworzyć chunk_time_interval — interwał chunków partycji, które należy używać. W moim przypadku interwał to jeden dzień — 86 400.
Trzeci parametr — migracja_danych. Jeśli ustawisz true, wszystkie istniejące dane zostaną przeniesione do wcześniej stworzonych chunków. Osobiście używałem migracja_danych. Miałem około 1 TB, co zajęło ponad godzinę. Nawet w niektórych przypadkach podczas testów usuwałem niepotrzebne dane historyczne typu znakowego, aby ich nie przenosić.
Ostatni krok — UPDATE: w db_extension ustawiamy timescaledb, aby baza danych rozumiała, że istnieje to rozszerzenie. Zabbix go aktywuje i poprawnie używa składni oraz zapytań już do bazy danych — tych funkcji, które są niezbędne dla TimescaleDB.
Konfiguracja sprzętu
Używałem dwóch serwerów. Pierwszy — maszyna VMware. Jest dość mała: 20 procesorów Intel® Xeon® CPU E5-2630 v 4 @ 2.20GHz, 16 GB pamięci RAM i dysk SSD o pojemności 200 GB.
Zainstalowałem na niej PostgreSQL 10.8 z systemem operacyjnym Debian 10.8-1.pgdg90+1 oraz systemem plików xfs. Wszystko zostało minimalnie skonfigurowane, aby używać tej konkretnej bazy danych, z wyjątkiem tego, co będzie używać sam Zabbix.
Na tej samej maszynie znajdował się serwer Zabbix, PostgreSQL i agenci obciążenia. Miałem 50 aktywnych agentów, którzy używali LoadableModule, aby bardzo szybko generować różne wyniki: liczby, napisy. Zasilałem bazę dużą ilością danych.
Początkowa konfiguracja zawierała 5 000 elementów danych na każdy host. Prawie każdy element zawierał wyzwalacz, aby to przypominało rzeczywiste instalacje. W niektórych przypadkach było więcej niż jeden wyzwalacz. Na jeden węzeł sieci przypadało 3 000-7 000 wyzwalaczy.
Interwał aktualizacji elementów danych — 4-7 sekundObciążałem system, używając nie tylko 50 agentów, ale także dodając ich więcej. Ponadto, za pomocą elementów danych dynamicznych regulowałem obciążenie i zmniejszałem interwał aktualizacji do 4 s.
PostgreSQL. 35 000 nvps
Pierwsze uruchomienie na tym sprzęcie miało miejsce z czystym PostgreSQL — 35 tys. wartości na sekundę. Jak widać, wstawianie danych zajmuje ułamki sekundy — wszystko jest w porządku i szybko. Jedynym problemem jest to, że dysk SSD o pojemności 200 GB szybko się zapełnia.

To standardowy dashboard wydajności Zabbix — serwery.

Pierwszy niebieski wykres — liczba wartości na sekundę. Drugi wykres po prawej — obciążenie procesów zbierających. Trzeci — obciążenie wewnętrznych procesów zbierających: syncery historii i Housekeeper, który był tu aktywny przez całkiem długi czas.
Czwarty wykres pokazuje wykorzystanie HistoryCache. To pewnego rodzaju bufor przed wstawianiem do bazy danych. Zielony piąty wykres pokazuje użycie ValueCache, czyli ile hitów ValueCache dla wyzwalaczy — to kilka tysięcy wartości na sekundę.
PostgreSQL. 50 000 nvps
Następnie zwiększyłem obciążenie do 50 tys. wartości na sekundę na tym samym sprzęcie.

Podczas ładowania z Housekeeperem wstawienie 10 tys. wartości zajmowało 2-3 s.

Housekeeper zaczyna już przeszkadzać w pracy.
Na trzecim wykresie widać, że ogólnie obciążenie trapperów i syncerów historii nadal utrzymuje się na poziomie 60%. Na czwartym wykresie HistoryCache w czasie pracy Housekeepera zaczyna być dość aktywnie zapełniony. Zapełnił się w 20% — to około 0,5 GB.
PostgreSQL. 80 000 nvps
Następnie zwiększyłem obciążenie do 80 tys. wartości na sekundę. To około 400 tys. elementów danych i 280 tys. wyzwalaczy.

Wstawienie przy obciążeniu trzydziestu syncerów historii jest już dość wysokie.
Zwiększałem także różne parametry: syncery historii, cache.

Na moim sprzęcie obciążenie syncerów historii osiągało maksimum. HistoryCache szybko zapełniał się danymi — w buforze zgromadziły się dane do przetworzenia.
Przez cały ten czas obserwowałem, jak wykorzystywane są procesor, pamięć RAM i inne parametry systemu, i odkryłem, że wykorzystanie dysków było maksymalne.

Uzyskałem wykorzystanie maksymalnych możliwości dysku na tym sprzęcie i na tej maszynie wirtualnej. Przy takiej intensywności PostgreSQL zaczął aktywnie zrzucać dane, a dysk nie nadążał już z operacjami zapisu i odczytu.
Drugi serwer
Wziąłem inny serwer, który miał już 48 procesorów i 128 GB pamięci RAM. Dostosowałem go – zainstalowałem 60 history syncer i osiągnąłem przyzwoitą wydajność.

W rzeczywistości to już granica wydajności, gdzie trzeba podjąć jakieś działania.
TimescaleDB. 80 000 nvps
Moim głównym celem jest przetestować możliwości TimescaleDB pod obciążeniem Zabbixa. 80 tysięcy wartości na sekundę to dużo, częstotliwość zbierania metryk (poza Yandexem, oczywiście) oraz znaczny „setup”.

Na każdym wykresie są spadki — to właśnie migracja danych. Po spadkach na serwerze Zabbix profil obciążenia history syncer zmienił się znacząco — spadł trzykrotnie.
TimescaleDB pozwala na wstawianie danych prawie trzy razy szybciej i używanie mniej HistoryCache.
Odpowiednio, dane będą dostarczane na czas.
TimescaleDB. 120 000 nvps
Następnie zwiększyłem liczbę elementów danych do 500 tysięcy. Głównym celem było przetestowanie możliwości TimescaleDB – uzyskałem obliczoną wartość 125 tysięcy wartości na sekundę.

To działający „setup”, który może długo funkcjonować. Ale ponieważ mój dysk miał tylko 1,5 TB, zapełniłem go w ciągu kilku dni.

Najważniejsze, że w tym samym czasie tworzono nowe partycje w TimescaleDB.
Dla wydajności jest to całkowicie niewidoczne. Kiedy partycje są tworzone w MySQL, na przykład, wszystko działa inaczej. Zwykle dzieje się to w nocy, ponieważ blokuje ogólne wstawianie, pracę z tabelami i może prowadzić do degradacji usług. W przypadku TimescaleDB to nie występuje.
Na przykład pokażę jeden wykres z wielu w community. Na obrazku włączono TimescaleDB, dzięki temu obciążenie związane z użyciem io.weight na procesorze spadło. Użycie elementów wewnętrznych procesów również zmniejszyło się. Chociaż to zwykła wirtualka na standardowych dyskach talerzowych, a nie SSD.

Wnioski
TimescaleDB to dobre rozwiązanie dla małych „setup”., które napotykają na ograniczenia wydajności dysku. Pozwoli to na dobrą kontynuację pracy aż do migracji bazy danych na szybszy sprzęt.
TimescaleDB jest prosty w konfiguracji, zapewnia wzrost wydajności, dobrze współpracuje z Zabbix i ma przewagi w porównaniu z PostgreSQL..
Jeśli korzystasz z PostgreSQL i nie planujesz go zmieniać, to polecam używać PostgreSQL z rozszerzeniem TimescaleDB w połączeniu z Zabbixem.To rozwiązanie efektywnie działa do średnich „setup”.
Mówiąc „wysoka wydajność” — mamy na myśli . Czekać, aby zapoznać się z technologiami i praktykami, które umożliwiają serwisom obsługę milionów użytkowników, to już niedługo. Lista na 7 i 8 listopada już została przygotowana, a można jeszcze zaproponować.
Śledź naszą i , w których ujawniamy szczegóły nadchodzącej konferencji i dowiedz się, jak maksymalnie skorzystać.
Źródło: habr.com
