Dlaczego InnoDB nie przechowuje liczby wierszy?

19

Wszyscy wiedzą, że w tabelach, które używają InnoDB jako silnika, zapytania takie SELECT COUNT(*) FROM mytablesą bardzo niedokładne i bardzo wolne, szczególnie gdy tabela staje się większa i ciągłe wstawianie / usuwanie wierszy podczas wykonywania tego zapytania.

Tak jak to rozumiem, InnoDB nie przechowuje liczby wierszy w zmiennej wewnętrznej, co jest przyczyną tego problemu.

Moje pytanie brzmi: dlaczego tak jest? Czy tak trudno byłoby przechowywać takie informacje? To ważna informacja, którą należy znać w tak wielu sytuacjach. Jedyną trudnością, jaką widzę, jeśli taki wewnętrzny licznik zostałby zaimplementowany, jest zaangażowanie transakcji: jeśli transakcja jest niezatwierdzona, czy liczycie wstawione wiersze, czy nie?

PS: Nie jestem ekspertem od DB, jestem po prostu kimś, kto ma MySQL jako proste hobby. Więc jeśli tylko zapytałem o coś głupiego, nie bądź zbyt krytyczny: D.

Radu Murzea
źródło
6
Powoli tak. Niedokładnie, nie. Jest wolny, ponieważ daje dokładny wynik. Gdy masz tabelę wierszy o wielkości 200 mln i możliwe jest, że wiele innych transakcji wstawia / usuwa do tej samej tabeli, być może wiele wierszy na sekundę, kolejnym pytaniem jest „czy potrzebujesz dokładnej liczby?”
ypercubeᵀᴹ
@ypercube Wiem, że widziałem kilka razy w phpmyadmin niektóre wartości zliczania wierszy, które były bardzo wyłączone. Dodatkowo istnieje komentarz, który mówi „może nie być dokładny”.
Radu Murzea
1
@RaduMurzea Użytkownicy phpMyAdmin to alternatywna metoda obliczania liczby tabel dla tabel InnoDB z powodów prędkości, o których wiesz. To tutaj pojawia się niedokładność, o której wspomniałeś. Rzeczywiste SELECT COUNT(*) FROM ...zapytania są dokładne. Jeśli wolisz, phpMyAdmin można skonfigurować tak, aby zawsze używał dokładnej liczby wierszy kosztem szybkości. Więcej informacji: stackoverflow.com/questions/11926259/…
DOOManiac

Odpowiedzi:

9

Zgadzam się z @RemusRusanu (+1 za odpowiedź)

SELECT COUNT(*) FROM mydb.mytablew InnoDB zachowuje się tak, jak powinien transakcyjny silnik pamięci. Porównaj to z MyISAM.

MyISAM

W mydb.mytableprzypadku tabeli MyISAM uruchamianie SELECT COUNT(*) FROM mydb.mytable;jest jak uruchamianie SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';. To powoduje szybkie sprawdzenie liczby wierszy w nagłówku tabeli MyISAM.

InnoDB

Jeśli mydb.mytablejest to stolik InnoDB, dostajesz mnóstwo rzeczy. Prowadzisz MVCC, regulując następujące kwestie:

  • ib_logfile0 / ib_logfile1 (Ponów dzienniki)
  • ibdata1
    • Cofnij dzienniki
    • Cofanie
    • Zmiany słownika danych
  • Zarządzanie pulą buforów
  • Izolacja transakcji (4 typy)
    • Powtarzalne odczyty
    • Czytaj Zaangażowane
    • Czytaj Nieprzyjęte
    • Serializowalny

Pytanie InnoDB o liczbę tabel wymaga nawigacji przez te złowieszcze rzeczy. W rzeczywistości nigdy tak naprawdę nie wiadomo, czy SELECT COUNT(*) from mydb.mytableliczy się tylko odczyty powtarzalne, czy obejmuje odczyty, które zostały zatwierdzone i te, które nie zostały zatwierdzone.

Możesz spróbować trochę ustabilizować sytuację, włączając innodb_stats_on_metadata .

Zgodnie z dokumentacją MySQL na temat danych innodb_stats_on_meta_data

Gdy ta zmienna jest włączona (która jest domyślna, jak przed jej utworzeniem), InnoDB aktualizuje statystyki podczas instrukcji metadanych, takich jak POKAŻ STATUS TABELI lub POKAŻ INDEKS, lub podczas uzyskiwania dostępu do tabel INFORMACJE_SCHEMA TABELE lub STATYSTYKA. (Te aktualizacje są podobne do tych, które mają miejsce w przypadku tabeli ANALIZA.) Po wyłączeniu InnoDB nie aktualizuje statystyk podczas tych operacji. Wyłączenie tej zmiennej może poprawić szybkość dostępu dla schematów, które mają dużą liczbę tabel lub indeksów. Może także poprawić stabilność planów wykonania zapytań dotyczących tabel InnoDB.

Wyłączenie go może, ale nie musi, dać bardziej stabilną liczbę w zakresie konfigurowania planów EXPLAIN. Może to wpływać na wydajność SELECT COUNT(*) from mydb.mytablew dobry, zły sposób lub wcale. Spróbuj i zobacz !!!

