Optymalizacja wydajności aplikacji
Wolna aplikacja to utraceni użytkownicy i wyższe rachunki za serwery. Mierzymy, znajdujemy wąskie gardła i usuwamy je: baza danych, cache, kolejki, Octane, wyszukiwarka i testy obciążeniowe.
Aplikacja, która działała szybko przy stu użytkownikach, zwalnia przy dziesięciu tysiącach. Strony ładują się sekundami, raporty nie chcą się wygenerować, a serwer trzeba co chwilę powiększać. Zanim zaproponujemy większy serwer albo przepisanie systemu, sprawdzamy, gdzie naprawdę ucieka czas.
Najpierw pomiar
Optymalizacja bez pomiaru to zgadywanie. Zaczynamy od danych: które żądania są najwolniejsze, ile zapytań do bazy wykonuje pojedyncza strona, co dzieje się w kolejkach, jak wygląda obciążenie serwera. Korzystamy z monitoringu w Grafanie, profilowania aplikacji i nagrań sesji użytkowników w OpenReplay, które pokazują, co faktycznie widzi klient.
Co zwykle przyspiesza aplikację
- Baza danych — brakujące indeksy, zapytania wykonywane w pętli (problem N+1), pobieranie danych, których nikt nie wyświetla. To tu najczęściej leży największy zysk.
- Cache w Redisie — wyniki, które zmieniają się rzadko, liczone raz zamiast przy każdym wejściu.
- Kolejki — eksporty, wysyłki, synchronizacje i generowanie plików przeniesione w tło, żeby użytkownik nie czekał.
- Laravel Octane — aplikacja uruchomiona na długo żyjącym serwerze aplikacji obsługuje więcej żądań na tym samym sprzęcie.
- Wyszukiwarka — pełnotekstowe wyszukiwanie i filtrowanie dużych zbiorów w Elasticsearch zamiast w bazie SQL.
- Frontend i CDN — mniejsze paczki JavaScript, ładowanie części interfejsu na żądanie, obrazy w odpowiednich rozmiarach i Cloudflare przed aplikacją.
Gdy mimo to jakaś część systemu nadal jest wąskim gardłem, wydzielamy ją do osobnej usługi w Rust.
Testy obciążeniowe
Przed ważną kampanią, startem produktu albo sezonem sprawdzamy, ile aplikacja wytrzyma. Testy obciążeniowe w k6 symulują ruch wielu użytkowników naraz i pokazują, co pierwsze przestanie działać — zanim zrobią to prawdziwi klienci. Ten sam test powtarzamy po zmianach, żeby porównać wynik.
Efekt, który da się zmierzyć
Na koniec dostajesz porównanie przed i po: czasy odpowiedzi najważniejszych stron, liczbę obsłużonych żądań i zużycie zasobów serwera. Jeśli aplikacja wymaga głębszych zmian w architekturze, mówimy o tym wprost i proponujemy plan. Wydajności pilnujemy potem w ramach utrzymania aplikacji, a stan całego systemu możemy sprawdzić w ramach audytu.
Najczęściej zadawane pytania
Czy zamiast optymalizacji nie wystarczy większy serwer?
Czasem tak, i wtedy to mówimy. Najczęściej jednak problem leży w kilku zapytaniach albo braku cache, a większy serwer tylko odsuwa go w czasie i podnosi rachunek co miesiąc.
Optymalizujecie tylko aplikacje w Laravelu?
Najgłębiej znamy Laravela i PHP, frontendy w React oraz bazy PostgreSQL i MySQL. Optymalizację bazy danych, cache, infrastruktury i frontendu możemy zrobić także w aplikacjach w innych technologiach.
Ile trwa optymalizacja?
Pomiar i lista wąskich gardeł to pierwszy, krótki etap. Pierwsze poprawki, zwykle w bazie danych, dają efekt szybko. Głębsze zmiany planujemy na podstawie wyników pomiaru.
Czy optymalizacja może coś zepsuć?
Każda zmiana przechodzi testy i code review, a wdrażamy je pojedynczo i mierzymy efekt. Dzięki temu wiadomo, która zmiana co dała, a w razie problemu łatwo ją wycofać.
Czy potrzebujecie dostępu do serwera produkcyjnego?
Do pomiaru przydaje się dostęp do monitoringu i logów produkcji, bo tam widać prawdziwy ruch. Same zmiany przygotowujemy i testujemy na środowisku testowym z kopią danych, a na produkcję trafiają zwykłym wdrożeniem.
Po czym poznać, że aplikacja ma problem z wydajnością, zanim zauważą to klienci?
Sygnały ostrzegawcze to rosnący czas odpowiedzi najważniejszych stron, coraz dłuższe kolejki zadań, rosnące zużycie pamięci i procesora oraz błędy przekroczenia czasu. Monitoring z progami alertów pokazuje te trendy tygodnie wcześniej, niż przełożą się na skargi użytkowników.