W poprzednim poście wspominałem o mechanizmie LOCKów w MyISAM. Zasadniczo wygląda to tak, że MyISAM obsługuje LOCKi na poziomie tabeli – jeśli dane są odczytywane przez jedno zapytanie, to nie ma możliwości aby w tym samym czasie wykonywana była modyfikacja danych w tej tabeli (wyjątkiem jest opisywana w poprzednim poście sytuacja). Domyślnie pierwszeństwo mają zapytania, które modyfikują dane. Dopiero jak żaden INSERT/UPDATE/DELETE nie wisi w kolejce, wykonywane są SELECTy. Czasami dobrze byłoby mieć możliwość zmiany tego zachowania. Jak to zrobić?

Jednym z rozwiązań jest po prostu ustawienie zmiennej low-priority-updates na ‚yes’. Zmienna ta może być definiowana na poziomie globalnym lub sesji. Wprowadzenie takiej zmiany odwraca domyślne ustawienie i w tym momencie wyższy priorytet będą miały zapytania typu SELECT. Zmiana ta jednak dotyczy wszystkich zapytań a w pewnych sytuacjach dobrze by było móc sterować priorytetem poszczególnych zapytań. Są i na to sposoby.

Jednym z nich jest modyfikator HIGH_PRIORITY do zapytania typu SELECT:

SELECT HIGH_PRIORITY * FROM tabela WHERE id=145;

Zapytanie takie będzie wykonywane przed każdym zapytaniem modyfikującym dane. Dzięki dodaniu tego modyfikatora wiemy, że otrzymamy wynik tak szybko, jak tylko będzie to możliwe do zrealizowania przez serwer MySQL. Co ciekawe, tego typu modyfikator można także stosować w przypadku zapytań INSERT – wykorzystuje się go w momencie gdy przestawimy zmienną low-priority-updates – wtedy każdy INSERT ma niższy priorytet od SELECTa, ale przy pomocy tego modyfikatora można to zmienić dla danego zapytania.

Kolejną możliwością jest zastosowanie modyfikatora LOW_PRIORITY. Dostępny jest on dla zapytań typu INSERT, UPDATE, DELETE, REPLACE. Przykładowo:

INSERT LOW_PRIORITY INTO tabela (id) VALUES (10);

Działa on tak, że zapytanie z tym modyfikatorem wykonywane jest z niższym priorytetem, czyli w momencie gdy w kolejce nie ma już żadnych zapytań typu SELECT. Jest to dokładnie odpowiednik konstrukcji:

SET <span style="font-family: verdana">low-priority-updates</span>=yes;
INSERT INTO tabela (id) VALUES (10);
SET <span style="font-family: verdana">low-priority-updates</span>=no;

Kolejną możliwością jest zastosowanie konstrukcji:

INSERT DELAYED INTO tabela (id) VALUES (10);

Modyfikator ten działa tak, że serwer od razu zwraca potwierdzenie wykonania zapytania do klienta, a następnie buforuje sobie zapytanie w pamięci i czeka z jego wykonaniem do momentu, w którym żaden inny wątek nie korzysta z danej tabeli. Takie rozwiązanie dobrze jest stosować w sytuacjach, gdy ruch do tabeli to głównie INSERTy, a co pewien czas wykonywane są długie SELECTy – np. w tabeli gromadzone są jakiegoś rodzaju logi, tak więc dane są dodawane na bieżąco, a co pewien czas generowane są skomplikowane raporty. Bez zastosowania takiego modyfikatora, w momencie gdy wykonywany jest SELECT wszystkie połączenia, które chcą dodać dane do tabeli musiałyby czekać na zakończenie wykonywania SELECTa. Dzięki zastosowaniu DELAYED klient łączy się z serwerem, wykonuje zapytanie, serwer odpowiada ok i się rozłącza. Klient może zająć się innymi czynnościami. Serwer natomiast buforuje wszystkie INSERTy i czeka aż SELECT się zakończy. Wtedy dopiero wykonywane są INSERTy. Dodatkową teoretyczną zaletą w takiej sytuacji jest to, że INSERTy wykonywane są hurtowo – wszystkie jednocześnie. Dzięki temu mamy oszczędność operacji dyskowych, bo raz że tego typu zapis będzie sekwencyjny, a dwa, że za jednym zamachem zapisanych zostanie więcej danych. Oczywiście, tego typu rozwiązanie ma także swoje wady. Po pierwsze, może się zdarzyć, że jeśli SELECTów będzie więcej, to zapytania INSERT DELAYED nie zostaną nigdy wykonane – będą w nieskończoność czekać aż zwolni się tabela (dlatego też ich zastosowanie ograniczone jest zazwyczaj do specyficznych sytuacji w których zapytań SELECT nie ma dużo). Po drugie, INSERTy buforowane są w pamięci. Jeśli w tym czasie serwer MySQL przestanie poprawnie funkcjonować, dane oczywiście są tracone. To drugi powód, dla którego sztandarowym przykładem na zastosowanie INSERT DELAYED jest logowanie czegoś do tabeli – zazwyczaj w przypadku logów nie jest kluczowe aby dokładnie wszystkie dane do bazy trafiły – przy większej ilości wpisów chodzi bardziej o statystykę niż o zalogowanie każdego pojedynczego wywołania.

Opisane powyżej modyfikatory można stosować w celu zniwelowania jednej z wad MyISAM, jaką jest blokowanie na poziomie tabeli – przykładowo, jesteśmy w stanie ograniczyć wpływ nagromadzenia się INSERTów na szybkość wykonywania się zapytań typu SELECT. Przydatna opcja, choć trzeba za każdym razem pamiętać o konsekwencjach – długi HIGH_PRIORITY SELECT zablokuje na dłuższy czas modyfikowanie danych w tabeli, zastosowanie INSERT DELAYED w sytuacji gdy SELECTów jest sporo skutkuje długim czasem oczekiwania na zmodyfikowanie danych przez serwer i potencjalną możliwością utraty tych danych. Jak to często w przypadku MySQL bywa, możliwości na zrealizowanie danego problemu jest sporo – trzeba samodzielnie wybrać najbardziej pasującą do danej sytuacji.