Przejdź do głównej zawartości

Ehcache - optymalizacja i tuning wydajności aplikacji webowych i serwerów aplikacyjnych z zastosowaniem buforowania danych.

Ehcache jest narzędziem, biblioteką podręczną wprowadzoną w październiku 2003 roku, służącą do optymalizacji działania tymczasowych danych używanych przez aplikację czyli tgzw.: "cache". Kluczowym celem dla zastosowania tego rozwiązania jest zwiększenie wydajności przez zmniejszenie obciążenia zasobów źródłowych. Ehcahe poza podstawową funkcją buforowania danych umożliwia również buforowanie danych drugiego poziomu oraz danych dostępowych do obiektów, poświadczeń bezpieczeństwa i stron internetowych. 


Ehcache może mieć również zastosowanie dla buforowania serwerów SOAP i REST, podnoszenia stabilności działania aplikacji oraz buforowania rozproszonego. 

PODSTAWOWE DEFINICJE

  • cache - Zgodnie z definicją encyklopedyczną "cache" jest to mechanizm, w którym część z pośród danych zgromadzonych w źródłach o długim czasie dostępu i niskiej przepustowości jest dodatkowo przechowywana w pamięci o lepszych parametrach. Skutkuję to poprawą czasu dostępności do tych informacji, które przypuszczalnie będą potrzebne w najbliższej przyszłości. Można powiedzieć, że "cache" jest to tymczasowe miejsce przechowywania danych znajdujące się w miejscu umożliwiającym szybki dostęp do danych przez wielokrotny ich odczyt z jak najmniejszym kosztem ich uzyskania.

  • cache-hit - Pojęcie określające element danych znajdujący się w pamięci tymczasowej do jakiego można odwołać się po istniejącym referencyjnym do niego kluczu. Celne trafienie

  • cache-miss - Pojęcie określające sytuację gdy występuję odwołanie referencyjne po określonym kluczu do nieistniejących danych w pamięci tymczasowej. Trafienie chybione

  • system-of-record - Podstawowym założeniem buforowania jest fakt, iż bufor zawiera potwierdzone prawdziwe dane, co często określane jest jako system-of-record (SOR). Cache działa jako lokalna kopia danych w jakiej można zapisywać i odczytywać informacje. Często jest to po prostu tradycyjna baza danych ale może być to również system plików. Na potrzeby działania Ehcache stosuję się bazę danych. 

JAK DZIAŁA CACHE ?

Lokalne odniesienia 

Podczas gdy Ehcache zajmuje się obiektami Java, buforowanie jest używane bardzo często w różnych dziedzinach i systemach informatycznych od tymczasowej pamięci procesora po pamieć tymczasową systemów DNS. Dlaczego ? Ponieważ wiele systemów informatycznych stosuje "lokalne odniesienia". Dlatego też jest dużo bardziej prawdopodobne, iż dane zlokalizowane blisko innych już używanych danych zostaną wykorzystane ponownie. 

Długi ogon 

W magazynie "Wired" Christian Anderson użył sformułowania "długi ogon" w odniesieniu do systemów e-commerce. Idea ta dotyczy faktu mówiącego, że nie wielka liczba elementów stanowi najważniejszą i największą część całości. Dla przykładu niewielka liczba produktów może stanowić największą część sprzedaży, a niewielka ilość blogów stanowi istotną część trafień wyszukiwarki itd. Pomimo, że liczba właściwych istotnych ważnych trafień nie jest długa i stanowi tylko nie wielką część całości, za nią znajduje się długa lista pozostałych elementów, które nie są już tak ważne ale jak gdy by ciągną się za całością. 


Obrazowe przedstawienie długiego ogona ( the long tail )



Wraz z twierdzeniem dotyczącym "długiego ogona" idzie w parze prawo energetycznego rozkładu prawdopodobieństwa bardziej znanego jako "reguła pareto" czyli stosunek 20/80. Zjawisko to ma również zastosowanie w buforowaniu gdzie 20% obiektów jest potrzebnych przez 80% czasu. Dobrze więc było by umieścić te 20% obiektów w najszybciej dostępnej lokalizacji, z której można będzie uzyskać do nich dostęp najmniejszym kosztem czasu. 

JAKIE KORZYŚCI Z BUFOROWANIA MAJĄ APLIKACJE ?

