Dzisiaj odkryłem, że dysk twardy, w którym przechowywane są moje bazy danych, był pełny. Stało się to wcześniej, zwykle przyczyna jest dość oczywista. Zwykle występuje błędne zapytanie, które powoduje, że ogromne wycieki powodują tempdb i rosną aż do zapełnienia dysku. Tym razem stało się nieco mniej oczywiste, co się stało, ponieważ tempdb nie był przyczyną pełnego dysku, lecz samą bazę danych.
Fakty:
- Zwykła wielkość bazy danych wynosi około 55 GB, wzrosła do 605 GB.
- Plik dziennika ma normalny rozmiar, plik danych jest ogromny.
- Plik danych ma 85% dostępnego miejsca (interpretuję to jako „powietrze”: miejsce, które zostało wykorzystane, ale zostało zwolnione. SQL Server rezerwuje całe miejsce po przydzieleniu).
- Rozmiar Tempdb jest normalny.
Znalazłem prawdopodobną przyczynę; jest jedno zapytanie, które wybiera o wiele za dużo wierszy (złe połączenie powoduje wybór 11 miliardów wierszy, gdzie oczekuje się kilkuset tysięcy). Jest to SELECT INTO
zapytanie, które sprawiło, że zastanawiałem się, czy mógł wystąpić następujący scenariusz:
- WYBIERZ INTO jest wykonywany
- Tabela docelowa jest tworzona
- Dane są wstawiane zgodnie z ich wyborem
- Dysk zapełnia się, powodując awarię wkładki
- SELECT INTO jest przerywane i wycofywane
- Wycofanie zwalnia miejsce (dane już wstawione są usuwane), ale SQL Server nie zwalnia zwolnionego miejsca.
Jednak w tej sytuacji nie spodziewałbym się, że utworzona przez nią tabela SELECT INTO
nadal będzie istnieć, powinna zostać usunięta przez wycofanie. Testowałem to:
BEGIN TRANSACTION
SELECT T.x
INTO TMP.test
FROM (VALUES(1))T(x)
ROLLBACK
SELECT *
FROM TMP.test
To skutkuje:
(1 row affected)
Msg 208, Level 16, State 1, Line 8
Invalid object name 'TMP.test'.
Jednak tabela docelowa istnieje. Rzeczywiste zapytanie nie zostało jednak wykonane w jawnej transakcji, czy to może wyjaśnić istnienie tabeli docelowej?
Czy założenia, które tu naszkicowałem, są prawidłowe? Czy to prawdopodobny scenariusz?
źródło