Zapytania UPDATE w poprzednich dwóch pytaniach ( Pytanie 1 , Pytanie 2 ) uderzają w tabelę „ludzie” według KLUCZA PODSTAWOWEGO z blokowaniem na poziomie wiersza. Oto, co powiedziałem w pytaniu 1 z 6 czerwca 2011 10:03
Wszystkie transakcje dotyczą klucza PODSTAWOWEGO. Ponieważ PODSTAWOWY jest indeksem klastrowym w InnoDB, klucz PODSTAWOWY i sam wiersz są razem. Zatem przemierzanie rzędu i KLUCZ PODSTAWOWY są jednym i tym samym. Dlatego każda blokada indeksu na KLUCZU PODSTAWOWYM jest również blokadą na poziomie wiersza.
Nie rozważono jeszcze czegoś innego, co może przypisywać powolność indeksom: Zastosowanie indeksów NIEJEDNOCZESNYCH w InnoDB. Każde indeksowane wyszukiwanie w InnoDB przy użyciu nieunikalnych indeksów ma również identyfikator wiersza każdego wiersza dołączony do klucza nieunikalnego. RowID zasadniczo eminuje z indeksu klastrowanego . Aktualizowanie nieunikalnych indeksów MUSI ZAWSZE oddziaływać na indeks klastrowany NAWET JEŚLI TABELA NIE MA PODSTAWOWEGO KLUCZA.
Inną rzeczą do przemyślenia jest proces zarządzania węzłami BTREE w indeksie. Czasami wymaga podziału strony węzłów. Wszystkie wpisy w węźle BTREE indeksów nieunikalnych zawierają pola nieunikalne PLUS identyfikator rowID w indeksie klastrowym. Aby właściwie złagodzić podział takich stron BTREE bez zakłócania integralności danych, wiersz powiązany z rowID musi wewnętrznie zostać zablokowany na poziomie wiersza.
Jeśli tabela „ludzie” ma wiele nieunikalnych indeksów, przygotuj się na dużą liczbę stron indeksu w obszarze tabel, a także od czasu do czasu zakrywających cię małymi blokadami wierszy.
Jest jeszcze jeden czynnik, który nie jest tak oczywisty: Kluczowa populacja
Czasami, gdy indeks jest zapełniany, kluczowe wartości tworzące indeksy mogą z czasem zostać zniekształcone i spowodować, że MySQL Query Optimizer przełączy się z wyszukiwania kluczowego, na skanowanie indeksów, a na koniec na pełne skanowanie tabeli. Nie możesz tego kontrolować, chyba że przeprojektujesz tabelę za pomocą nowych indeksów, aby skompensować krzywe klawisze. Podaj strukturę tabeli dla tabeli „ludzie”, liczbę tabel „osób” oraz dane wyjściowe dotyczące indeksów show dla tabeli „osoby” .
Nawet jeśli zapytania używają tylko KLUCZA PIERWOTNEGO, przekrzywienie kluczy w nieindywidualnych indeksach nadal wymaga równoważenia BTREE i podziału strony. Takie zarządzanie BTREE spowoduje znaczące spowolnienie z powodu sporadycznych blokad na poziomie wierszy, których nie zamierzałeś.
AKTUALIZACJA 14.06.2011 22:19
Pytania od pytania 1
UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>',
iphone_device_time = '2011-06-06 05:35:09', last_checkin = '2011-06-06 05:24:42',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125
UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>-<id>-<id>',
iphone_device_time = '2011-06-06 05:24:42', last_checkin = '2011-06-06 05:35:07',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125
Wyobraź sekwencję zdarzeń
- Znajdź wiersz według klucza podstawowego
- Zablokuj wiersz i indeks klastrowany
- Utwórz dane MVCC dla wszystkich aktualizowanych kolumn
- Indeksowane są cztery kolumny (adres e-mail, id_firmy, id_urządzenia iphone, id_blobu_obrazu)
- Każdy indeks wymaga zarządzania BTREE
- W obrębie tej samej przestrzeni transakcji kroki 1-5 próbują zostać powtórzone w tym samym wierszu, aktualizując te same kolumny (wyślij to samo w obu zapytaniach, nazwa_obsługi taka sama w obu zapytaniach, picture_blob_id taka sama w obu zapytaniach, iphone_device_id różne)
Pytania od pytania 2
UPDATE people SET iphone_device_id=NULL
WHERE iphone_device_id='iphone:<device_id_blah>' AND people_id<>666;
UPDATE people SET company_id = 444, name = 'Dad', password = '<pass>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@gmail.com',
phone = NULL, mobile = NULL, iphone_device_id = 'iphone:<device_id_blah>',
iphone_device_time = '2011-06-06 19:12:29', last_checkin = '2011-06-07 02:49:47',
location_lat = <lat>, location_long = <lng>, gps_strength = 66,
picture_blob_id = 1661,
authority = 1, active = 1, date_created = '2011-03-20 19:18:34',
last_login = '2011-06-07 11:15:01', panic_mode = 0, battery_level = 0.55,
battery_state = 'unplugged' WHERE people_id = 666;
Te dwa zapytania są jeszcze bardziej mylące, ponieważ pierwsze zapytanie aktualizuje wszystko oprócz people_id 666. Setki wierszy są boleśnie blokowane tylko pierwszym zapytaniem. Drugim zapytaniem jest aktualizacja people_id 666 uruchamiający 5 sekwencji zdarzeń. Pierwsze zapytanie uruchamia te same 5 sekwencji zdarzeń w każdym zaangażowanym wierszu, z wyjątkiem people_id 666, ale indeks dla iphone_device_id jest na kursie interecept z dwoma różnymi zapytaniami. Ktoś musi zablokować strony BTREE według kolejności zgłoszeń.
W obliczu tych dwóch par zapytań na kursie kolizyjnym zablokowanie tych samych stron BTREE w ramach jednego indeksu może być kłopotliwe dla InnoDB lub dowolnego RDBMS zgodnego z ACID. Dlatego spowolnienie indeksów jest przeznaczeniem tych par zapytań, chyba że możesz zagwarantować, że zapytania będą działały z AUTOCOMMIT = 1 lub zezwalając na nieczytelne odczyty (chociaż takie kolizje sprawiają, że ODCZYTYWANIE ZOBOWIĄZANIA i ODCZYTYWANIE NIEZGODNE są koszmarem dla MVCC).
AKTUALIZACJA 15.06.2011 10:29
@RedBlueThing: W zapytaniach z pytania 2 pierwsze zapytanie jest zapytaniem o zakres, więc uzyskuje się wiele blokad wierszy. Zauważ również, że oba zapytania próbują zablokować tę samą przestrzeń o numerze 0 strona nr 4611 n bity 152 są blokowane w kluczu podstawowym, czyli indeksie klastrowym.
Aby mieć pewność, że aplikacja działa przynajmniej w oparciu o oczekiwaną serię zdarzeń, możesz wypróbować dwie różne opcje:
Opcja 1) Konwertuj tę tabelę na MyISAM (przynajmniej na serwerze programistycznym). Każda AKTUALIZACJA, WSTAW i USUŃ nałoży blokadę pełnego stołu na zasadzie „kto pierwszy, ten lepszy”.
Opcja 2) Spróbuj użyć poziomu izolacji SERIALIZABLE . Spowoduje to zablokowanie wszystkich zamierzonych wierszy w trybie UDOSTĘPNIANIA.
Sekwencja zdarzeń, której się spodziewasz, ulegnie awarii lub zakończy się powodzeniem przy użyciu tych dwóch alternatywnych opcji. Jeśli obie te opcje zawiodą, musisz przejrzeć swoją aplikację i ustalić kolejność wykonywania zapytań. Po ustaleniu tego priorytetu możesz po prostu cofnąć te opcje (w przypadku opcji 1 wróć do InnoDB, w przypadku opcji 2 wróć do domyślnego poziomu izolacji [przestań używać SERIALIZABLE]).