Krótko mówiąc korzyści dla aplikacji zostały już przedstawione powyżej. Wydajność aplikacji w dużej mierze zależy od jej przeznaczenia czyli rodzaju zadań jakie dane aplikacja ma wykonywać. Dalej istotne jest z czym te zadania są związane np.: czy potrzeba wykonać wiele złożonych obliczeń, co przekłada się na wykorzystanie mocy procesora lub czy też potrzeba zapisywać i odczytywać wiele danych, co przekłada się na wydajność operacji wejścia / wyjścia i wydajność urządzeń z tym związanych. 

Co prawda idea buforowania skupiona jest na podnoszeniu wydajności to należy zdawać sobie również sprawę, że poprzez stosowanie buforowania obniżane jest również obciążenie danego systemu. Czas potrzebny na wykonanie danej operacji stanowi zazwyczaj koszt wykonania danej operacji. Poprzez przyspieszenie operacji wykorzystując w tym celu wykonywanie działań na danych zbuforowanych zmniejszymy obciążenie ograniczonych zasobów.  

Przyspieszanie aplikacji obciążających zasoby procesorowe

Sposobami na przyspieszenie działania aplikacji, które swoje działanie opierają o wykonywanie dużej liczby operacji obliczeniowych wykorzystując w tym celu zasoby procesora są : 

  • poprawa wydajności przez optymalizacje lub zmianę algorytmów obliczeniowych
  • rozdzielenie wykonywanych zadań pomiędzy wiele procesorów, wiele maszyn lub klastrowanie
  • zmiana procesora na bardziej wydajny lub jeżeli to możliwe dodanie kolejnego procesora

Rolą bufora jeżeli został wykorzystany w takim przypadku jest przechowywanie obliczeń jakie mogą zostać wykorzystane ponownie. Przykładem zastosowanie ehcache mogą być duże strony internetowe, które mają wysokie koszty utylizacji. Innym przykładem może być buforowanie statusu uwierzytelniania w przypadku gdy uwierzytelnianie wymaga kryptograficznych transformacji. 

Przyspieszanie aplikacji obciążających operacje I/O (wejścia/wyjścia)

Wiele aplikacji podczas swojego działania wykonuje dużą liczbę operacji wejścia/wyjścia,  zapisu i odczytu danych  z dysku oraz operacji sieciowych. W przypadku baz danych ograniczenia mogą występować w wyniku wszystkich wymienionych powodów. 

Dla dysków twardych prawo Moora nie ma zastosowania. Dysk o obrotach 10 000 RPM byl szybki i jest nadal szybki tak jak 10 lat temu. Optymalizacja wydajności dotycząca dysków opiera się na wewnętrznym buforowaniu wewnętrznych bloków pamięci. 

Wydajność operacji sieciowych może być związana z szeregiem czynników. 
  • czas nawiązania i zamknięcia połączenia 
  • opóźnienia związane z minimalnym czasem przepływu danych
  • limity przepustowości łącza
Buforowanie danych bardzo często pomaga w przypadku operacji wejścia/wyjścia podczas działania aplikacji. Przykłady zastosowania ehcache w tym wypadku dotyczą : 
  • Buforowanie obiektów dla Hibernate
  • Buforowanie stron internetowych jakie są generowane z baz danych.

Zwiększenie skalowalności aplikacji

Kolejnym krokiem skutkującym zwiększeniem wydajności jest skalowalność aplikacji. Załóżmy, że mamy bazę danych na jakiej wykonujemy 100 obciążających zapytań w sekundę. Następnie robione są kopie zapasowe i jeżeli nadal dodawane są nowe połączenia działa coraz wolniej i pomału umiera. 

W tym przypadku buforowanie jest wstanie zmniejszyć generowane obciążenie. Jeśli dzięki buforowaniu od 90 do 100 trafień odbywało by się w pamięci podręcznej bez wykonywania połączeń do bazy danych skalowalność osiągnęła by 10 razy wyższy stopień niż w jakikolwiek inny sposób.  

O ile wzrośnie wydajność aplikacji z użyciem buforowania ?

Odpowiedź jest krótka. Zależy to od wielu czynników takich jak :
  • Ile razy dany element znajdujący się w buforze jest ponownie wykorzystywany przez aplikację 
  • Proporcjonalny czas odpowiedzi z pamięci użytej na bufor 
W aplikacjach, których praca związana jest w dużej mierze z operacjami wejścia / wyjścia, czyli w większości przypadków aplikacji biznesowych straty wydajności znajdują się w momencie pobierania danych z bazy danych. Dlatego w takim przypadku wydajność w dużej mierze jest zależna od tego ile razy aplikacja musi sięgać po ten sam element. 

