W mojej głównej produkcyjnej bazie danych RAC widzę okresy spowolnienia, a dominującym zdarzeniem oczekiwania w całym systemie jest „kursor:pin S czekaj na X”. Wydarzenie przychodzi i odchodzi, ale od czasu do czasu je widzę. Więc musiałem poznać sedno tego. Zauważ, że nie jest to problem RAC. To zdarzenie można łatwo zobaczyć również w bazach danych o jednym wystąpieniu. Kiedy widzę to w wielu instancjach mojej bazy danych Oracle RAC, dzieje się tak dlatego, że mam wiele sesji z tej samej aplikacji rozłożonych między instancjami, wszystkie robią to samo, a zatem wszystkie mają ten sam problem.
Po pierwsze, o co chodzi w przypadku oczekiwania? Każde z czekań „kursora:” jest wąskim gardłem w puli współdzielonej w obszarze SQL. Dawno temu ta część wspólnej puli była chroniona zatrzaskami. Ale tak jak w przypadku wielu obszarów puli współdzielonej, Oracle używa teraz muteksów. Wraz ze zmianą mechanizmu ochrony mamy teraz nowe zdarzenia oczekiwania.
W przypadku tego konkretnego zdarzenia oczekiwania mamy kursor, który chce udostępnić pin, ale musi poczekać na kolejną sesję, aby zwolnić swój wyłączny muteks. Kursor próbuje zostać przeanalizowany. Ale nie można go przeanalizować, ponieważ inna sesja utrzymuje ten sam muteks.
Sesje oczekujące na to wydarzenie mają trzy główne przyczyny.
- Wysokie twarde parsowanie
- Duża liczba wersji instrukcji SQL
- Błędy
Niestety, istnieje wiele błędów związanych z tym zdarzeniem oczekiwania. Większość z tych, które widziałem, została naprawiona w 11.2.0.4 lub 12.1.0.1, więc jeśli masz opóźnienia w wersjach, rozważ aktualizację do jednej z nowszych wersji Oracle.
Zobaczmy więc, czy możemy przejść przez przykład, aby określić przyczynę problemu. W tym celu użyłem następującego zapytania:
select s.inst_id as inst, s.sid as blocked_sid, s.username as blocked_user, sa.sql_id as blocked_sql_id, trunc(s.p2/4294967296) as blocking_sid, b.username as blocking_user, b.sql_id as blocking_sql_id from gv$session s join gv$sqlarea sa on sa.hash_value = s.p1 join gv$session b on trunc(s.p2/4294967296)=b.sid and s.inst_id=b.inst_id join gv$sqlarea sa2 on b.sql_id=sa2.sql_id where s.event='cursor: pin S wait on X';
Uruchamiając to w jednej z moich produkcyjnych baz danych RAC, otrzymuję następujące dane wyjściowe:
INST BLOCKED_SID BLOCKED_USER BLOCKED_SQL_ID BLOCKING_SID BLOCKING_USER BLOCKING_SQL_ID ---- ----------- ------------ -------------- ------------ ------------- --------------- 4 723 USER12345 cn7m7t6y5h77g 1226 USER12345 cn7m7t6y5h77g 4 723 USER12345 cn7m7t6y5h77g 1226 USER12345 cn7m7t6y5h77g 4 723 USER12345 cn7m7t6y5h77g 1226 USER12345 cn7m7t6y5h77g 4 723 USER12345 cn7m7t6y5h77g 1226 USER12345 cn7m7t6y5h77g 4 1226 USER12345 cn7m7t6y5h77g 1796 USER12345 cn7m7t6y5h77g 4 1226 USER12345 cn7m7t6y5h77g 1796 USER12345 cn7m7t6y5h77g 4 1226 USER12345 cn7m7t6y5h77g 1796 USER12345 cn7m7t6y5h77g 4 1226 USER12345 cn7m7t6y5h77g 1796 USER12345 cn7m7t6y5h77g
Pierwszą rzeczą, na którą należy zwrócić uwagę, jest to, że mutex znajduje się tylko w tej instancji dla baz danych Oracle RAC. W przypadku baz danych o jednym wystąpieniu powyższe zapytanie będzie nadal działać. W przypadku Oracle RAC dane wyjściowe tego zapytania pokażą, w której instancji występuje problem.
W powyższym przykładzie mamy sesję 723 blokowaną przez sesję 1226. Sesja 1226 jest dodatkowo blokowana przez sesję 1796. Zauważ, że wszystkie trzy sesje wysyłają to samo zapytanie z identyfikatorem SQL cn7m7t6y5h77g .
Teraz, gdy znamy identyfikator SQL, możemy łatwo wysłać zapytanie do V$SQL, aby określić instrukcję SQL związaną z problemem. Użyłem tego zapytania, aby uzyskać więcej informacji.
select sql_id,loaded_versions,executions,loads,invalidations,parse_calls from gv$sql where inst_id=4 and sql_id='cn7m7t6y5h77g';
Wynik zapytania V$SQL jest następujący:
SQL_ID LOADED_VERSIONS EXECUTIONS LOADS INVALIDATIONS PARSE_CALLS ------------- --------------- ---------- ---------- ------------- ----------- cn7m7t6y5h77g 1 105 546 308 3513
Widzimy teraz, że to zapytanie ma tylko 1 wersję w obszarze SQL. Dlatego od razu wyeliminowaliśmy jeden z potencjalnych obszarów problemowych. W przyszłym wpisie na blogu omówię zapytania z dużą liczbą wersji w obszarze SQL. Ale to nie jest dzisiaj nasz problem, więc kontynuujemy.
Z powyższego powinno być oczywiste, że istnieje bardzo duża liczba wywołań parsowania. Zapytanie zostało wykonane tylko 105 razy, ale zostało przeanalizowane 3513 razy. Ojej! Wysoka liczba unieważnień prawdopodobnie ma z tym coś wspólnego.
W tym przykładzie mamy teraz dobre pojęcie o tym, na czym polega problem. To jest problem z aplikacją. Aplikacja nadmiernie analizuje zapytanie. Więc odeślemy to z powrotem do rozwoju i zagłębimy się w kod aplikacji. Należy zbadać zwykłe powody nadmiernej analizy.
Gdyby liczba wersji była niska, a nadmierne analizowanie/unieważnianie/ładowania nie stanowiło problemu, podejrzewałbym błąd i wysłałby SR do Oracle Support.