Gdzie InnoDB przechowuje dane transakcji przed ich zatwierdzeniem?

12

Zrobiłem kilka testów w domu READ_COMMITTEDi READ_UNCOMMITTEDprzy użyciu technologii JDBC.

Widzę, że READ_UNCOMMITTEDfaktycznie może odczytać niezaangażowane dane, np. Dane z niektórych transakcji, które nie zostały jeszcze zatwierdzone (może wykonać zapytanie UPDATE).

pytania

  • Gdzie przechowywane są niezaangażowane dane, aby READ_UNCOMMITTEDtransakcja mogła odczytać nieprzypisane dane z innej transakcji?
  • Dlaczego READ_COMMITTEDtransakcja nie może odczytać niezaangażowanych danych, tj. Wykonać „brudny odczyt”? Jaki mechanizm wymusza to ograniczenie?
Shuzheng
źródło

Odpowiedzi:

11

Gdzie przechowywane są nieprzypisane dane, na przykład, że transakcja READ_UNCOMMITTED może odczytać nieprzypisane dane z innej transakcji?

Nowe niezatwierdzone wersje rekordów (klastrowane PK) są traktowane jako „bieżąca” wersja rekordu na stronie. Mogą być więc przechowywane w puli buforów i / lub w obszarze tabel (np. Tablename.ibd). Transakcje, które następnie muszą zbudować migawkę / widok w czymkolwiek innym niż ODCZYT-NIEZALECANE, muszą zbudować poprzednią wersję wiersza (zgodnie z listą historii) przy użyciu rekordów UNDO (przechowywanych w systemowym obszarze tabel ). Podczas odczytywania niezaangażowanego rekordu InnoDB może również potrzebować odczytać niektóre nieprzypisane rekordy indeksu wtórnego z bufora zmian i zastosować je przed przedstawieniem rekordu użytkownikowi.

To zachowanie może sprawić, że wycofanie w InnoDB będzie stosunkowo drogie. Jest to duży czynnik, który może również prowadzić do potencjalnych problemów z wydajnością spowodowanych przez długotrwałe bezczynne transakcje, które przechowują zaktualizowane rekordy, ponieważ transakcje te będą blokować operacje czyszczenia i rośnie lista historii starych wersji rekordów, a rekordy UNDO potrzebne do odbudowania starych wersji na żądanie będzie nadal rosnąć. Spowalnia nowe transakcje, które muszą czytać starszą / zatwierdzoną wersję rekordu, ponieważ muszą przeglądać coraz dłuższą listę historii - która jest pojedynczo połączoną listą rekordów UNDO - i wykonywać więcej pracy w celu odtworzenia stara wersja rekordu. W rezultacie używasz wielu cykli procesora (nie wspominając o wewnętrznych operacjach blokujących: muteksach, blokach rw, semaforach itp.

Mam nadzieję, że to ma sens? :)

Jako FYI, w MySQL 5.7 możesz przenieść obszar tabel UNDO i wylogować się z systemowego obszaru tabel i automatycznie je obciąć. Mogą rosnąć dość duże, jeśli masz długoterminową transakcję, która zapobiega operacjom czyszczenia, co skutkuje bardzo długą i wciąż rosnącą długością listy historii. Przechowywanie ich w systemowym obszarze tabel było najczęstszą przyczyną ogromnego / rosnącego pliku ibdata1, którego z kolei nie można obciąć / skurczyć / odkurzyć, aby później odzyskać to miejsce.

Matt Lord
źródło
4

Zapytałeś

gdzie przechowywane są niezaangażowane dane, aby transakcja READ_UNCOMMITTED mogła odczytać nieprzypisane dane z innej transakcji?

Aby odpowiedzieć na twoje pytanie, musisz wiedzieć, jak wygląda architektura InnoDB.

Poniższe zdjęcie zostało stworzone lata temu przez CTO Percona Vadima Tkachenko

Architektura InnoDB

Według dokumentacji MySQL na temat modelu transakcji i blokowania InnoDB

COMMIT oznacza, że ​​zmiany dokonane w bieżącej transakcji są trwałe i stają się widoczne dla innych sesji. Natomiast instrukcja ROLLBACK anuluje wszystkie modyfikacje dokonane przez bieżącą transakcję. Zarówno COMMIT, jak i ROLLBACK zwalniają wszystkie blokady InnoDB ustawione podczas bieżącej transakcji.

Ponieważ COMMIT i ROLLBACK zarządzają widocznością danych, READ COMMITTED i READ UNCOMMITTED musiałyby polegać na strukturach i mechanizmach, które rejestrują zmiany

  1. Wycofywanie segmentów / Cofnij spację
  2. Ponów dzienniki
  3. Luki Blokuje przeciwko zaangażowanym stołom

Wycofywanie segmentów i Cofnij przestrzeń wiedziałoby, jak wyglądały zmienione dane przed ich zastosowaniem. Ponów dzienniki będą wiedzieć, jakie zmiany należy wprowadzić, aby dane były aktualizowane.

Ty też zapytałeś

dlaczego nie jest możliwe, aby transakcja READ_COMMITTED odczytała niezaangażowane dane, tj. wykonała „brudny odczyt”? Jaki mechanizm wymusza to ograniczenie?

Przerobione są dzienniki powtórzeń, cofanie spacji i zablokowane rzędy. Musisz także wziąć pod uwagę pulę buforów InnoDB (gdzie możesz mierzyć brudne strony za pomocą innodb_max_dirty_pages_pct , innodb_buffer_pool_pages_dirty i innodb_buffer_pool_bytes_dirty ).

W związku z tym READ COMMITTED wiedziałby, jak dane wyglądają na stałe. Dlatego nie trzeba szukać brudnych stron, które nie zostały popełnione. CZYTANIE ZATWIERDZONE byłoby niczym innym jak brudnym odczytem, ​​który został popełniony. PRZECZYTAJ NIEZGŁOSZONE nadal wiedziałby, które wiersze mają zostać zablokowane i jakie dzienniki powtórzeń zostały odczytane lub zignorowane, aby dane były widoczne.

Aby w pełni zrozumieć blokowanie wierszy w celu zarządzania izolacją, zapoznaj się z modelem transakcji i blokowaniem InnoDB

RolandoMySQLDBA
źródło
1
Po pierwsze, dziękuję za odpowiedź i modyfikację mojego postu ... Więc przed COMMIT zmiany nie są widoczne dla innych użytkowników systemu? Tutaj użytkownik dosłownie oznacza transakcję, prawda? Ponieważ READ UNCOMMITTED może odczytywać nieprzydzielone dane, to gdzie ten poziom izolacji odczytuje te dane? Czy może istnieć więcej niż jedno źródło niezaangażowanych danych dla określonego elementu danych w bazie danych? Jeśli tak, to który nieprzydzielony fragment danych zostanie odczytany?
Shuzheng,