
W tym artykule omówimy niektóre aspekty podsystemu wejścia-wyjścia oraz ich wpływ na wydajność.
Kilka tygodni temu zderzyłem się z pytaniem, dlaczego NVMe na jednym serwerze jest wolniejsze niż SATA na innym. Zajrzałem do specyfikacji serwerów i zrozumiałem, że to była pułapka: NVMe był z segmentu konsumenckiego, a SSD — z segmentu serwerowego.
Jasne jest, że porównywanie produktów z różnych segmentów w różnych warunkach nie jest właściwe, ale to nie stanowi wyczerpującej technicznej odpowiedzi. Zbadajmy podstawy, przeprowadźmy eksperymenty i udzielmy odpowiedzi na postawione pytanie.
Czym jest fsync i gdzie jest używane?
Aby przyspieszyć pracę z nośnikami, dane są buforowane, to znaczy przechowywane w pamięci nieulotnej, aż nadejdzie dogodny moment na zapis zawartości bufora na nośnik. Kryteria »dogodnego momentu« są określane przez system operacyjny oraz specyfikację nośnika. W przypadku utraty zasilania wszystkie dane w buforze zostaną utracone.
Istnieje szereg zadań, w których należy mieć pewność, że zmiany w pliku zostały zapisane na nośniku, a nie leżą w pośrednim buforze. Tę pewność można uzyskać, korzystając z systemowego wywołania zgodnego z POSIX, fsync. Wywołanie fsync inicjuje wymuszenie zapisu z bufora na nośnik.
Zademonstrujemy wpływ buforów na sztucznym przykładzie w postaci krótkiego programu w języku C.
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(void) {
/* Открываем файл answer.txt на запись, если его нет -- создаём */
int fd = open("answer.txt", O_WRONLY | O_CREAT);
/* Записываем первый набор данных */
write(fd, "Answer to the Ultimate Question of Life, The Universe, and Everything: ", 71);
/* Делаем вид, что проводим вычисления в течение 10 секунд */
sleep(10);
/* Записываем результат вычислений */
write(fd, "42n", 3);
return 0;
}Komentarze dobrze wyjaśniają sekwencję działań w programie. Tekst »odpowiedź na główne pytanie życia, wszechświata i wszystkiego« zostanie zbuforowany przez system operacyjny, a jeśli zrestartujesz serwer przyciskając Reset w trakcie »obliczeń«, plik będzie pusty. W naszym przykładzie utrata tekstu nie stanowi problemu, więc fsync nie jest potrzebne. Bazy danych nie podzielają takiego optymizmu.
Bazy danych to skomplikowane programy, które jednocześnie operują na wielu plikach, dlatego chcą mieć pewność, że zapisywane przez nie dane zostaną zachowane na nośniku, ponieważ od tego zależy spójność danych wewnątrz Bazy Danych. Bazy danych są zaprojektowane tak, aby zapisywać wszystkie zakończone transakcje i być gotowe na utratę zasilania w dowolnym momencie. Takie zachowanie obliguje do ciągłego korzystania z fsync w dużych ilościach.
Jak częste używanie fsync wpływa na wydajność
Podczas standardowego wejścia/wyjścia system operacyjny stara się zoptymalizować komunikację z dyskami, ponieważ w hierarchii pamięci zewnętrzne nośniki są najwolniejsze. Dlatego system operacyjny stara się za jednym dostępem do nośnika zapisać jak najwięcej danych.
Zademonstrujemy wpływ używania fsync na konkretnym przykładzie. W naszym teście weźmiemy następujące dyski SSD:
- Intel® DC SSD S4500 480 GB, podłączony przez SATA 3.2, 6 Gb/s;
- Samsung 970 EVO Plus 500GB, podłączony przez PCIe 3.0 x4, ~31 Gb/s.
Testy są przeprowadzane na Intel® Xeon® W-2255 z systemem operacyjnym Ubuntu 20.04. Do testowania dysków używamy sysbench 1.0.18. Na dyskach utworzono jedną partycję, sformatowaną jako ext4. Przygotowanie do testu polega na utworzeniu plików o pojemności 100 GB:
sysbench --test=fileio --file-total-size=100G prepareRozpoczęcie testów:
# Без fsync
sysbench --num-threads=16 --test=fileio --file-test-mode=rndrw --file-fsync-freq=0 run
# С fsync после каждой записи
sysbench --num-threads=16 --test=fileio --file-test-mode=rndrw --file-fsync-freq=1 runWyniki testów są przedstawione w tabeli.
Test
Intel® S4500
Samsung 970 EVO+
Odczyt bez fsync, MiB/s
5734.89
9028.86
Zapis bez fsync, MiB/s
3823.26
6019.24
Odczyt z fsync, MiB/s
37.76
3.27
Zapis z fsync, MiB/s
25.17
2.18
Nie trudno zauważyć, że NVMe z segmentu konsumenckiego pewnie prowadzi, gdy system operacyjny sam decyduje, jak pracować z dyskami, i przegrywa, gdy używane jest fsync. Z tego wynikają dwa pytania:
- Dlaczego w teście bez fsync prędkość odczytu przekracza fizyczną przepustowość kanału?
- Dlaczego SSD z segmentu serwerowego lepiej radzi sobie z dużą ilością zapytań fsync?
Odpowiedź na pierwsze pytanie jest prosta: sysbench generuje pliki wypełnione zerami. Test przeprowadzono na 100 gigabajtach zer. Ponieważ dane są bardzo jednorodne i przewidywalne, aktywują się różne optymalizacje systemu operacyjnego, co znacznie przyspiesza wykonanie.
Jeśli kwestionować wszystkie wyniki sysbench, można skorzystać z fio.
# Без fsync
fio --name=test1 --blocksize=16k --rw=randrw --iodepth=16 --runtime=60 --rwmixread=60 --fsync=0 --filename=/dev/sdb
# С fsync после каждой записи
fio --name=test1 --blocksize=16k --rw=randrw --iodepth=16 --runtime=60 --rwmixread=60 --fsync=1 --filename=/dev/sdbTest
Intel® S4500
Samsung 970 EVO+
Odczyt bez fsync, MiB/s
45.5
178
Zapis bez fsync, MiB/s
30.4
119
Odczyt z fsync, MiB/s
32.6
20.9
Zapis z fsync, MiB/s
21.7
13.9
Tendencja do spadku wydajności NVMe podczas używania fsync jest wyraźnie widoczna. Możemy przejść do odpowiedzi na drugie pytanie.
Optymalizacja czy oszustwo
Wcześniej mówiliśmy, że dane są przechowywane w buforze, ale nie precyzowaliśmy, w jakim, ponieważ nie miało to większego znaczenia. I teraz nie będziemy zgłębiać szczegółów systemów operacyjnych i wyróżnimy dwa ogólne typy buforów:
- programowy;
- sprzętowy.
Bufor oprogramowania odnosi się do buforów obecnych w systemie operacyjnym, podczas gdy bufor sprzętowy to pamięć kontrolera dysku, która jest zależna od energii. Systemowe wywołanie fsync poleca nośnikowi zapisanie danych z jego bufora do głównego magazynu, ale nie może kontrolować poprawności wykonania tego polecenia.
Ponieważ SSD osiągają lepsze wyniki, można wysunąć dwa przypuszczenia:
- dysk został zaprojektowany z myślą o takim obciążeniu;
- dysk 'blefuje' i ignoruje polecenie.
Nieuczciwe zachowanie nośnika można zauważyć, przeprowadzając test z zniknięciem zasilania. Można to sprawdzić za pomocą skryptu , który został w 2005 roku.
Ten skrypt wymaga dwóch fizycznych maszyn — 'serwera' i 'klienta'. Klient zapisuje na testowanym dysku niewielką ilość danych, wywołuje fsync i przesyła serwerowi informację o tym, co zostało zapisane.
# Запускается на сервере
./diskchecker.pl -l [port]
# Запускается на клиенте
./diskchecker.pl -s <server[:port]> create <file> <size_in_MB>Po uruchomieniu skryptu należy odłączyć 'klienta' od zasilania i nie przywracać zasilania przez kilka minut. Ważne jest, aby odłączyć testowany od zasilania, a nie tylko wykonać twarde wyłączenie. Po upływie jakiś czasu serwer można podłączyć i uruchomić system operacyjny. Po uruchomieniu systemu operacyjnego należy ponownie uruchomić diskchecker.pl, ale z argumentem verify.
.\/diskchecker.pl -s verifyNa koniec testu zobaczysz liczbę błędów. Jeśli wynosi ona 0, oznacza to, że dysk przeszedł test. Aby wykluczyć szczęśliwe okoliczności dla dysku, można powtórzyć doświadczenie kilka razy.
Nasz S4500 nie pokazał błędów przy utracie zasilania, więc można stwierdzić, że jest gotowy na obciążenia związane z dużą ilością wywołań fsync.
Podsumowanie
Podczas wyboru dysków lub całych gotowych konfiguracji należy pamiętać o specyfice zadań, które należy rozwiązać. Na pierwszy rzut oka wydaje się, że NVMe, czyli SSD z interfejsem PCIe, jest szybszy od 'klasycznego' SSD SATA. Jednak, jak dzisiaj zrozumieliśmy, w specyficznych warunkach i przy określonych zadaniach może tak nie być.
A jak testujesz komponenty serwerów przy wynajmie od dostawcy IaaS?
Czekamy na Twoje uwagi w komentarzach.
Źródło: habr.com
