Za kulisami Pulsar Online, baza danych
Jak już kilkukrotnie na łamach tego bloga pisałem, od kilku lat pracuję nad przeglądarkową grą MMORPG, której rozgrywka osadzona jest kosmosie. Cytując mój wpis sprzed półtorej roku:
W Pulsar Online to gra przeglądarkowa typu MMORPG w której wcielasz się pilota statku kosmicznego i zostajesz przeniesiony do wszechświata Pulsar. Wszechświat jest podzielony na systemy, a systemy na sektory. W systemach znajduję się sektory różnych typów (asteroidy, mgławica, itp.) różniące się parametrami oraz zasobami jakie w nich występują. Oprócz tego, w systemach są ulokowane stacje i porty kosmiczne. Gracz ma możliwość poruszania się wewnątrz systemów jak i pomiędzy nimi (używając Wrót Skoku), dokowania do portów i stacji, kupna i sprzedaży towarów, zbierania zasobów z przestrzeni kosmicznej. Dzięki handlowi i zbieractwu można kupować lepsze statki, wyposażenie i uzbrojenie. Posiadając odpowiedni statek można walczyć z innymi graczami.
Ostatnimi czasy znów przysiadłem fałd i zintensyfikowałem prace nad grą. Wielkimi krokami zbliża się kolejna (ostatnia) wersja Alfa i publiczna Beta. Jeśli wszystko pójdzie zgodnie z planem, gra ujrzy światło dzienne na początku wakacji 2011. Na dzień dzisiejszy, termin pierwszego lipca jest jak najbardziej realny.
Tworząc silnik Pulsara postawiłem sobie za zadanie, aby był jak najbardziej elastyczny i umożliwiał wdrożenie zarówno na zwykłym hostingu współdzielonym, jak i serwerze dedykowanym. Decyzja była więc (przynajmniej dla mnie) oczywista: MySQL 5.
Doświadczenia zebrane podczas prac na moją poprzednią grą, czyli Omricon Beta jasno wykazały, że najwęższym gardłem będzie, jak zwykle, baza danych. Optymalizacja zapytań i ograniczenie ich liczby ma kluczowe znaczenie dla płynności gry. Strata każdej milisekundy na zapytaniu może skutkować zablokowaniem dostępu do danych i w efekcie zatrzymać cały silnik. Z tego powodu odrzuciłem rozwiązania typu PDO, które ze względu na zbyt duży (relatywnie) narzut sterownika były za mało wydajne. Standardowy sterownik mysql_ do PHP, choć teoretycznie najszybszy, także został odrzucony. Nie dość, że nie jest rozwijany od dłuższego czasu, to w dodatku nie wspiera możliwości MySQL 5 takich jak prepared statements, transakcje, czy wielokrotne zapytania i procedury. Na placu boju pozostało tylko mysqli_ i to ono jest wykorzystywane przez silnik Pulsara.
Kolejną ważną decyzją jaką podjąłem projektując obsługę bazy danych, było zdecydowanie się na częściowe porzucenie modelu relacyjnego w warstwie aplikacji i stworzenie wrappera odwzorowującego pojedyncze tabele na model obiektowy. Takie rozwiązanie, nie dość, że pozwoliło na zwiększenie abstrakcyjności kodu i częściowe wyeliminowanie ręcznego składania zapytań SQL, to dzięki minimalnemu narzutowi obliczeniowemu nie spowalnia silnika. W dodatku, wrapper posiada wewnętrzny mechanizm pamięci podręcznej zmniejszający liczbę zapytań wysyłanych do serwera bazy danych (nawet w przypadku, gdyby żądanie mogło być zaspokojone z pamięci podręcznej zapytań serwera).
Baza danych Pulsara wykorzystuje 3 silniki składowania wbudowane w MySQL:
- MyISAM - dla tabel, dla których przewidywany współczynnik zapisów jest niższy niż 0,1. W zasadzie, są to wszystkie tabele odpowiedzialne za opis świata gry i część tabel przechowujących informacje o rozgrywce
- MEMORY - dla wybranych tabel tymczasowych o bardzo wysokim współczynniku zapisów i znacznej liczbie odczytów
- InnoDB - dla często aktualizowanych tabel przechowujących informacje o rozgrywce
Jak widać, nie zdecydowałem się ma monolityczny model silników składowania. Wszędzie tam, gdzie zalety silnika InnoDB (blokada na poziomie rekordów, transakcje) mogą być wykorzystane, mechanizm jest w użyciu. Tabele tylko do odczytu są zarządzane przez MyISAM. Osobnym przypadkiem są tabele z danymi tymczasowymi z mechnizmem MEMORY. Dane w nich przechowywane są ważne tylko przez kilka minut, a przewidywana liczba równoczesnych rekordów nie przekracza kilkuset. Co więcej, są one często odczytywane i modyfikowane. Mechanizm MEMORY wydaje się dla nich najlepszym rozwiązaniem.
Ostatnim ważnym elementem silnika Pulsara z punktu widzenia bazy danych, są procedury utrzymaniowe świata gry takie jak generowanie towarów hadlowych w portach, zachowania NPC (Non Personal Character, boty), czy dodawanie przedmiotów w lokacjach. Silnik gry Omricon Beta wykonywał takie operacje w wyznaczonych interwałach czasowych globalnie dla całego świata gry. Takie podejście okazało się złe. Same operacje na lokacjach potrafiły na kilka minut obciążyć serwer w takim stopniu, że gra w czasie ‘resetów’ stawała się niemożliwa. Silnik Pulsara wykonuje te operacje ‘rozważniej’ i tylko w tych lokacjach, w których przebywają aktywni gracze. Przykładowo, ‘reset’ portu jest wykonywany tylko jeśli gracz pojawi się nad tym portem (nie częściej niż zdefiniowany interwał) lub wykona w nim dowolną czynność. Takie podejście pozwala na ograniczenie regularnych peaków obciążenia serwera baz danych. Co prawda, w łącznym rozrachunku, liczba operacji może być równa lub nawet większa, lecz jest rozłożona w czasie i nie skutkuje ‘przytkaniem’ silnika.
