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 INTOzapytanie, 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 INTOnadal 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?