W systemie, w którym dostęp do danego elementu występuję tylko jeden raz straty są zerowe. W przypadku środowiska gdzie dane są wykorzystywane wielokrotnie przyspieszenie jakie można osiągnąć dzięki buforowaniu jest bardzo wysokie. 

Pełna odpowiedź jest bardziej skomplikowana i związana z matematyką. 

Prawo Amdahl'a

Prawo Amdahla, znane również jako "Wywód Amdahla", zostało nazwane od nazwiska twórcy architektur komputerowych Gene Amdahla, i jest używany do znajdowania maksymalnego spodziewanego zwiększenia wydajności całkowitej systemu jeżeli tylko część systemu została ulepszona. Jest ono często używane w przypadku prowadzenia obliczeń równoległych do przewidzenia teoretycznego maksymalnego wzrostu szybkości obliczeń przy użyciu wielu procesorów. Tak więc z prawa tego wynika, że wydajność całego systemu zleży od wydajności elementów z jakich się on składa. 


1 / ((1 - Proporcjonalne przyspieszenie) + Proporcjonalne przyspieszenie / Przyspieszenie)


Prawo Amdahl'a

Zwiększenie szybkości wykonywania się programu przy użyciu wielu procesorów w obliczeniach równoległych jest ograniczane przez czas potrzebny do sekwencyjnego dzielenia programu. Na przykład jeżeli program potrzebuje 20 godzin w przypadku obliczeń prowadzonych na procesorze jednordzeniowym i 1 godzina obliczeń nie może zostać przetworzona poprzez obliczenia równoległe, ale pozostałe 19 godzin (95%) obliczeń mogą, wówczas bez względu na to ile procesorów zostanie użytych do przeprowadzenia obliczeń równoległych minimalny czas wykonania programu nie będzie nigdy mniejszy niż ta krytyczna 1 godzina. Tak więc zwiększenie szybkości obliczeń jest ograniczone do 20x, jak przedstawiono na diagramie.

Poniżej znajdują się przykłady użycia prawa Amdahla. Dla uproszczenia zakładamy, że środowisko w jakim będzie wykonywana optymalizacja składa się z  : 
  •  Pojedynczy serwer 
  • System jest jednowątkowy, 
  • 100% obiektów może znajdować się w buforze
  • Czas życia obiektów jest nieskończony

Buforowanie relacyjne obiektów trwałych 

Użycie obiektu session.load() w Hibernate jest 1000 razy szybsze przy użyciu bufora niż z bazy danych. Typowe zapytanie zwróci listę identyfikatorów z bazy danych jakie następnie mogą zostać użyte. Jeżeli zostanie użyte wywołanie session.iterate() ponownie wracamy do bazy danych w celu załadowania każdego obiektu. 

Załóżmy scenariusz w którym wykonujemy zapytanie do bazy danych zwracające 100 identyfikatorów, które następnie mają zostać zwrócone. Czas samego zapytania stanowi 20% z czasu jaki musi upłynąć do zwrócenia wszystkich elementów (80%). Samo działanie zapytania w bazie danych potrzebuję 75% czasu. Proporcjonalne przyspieszenie wynosi 60% (75% * 80%). 

Oczekiwane przyspieszenie : 

1 / ((1 - .6) + .6 / 1000) = 1 / (.4 + .0006) = 2.5 raza szybciej 


Buforowanie stron WWW

Przyspieszenie ładowania stron przy użyciu ehcache wynosi nawet do 1000 razy. Ehcache potrafi pobrać stronę z własnego SimplePageCachingFilter w kilka milisekund. Ponieważ strona jest końcowym rezultatem obliczeń udział wynosi w tym wypadku 100%.

1 / ((1 - 1) + 1 / 1000) = 1 / (0 + .0001) = 1000 razy szybciej


Buforowanie fragmentów strony WWW

Czasami z takich czy innych przyczyn nie ma możliwości by wykorzystać buforowanie dla całej strony WWW. W takim wypadku możliwe jest zastosowanie SimplePageFragmentCachingFilter, co umożliwia wykorzystanie cache dla wybranych elementów witryny. 

Załóżmy, że możemy podnieść wydajność o 1000 razy dla wybranego fragmentu witryny, który obecnie zużywa 40% czasu jej renderowania. 

