Słyszeliście, drodzy Czytelnicy, o mechanizmie Concurent Inserts? Jeśli nie, to posłuchajcie. Jak wiemy, w silniku MyISAM locki tworzone są na poziomie tabel. Jest to z jednej strony dobre, bo np. w ten sposób wykluczamy możliwość zaistnienia deadlocka (a przynajmniej w dużej części – w przypadku użycia kursorów deadlock może się pojawić także w przypadku blokowania na poziomie tabel), z drugiej strony jest to problem, bo tego typu mechanizm blokowania nie radzi sobie w momencie, gdy ruch do danej tabeli obejmuje spory procent zapisów.

Mechanizm locków wygląda następująco – zapytanie typu SELECT blokuje dostęp do tabeli nakładając na nią READ LOCK – inne zapytania mogą odczytywać dane z tej tabeli, ale nie mogą danych modyfikować. Jeśli pojawi się jakiś INSERT/UPDATE/DELETE/REPLACE modyfikujący dane w tabeli, na którą nałożony jest READ LOCK, zapytanie to musi poczekać aż lock zostanie zdjęty. Zapytania modyfikujące dane nakładają na tabelę WRITE LOCK – blokowany jest dostęp do niej dla wszystkich innych zapytań – nie ważne czy są to SELECTy, czy zapytania modyfikujące dane. Z powyższego wynika, że zapytanie modyfikujące dane całkowicie blokuje dostęp do tabeli na czas potrzebny do swojego wykonania – jest to spory spadek wydajności, jeśli takich zapytań jest sporo. Domyślnie, w MySQL pierwszeństwo mają zapytania modyfikujące dane – najpierw wykonywane są one, potem dopiero SELECTy. Można to odpowiednio zmodyfikować i zmniejszyć/zwiększyć priorytet poszczególnych zapytań – będę o tym pisał przy innej okazji. Czy jest sposób aby zwiększyć ilość jednocześnie wykonywujących się zapytań SELECT i INSERT? Tu pojawia się mechanizm concurent inserts. Oprócz tradycyjnego READ LOCK i WRITE LOCK istnieje także trzeci rodzaj blokady: READ LOCAL LOCK. Jeśli w tabeli mamy dziury (jest zdefragmentowana, część rekordów ze środka jest usuniętych), to taka blokada staje się odpowiednikiem zwykłego READ LOCK. Jeśli jednak struktura tabeli to monolit, zaczyna się magia – w takiej sytuacji pojawia się możliwość dopisywania danych do końca tabeli (czyli po prostu wykonywania INSERTów) nawet pomimo tego, że cały czas do tabeli lecą SELECTy (albo na odwrót – pojawia się możliwość czytania z tabeli, pomimo tego że cały czas lecą INSERTy). Dzięki temu mechanizmowi zwiększa się ilość zapytań w jednostce czasu, jaka może zostać wykonana przez serwer MySQL.

Co zrobić jeśli jednak nasza tabela jest dziurawa jak ser szwajcarski? Trzeba ją zdefragmentować – służy do tego polecenie:

OPTIMIZE TABLE tabela;

Po defragmentacji, jeśli tylko samodzielnie nie będziemy kasować jakichś rekordów w tabeli, automatycznie będziemy korzystali z mechanizmu concurent inserts. Skasowanie jakiegoś rekordu w środku tabeli skutkować będzie koniecznością ponownej defragmentacji.

Mechanizm ten jest przydatny, choć ma też swoje wady – główną jest uzależnienie od ciągłości struktury tabeli. Nietrudno sobie wyobrazić mocno obciążony serwer bazodanowy, który nagle zaczyna umierać – następuje znaczny spadek ilości zapytań realizowanych przez niego w jednostce czasu. Okazuje się, że ktoś wykonał jedno zapytanie typu DELETE – tabela się zdefragmentowała i serwer, pozbawiony możliwości stosowania INSERT i SELECT jednocześnie, po prostu przestał dawać rady. Jeśli decydujemy się polegać na tym mechanizmie, konieczne jest dokładne pilnowanie, aby do sytuacji takich jak opisana przed chwilą nie dochodziło. Dodatkowo trzeba by oskryptować serwer tak, aby proces defragmentacji tabeli był wykonywany co pewien czas.