
Szanowna społeczności, ten artykuł będzie poświęcony efektywnemu przechowywaniu i wydawaniu setek milionów małych plików. Na tym etapie proponowane jest finalne rozwiązanie dla systemów plików zgodnych z POSIX, z pełnym wsparciem dla blokad, w tym klastrowych, i zdaje się, że także już bez obejść.
Dlatego w tym celu napisałem własny specjalistyczny serwer.
W trakcie realizacji tego zadania udało się rozwiązać podstawowy problem, a przy okazji osiągnąć oszczędność miejsca na dysku i pamięci RAM, którą bezlitośnie pochłaniał nasz klastrowy system plików. Tak naprawdę taka liczba plików jest szkodliwa dla każdego klastrowego systemu plików.
Pomysł jest taki:
Mówiąc prosto, przez serwer przesyłane są małe pliki, które są bezpośrednio zapisywane w archiwum i z niego odczytywane, a większe pliki są umieszczane obok. Schemat: 1 folder = 1 archiwum, co daje nam kilka milionów archiwów z małymi plikami, a nie kilka setek milionów plików. A wszystko to zrealizowane jest w pełni, bez żadnych skryptów i układania plików w archiwa tar/zip.
Postaram się streścić, z góry przepraszam, jeśli post będzie zbyt obszerny.
Wszystko zaczęło się od tego, że nie mogłem znaleźć odpowiedniego serwera na świecie, który mógłby zapisywać dane otrzymane przez protokół HTTP bezpośrednio do archiwów, aby uniknąć wad charakterystycznych dla zwykłych archiwów i obiektowych magazynów. Powodem tych poszukiwań był rozrośnięty do dużych rozmiarów klaster Origin składający się z 10 serwerów, na którym zgromadziło się już 250 000 000 małych plików, a tendencja wzrostu nie zamierzała ustępować.
Dla tych, którzy nie przepadają za artykułami, a mała dokumentacja jest łatwiejsza:
i .
I docker przy okazji, obecnie jest dostępna opcja tylko razem z nginx w razie potrzeby:
docker run -d --restart=always -e host=localhost -e root=\/var\/storage \n-v \/var\/storage:\/var\/storage --name wzd -p 80:80 eltaline\/wzdDalej:
Jeśli plików jest bardzo dużo, potrzebne są znaczne zasoby, a co najgorsze, część z nich jest tracona. Na przykład, przy użyciu klastrowego systemu plików (w tym przypadku – MooseFS), plik, niezależnie od faktycznego rozmiaru, zajmuje zawsze minimum 64 KB. To oznacza, że dla plików o rozmiarze 3, 10 lub 30 KB na dysku wymagane jest 64 KB. Jeśli plików jest ćwierć miliarda, tracimy od 2 do 10 terabajtów. Nie uda się w nieskończoność tworzyć nowych plików, ponieważ w MooseFS istnieje ograniczenie: nie więcej niż 1 miliard przy jednej replikacji każdego pliku.
Wraz ze zwiększeniem liczby plików, potrzebna jest duża ilość pamięci RAM na metadane. Częste duże zrzuty metadanych sprzyjają zużywaniu dysków SSD.
Serwer wZD. Porządkujemy dyski.
Serwer został napisany w języku Go. Przede wszystkim musiałem zmniejszyć liczbę plików. Jak to zrobić? Poprzez archiwizację, ale w tym przypadku bez kompresji, ponieważ moje pliki to po prostu spłaszczone obrazy. Z pomocą przyszła BoltDB, którą również musiałem pozbawić wad, co zostało odzwierciedlone w dokumentacji.
Ostatecznie, zamiast ćwierć miliarda plików, w moim przypadku pozostało tylko 10 milionów archiwów Bolt. Gdybym miał możliwość zmiany obecnej struktury wypełniania katalogów plikami, być może udałoby się zredukować liczbę do około 1 miliona plików.
Wszystkie małe pliki są pakowane w archiwa Bolt, które automatycznie przyjmują nazwy katalogów, w których się znajdują, a wszystkie duże pliki pozostają obok archiwów, nie ma sensu ich pakować, to jest konfigurowalne. Małe – archiwizujemy, duże – pozostawiamy bez zmian. Serwer działa przejrzyście zarówno z jednymi, jak i z drugimi.
Architektura i cechy serwera wZD.