RolandoMySQLDBA
źródło
16

Na początek nie ma czegoś takiego jak „bieżąca liczba” do przechowywania w zmiennej. Podobne zapytanie SELECT COUNT(*) FROM ...jest zależne od bieżącego poziomu izolacji i wszystkich jednoczesnych oczekujących transakcji. W zależności od poziomu izolacji zapytanie może wyświetlać lub nie wyświetlać wierszy wstawianych lub usuwanych przez oczekujące niezatwierdzone transakcje. Jedynym sposobem na odpowiedź jest policzenie wierszy widocznych dla bieżącej transakcji.

Zauważ, że nawet nie dotknąłem jeszcze bardziej drażliwego tematu współbieżnych transakcji, które zaczynają się lub kończą podczas liczenia. Nie wspominając już o wycofywaniu ...

Remus Rusanu
źródło
1
Ok, więc to zależy od poziomu izolacji, co ma sens. Ale nadal można go wdrożyć.
Radu Murzea
@SoboLAN Istnieje wiele powodów, dla których nie powinno i nie powinno być, z których większość wymieniono powyżej. Czy zaimplementowałbyś to, utrzymując listę zliczeń na tabelę na początek transakcji (niezależnie od SCN Oracle w MySQL)? Zarządzanie takimi liczbami byłoby ogromnym narzutem - pomyśl o bazie danych z setkami lub tysiącami równoczesnych sesji, z których każda wykonuje duże ilości INSERT / DELETE w tej samej tabeli. Niemożliwe do utrzymania.
Philᵀᴹ
Wdrożenie tego jest dość trudne. Pomyśl tylko, że liczba musi zostać utrwalona w bazie danych, co oznacza, że ​​gdzieś w metadanych, a liczba ta musi być utrzymywana przez każdą transakcję, która wstawia lub usuwa wiersz. Jak zablokowałbyś te metadane? A jak poradziłbyś sobie z wycofywaniem? Jest daleki od trywialnego. Wynik byłby użyteczny w przypadku bardzo bardzo wąskiego podzbioru zapytań.
Remus Rusanu
3
@JackDouglas Interesujące. Z tego, co widziałem w przeszłości, COUNT(*)zapytania są w rzeczywistości rzadko potrzebne i zwykle wynikają z braku doświadczenia programisty (policz wiersze, zanim je wybierzemy!) Lub złego projektu aplikacji.
Philᵀᴹ
1
@SoboLAN - nie, nie zrobiłby tego. Posiadanie usługi aktualizującej tabelę statystyk w określonych odstępach czasu jest znacznie lepsze. Wyobraź sobie, że masz dużą bazę danych i kilku administratorów odpytujących większość tabel SELECT COUNT(*), dodaj niezoptymalizowaną WHEREtabelę, a będziesz mieć kilku użytkowników rzucających db na kolana, aby uzyskać kilka wątpliwych użytecznych liczników statystyk.
NB
0

Chociaż teoretycznie byłoby możliwe utrzymanie dokładnej liczby wierszy dla danej tabeli za pomocą InnoDB, byłoby to kosztem dużej ilości blokowania, co negatywnie wpłynęłoby na wydajność. Różniłby się również w zależności od poziomu izolacji.

MyISAM już blokuje na poziomie stołu, więc nie ma tam dodatkowych kosztów.

Rzadko wymagam liczenia wierszy dla tabeli, chociaż dość często używam COUNT (*). Zasadniczo mam dołączoną klauzulę WHERE. Używając wydajnego indeksu dla małego zestawu wyników, stwierdzam, że są wystarczająco szybkie.

Nie zgadzam się, że liczby są niedokładne. Liczby reprezentują migawkę danych i zawsze uważałem, że są dokładne.

Krótko mówiąc, MySQL pozostawia Ci wdrożenie tego w InnoDB. Możesz przechowywać liczbę i zwiększać / zmniejszać ją po każdym zapytaniu. Jednak łatwiejszym rozwiązaniem jest prawdopodobnie przejście na MyISAM.

Marcus Adams
źródło
2
To nie możliwe, aby zachować dokładne zliczanie wierszy w systemie transakcyjnym. Ponieważ istnieje tyle różnych (i poprawnych) liczby wierszy, ile aktywnych transakcji.
a_horse_w_no_name
5
Podałem tutaj -1 za „Chociaż łatwiejszym rozwiązaniem jest prawdopodobnie przejście na MyISAM”. Nigdy nie zalecałbym przejścia na MyISAM, aby uzyskać liczbę wierszy.
Derek Downey
@ a_horse_with_no_name, więc zgadzasz się, że dla każdej transakcji będzie „poprawne” liczba wierszy. Wydaje mi się to możliwe.
Marcus Adams
1
@DTest, nigdy nie powiedziałem „po prostu uzyskać liczbę wierszy”.
Marcus Adams
@ a_horse_with_no_name, To nie wydaje się właściwe. Z pewnością liczymy tylko liczbę wierszy, kiedy transakcje zostaną zatwierdzone , prawda?
Pacerier