Oczekiwane przyspieszenie wyniesie wtedy : 


1 / ((1 - .4) + .4 / 1000) = 1 / (.6 + .0004)  = 1.6 razy szybciej od stanu wyjściowego


WYDAJNOŚĆ BUFOROWANIA

W prawdziwym życiu zawarte w buforze elementy nie są dostępne zawsze. Choć niektóre z przykładów są dość blisko zachowania wieczności jak np.: strony www i ich fragmenty takie jak stopka witryny.

Czynnikami wpływającymi na wydajność buforowania są :

  • żywotność - jak długo potrzebne dane mają znajdować się w buforze. Krótszy czas życia danych w buforze powoduję możliwość obsłużenia ich w większej ilości. Ograniczenia stanowi jedynie ilość pamięci dostępnej dla danego hosta. W przypadku systemów 32 bitowych istniał twardy limit wynoszący 2GB. W chwili obecnej nieco poszerzono te granice oraz usprawniono działanie mechanizmu oczyszczania pamięci ze zbędnych danych (garbage collector). 
  • rozkład użycia danych - Jeżeli do pamięci podręcznej trafi jedynie 300 z 3000 elementów warto zwrócić uwagę by rozkład ten przewidywał wybiórcze umieszczenie tych elementów w buforze, które stanowią, zgodnie z zasadą Pareto. 80% kosztów działania całego systemu
  • wskaźnik odczyt / zapis - Proporcja dotycząca czasu w jakim porcja danych jest zapisywana oraz czasu w jakim porcja danych jest odczytywana w raz z częstotliwością występowania obu przypadków. Dla przykładu dane na temat meldunku osób w pokojach hotelowych będą stanowiły znaczną część całości w porównaniu ze stałym opisem poszczególnych pokoi jeżeli by wziąć pod uwagę witrynę usług hotelowych. 


WYDAJNOŚĆ KLASTRA 

W realnym scenariuszu dość rzadko znajdziemy środowisko składające się z jednego serwera. Za zwyczaj obciążenie równoważone jest przez rozłożenie zasobów na kilku maszynach do jakich trafiają zapytania. Pamięć podręczna zawiera tylko jeden wpis o określonej długości żądaniu i czasie życia. Poniższa tabela obrazuje wpływ czasu życia na możliwość chybionego zapytania. 




Wskaźnik trafienia w pamięć podręczną dla danego systemu jest obliczany następująco :


Wydajność klastra autonomicznej pamięci podręcznej wynosi zazwyczaj :

1 / ((1 - Proporcja przyspieszenia * efektywna wydajność bufora) +
(Proporcja przyspieszenia * efektywna wydajność bufora)/ Przyspieszenie)
efektywna wydajność bufora = (wydajność bufora) * (wydajność klastra)


Przykładowe obliczenia podniesienia wydajności dla strony WWW. 

Załóżmy, że wydajność bufora jest na poziomie 35% o średniej żywotności 10 zapytań i dwa serwery.

wydajność bufora  = 0.35 
wydajność klastra = (10 - 1) / 10 = 0.9 

Efektywna wydajność bufora  

0.35 * 0.9 = 0.315 1 / ((1 - 1 * 0.315) + 1 * 0.315 / 1000) = 1 / (0.685 + 0.000315) = 1.45

Co się stanie jeżeli wydajność bufora wyniesie 70% ?

wydajność bufora  = 0.70 
wydajność klastra  = (10 - 1) / 10 = 0.9 

Efektywna wydajność bufora

0.70 * 0.9 = 0.63 1 / ((1 - 1 * 0.63) + 1 * .63 / 1000) = 1 / (.37 + .00063) = 2.69 

A gdy by tak wydajność bufora wynosiła 90% ?

wydajność bufora = 0.90 
wydajność klastra = .(10 - 1) / 10 = 0.9 

Efektywna wydajność bufora  

0.9 * 0.9 = .81 1 / ((1 - 1 * 0.81) + 1 * 0.81 / 1000) = 1 / (0.19 + 0.00081) = 5.24 

Komentarze

Prześlij komentarz

Najczęściej czytane w tym miesiącu

50 popularnych pytań dotyczących systemu Linux zadawanych na rozmowach kwalifikacyjnych. (Pytania & Odpowiedzi)

Jak dodać użytkownika w systemie Windows z poziomu konsoli CMD? (net user, net localgroup)

Generowanie testowych plików o określonej wielkości