Serwer działa pod kontrolą systemów operacyjnych Linux, BSD, Solaris i OSX. Testowałem tylko na architekturze AMD64 pod Linux, ale powinien również pasować do ARM64, PPC64, MIPS64.
Główne funkcje:
- Wielowątkowość;
- Wieloserwerowość, zapewniająca odporność na awarie i zrównoważenie obciążenia;
- Maksymalna przejrzystość dla użytkownika lub programisty;
- Obsługiwane metody HTTP: GET, HEAD, PUT i DELETE;
- Zarządzanie zachowaniem przy odczycie i zapisie przez nagłówki klienta;
- Wsparcie dla elastycznie konfigurowalnych hostów wirtualnych;
- Wsparcie dla integralności danych CRC podczas zapisu/odczytu;
- Półdynamiczne bufory dla minimalnego zużycia pamięci i optymalnej konfiguracji wydajności sieci;
- Opóźniona kompresja danych;
- Dodatkowo dostępny jest wielowątkowy archiwizator wZA do migracji plików bez przerywania usług.
Rzeczywiste doświadczenie:
Opracowywałem i testowałem serwer oraz archiwizator na rzeczywistych danych przez dość długi czas, obecnie działa on z powodzeniem w klastrze obejmującym 250 000 000 małych plików (zdjęć) rozmieszczonych w 15 000 000 katalogów na oddzielnych dyskach SATA. Klastr 10 serwerów stanowi serwer Origin, umieszczony za siecią CDN. W jego obsługę zaangażowane są 2 serwery Nginx i 2 serwery wZD.
Ci, którzy zdecydują się na korzystanie z tego serwera, powinni zaplanować strukturę katalogów przed użyciem, jeśli to możliwe. Od razu zaznaczam, że serwer nie jest przeznaczony do tego, aby zgrać wszystko do jednego archiwum Bolt.
Testowanie wydajności:
Im mniejszy rozmiar skompresowanego pliku, tym szybciej przeprowadzane są operacje GET i PUT. Porównamy całkowity czas zapisu klientem HTTP w zwykłych plikach oraz w archiwach Bolt, a także odczytu. Porównywana jest praca z plikami o rozmiarach 32 KB, 256 KB, 1024 KB, 4096 KB i 32768 KB.
Przy pracy z archiwami Bolt sprawdzana jest integralność danych każdego pliku (używane jest CRC), przed zapisem oraz po zapisie następuje odczyt na żywo i przeliczanie, co naturalnie wprowadza opóźnienia, ale najważniejsze — bezpieczeństwo danych.
Testy wydajności przeprowadzałem na dyskach SSD, ponieważ na dyskach SATA testy nie pokazują wyraźnej różnicy.
Wykresy wyników testów:


Jak widać, dla małych plików różnica w czasie odczytu i zapisu między plikami archiwizowanymi a niearchiwizowanymi jest niewielka.
Inny obraz uzyskujemy już w teście odczytu i zapisu plików o rozmiarze 32 MB:

Różnica w czasie między odczytem plików wynosi od 5 do 25 ms. Z zapisami jest gorzej, różnica wynosi około 150 ms. Jednak w tym przypadku nie ma potrzeby zgrywania dużych plików, po prostu nie ma sensu, mogą one żyć oddzielnie od archiwów.
*Technicznie można używać tego serwera także do zadań wymagających NoSQL.
Główne metody pracy z serwerem wZD:
Ładowanie zwykłego pliku:
curl -X PUT --data-binary @test.jpg http://localhost/test/test.jpgPrzesyłanie pliku do archiwum Bolt (jeśli nie przekroczono serwerowego parametru fmaxsize, który określa maksymalny rozmiar pliku, jaki może być dodany do archiwum; jeśli przekroczono, plik zostanie przesłany jak zwykle obok archiwum):
curl -X PUT -H "Archive: 1" --data-binary @test.jpg http://localhost/test/test.jpgPobieranie pliku (jeśli na dysku i w archiwum są pliki o tych samych nazwach, priorytet przy pobieraniu domyślnie przypisany jest plikowi niezaarchiwizowanemu):
curl -o test.jpg http://localhost/test/test.jpgPobieranie pliku z archiwum Bolt (wymuszone):
curl -o test.jpg -H "FromArchive: 1" http://localhost/test/test.jpgOpisy innych metod znajdują się w dokumentacji.
Serwer obecnie obsługuje tylko protokół HTTP, z HTTPS nie działa jeszcze. Metoda POST również nie jest wspierana (jeszcze nie podjęto decyzji, czy jest potrzebna czy nie).
Kto pokopuje się w kodzie źródłowym, znajdzie tam iryskę, której nie wszyscy lubią, ale nie powiązałem głównego kodu z funkcjami frameworka webowego, poza obsługą przerwań, więc w przyszłości mogę szybko przepisać prawie na dowolny silnik.
Do zrobienia:
- Rozwój własnego replikatora i dystrybutora + geo dla możliwości wykorzystania w dużych systemach bez klastrowych FS (wszystko na poważnie)
- Możliwość pełnego odwrotnego przywracania metadanych w przypadku ich całkowitej utraty (w przypadku korzystania z dystrybutora)
- Nattywny protokół dla możliwości użycia stałych połączeń sieciowych i sterowników do różnych języków programowania
- Rozbudowane możliwości wykorzystania komponentów NoSQL
- Kompresja różnych typów (gzip, zstd, snappy) dla plików lub wartości wewnątrz archiwów Bolt i dla zwykłych plików
- Szyfrowanie różnych typów dla plików lub wartości wewnątrz archiwów Bolt i dla zwykłych plików
- Opóźniona serwerowa konwersja wideo, w tym na GPU
To wszystko, mam nadzieję, że ten serwer komuś się przyda, licencja BSD-3, podwójne prawa autorskie, ponieważ gdybym nie miał firmy, w której pracuję, nie napisałbym i serwera. Jestem jedynym deweloperem. Będę wdzięczny za znalezione błędy i propozycje funkcji.
Źródło: habr.com
