Чем эти два отката SQL Server отличаются?

13

В SQL Server 2008 R2 чем отличаются эти два отката:

  1. Выполните ALTERоператор в течение нескольких минут, а затем нажмите «Отменить выполнение». Для полного отката требуется несколько минут.

  2. Выполните ту же ALTERинструкцию, но убедитесь, что LDFфайл недостаточно велик для успешного завершения. Как только LDFпредел достигнут, и «автоматический рост» не разрешен, выполнение запроса немедленно останавливается (или происходит откат) с этим сообщением об ошибке:

The statement has been terminated.
Msg 9002, Level 17, State 4, Line 1
The transaction log for database 'SampleDB' is full. 
To find out why space in the log cannot be reused, see the 
log_reuse_wait_desc column in sys.databases

Как эти два отличаются по следующим пунктам?

  1. Почему второй «откат» происходит мгновенно? Я не совсем уверен, можно ли это назвать откатом. Я предполагаю, что журнал транзакций записывается по мере выполнения и, как только он осознает, что не хватает места для полного завершения задачи, он просто останавливается с каким-то сообщением «конец», без фиксации.

  2. Что происходит, когда первый откат занимает столько времени (откат однопоточный)?
    2.1. SQL Server возвращается и отменяет записи, сделанные в LDFфайле?
    2.2. Размер LDFфайла становится меньше в конце отката (с DBCC SQLPERF(LOGSPACE))

  3. Еще один вопрос: во втором сценарии SQL Server начинает LDFдовольно быстро использовать файл. В моем случае он увеличился с 18% до 90% за первые несколько минут (<4 минут). Но как только он достиг 99%, он оставался там в течение еще 8 минут, в то время как его использование колебалось от 99,1% до 99,8%. Он поднимается (99,8%) и падает (99,2%), а затем снова (99,7%) и падает (99,5%) несколько раз, прежде чем выдается ошибка. Что происходит за кулисами?

Любые ссылки MSDN, которые могли бы помочь объяснить это больше, приветствуются.

По предложению Али Разеги я добавляю perfmon: Disk Bytes/sec

Сценарий 1:

Сценарий 1

Сценарий 2:

Сценарий 2

ToC
источник
Просто быстрый комментарий: размер файла! = Пространство, используемое в файле.
Аарон Бертран
@ Аарон Да, я знаком с этим. Я использовал DBCC SQLPERF (LOGSPACE) для измерения использования. Файл LDF не менялся в течение всего времени, так как я ограничил размер файла до 10 ГБ. Внутреннее использование варьируется.
ToC
1
Я подозреваю, что второй откат появляется мгновенно, потому что ошибка сообщается после завершения отката. Вероятно, это те 8 минут, которые вы наблюдаете в 3., где использование LDF остается довольно постоянным.
Mustaccio
@mustaccio Я бы с тобой согласился, но артефакты указывают в другом направлении. Если на самом деле произошел откат, то использование файла журнала должно вернуться к меньшему числу; и не оставаться на уровне 99,3% при появлении сообщения об ошибке.
ToC
1
Я считаю, что откат занимает место в журнале. Когда журнал заполняется во время отката, БД становится подозрительной. Это не трогается дальше. Это выглядит как мгновенный откат, но откат откладывался до тех пор, пока вы не сможете перевести базу данных в оперативный режим (при наличии свободного места на диске) .; Кроме того, когда журнал заполняется, SQL Server может попытаться освободить место с помощью контрольных точек, которые могут объяснить всплески активности ввода-вывода.
USR

Ответы:

1

Как указано выше, после выполнения большего количества тестов я пришел к расчетным выводам. Я собрал их всех в блоге здесь , но я скопирую часть контента в этот пост для потомков.

Гипотеза (на основе некоторых тестов)

На данный момент у меня нет четкого объяснения, почему это так. Но ниже приведены мои оценки, основанные на артефактах, собранных во время испытаний.

Откат происходит в обоих сценариях. Один - явный откат (пользователь нажимает кнопку «Отмена»), другой - неявный (Sql Server принимает это решение внутренне).

В обоих сценариях трафик, идущий в файл журнала, является согласованным. Смотрите изображения ниже:

Сценарий 1:

Сценарий 1:

Сценарий 2:

Сценарий 2

  • Один из артефактов, который усилил эту линию мышления, - захват Sql Trace в обоих сценариях.

    • Сценарий 1 самоочевиден, иначе когда мы нажимаем «Отмена», он откатывается назад.
    • В сценарии 2 сообщение об ошибке отображается после неявного выполнения «отката». В Sql Trace мы видим сообщение об ошибке «Журнал транзакций для базы данных« SampleDB »заполнен» задолго до того, как сообщение отобразится на экране. Итак, я предполагаю, что откат происходит в обоих сценариях, но сообщение об ошибке «Сценарий 2» отображается после успешного и полного выполнения отката.
  • Сценарий 2, кажется, занимает больше времени, поскольку он продвигается гораздо дальше, поэтому откат занимает больше времени.

Необъяснимое поведение:

  • Почему использование файла журнала сильно отличается?
    • Увеличивается до 90%, затем до 85%, затем до 99% и долго там парит. Я вижу, как это происходит несколько раз: 99,2%, 99,8%, 99,1%, 99,7%. Почему это происходит?
    • Одно из возможных объяснений состоит в том, что может существовать фоновый процесс (что-то вроде очистки журнала), который очищает файл журнала каждые несколько минут. И каждый раз, когда это происходит, некоторые записи очищаются, в результате чего появляется больше свободного места.

Любые идеи, которые помогут объяснить это поведение лучше, приветствуются.

ToC
источник
2
Посоветовавшись с Полом Рэндалом, он подтвердил, что вы пришли к правильному выводу - откат одинаков в обоих случаях.
Пол Уайт 9
0

Я попробовал следующий эксперимент и получил похожие результаты. В обоих случаях fn_dblog () показывает, что происходит откат, и в сценарии 2 это происходит быстрее, чем в сценарии 1.

Кстати, я поместил и MDF, и LDF на один и тот же внешний (USB 2.0) диск.

Мой первоначальный вывод состоит в том, что в этом случае нет разницы в операции отката, и, вероятно, любая очевидная разница в скорости связана с подсистемой ввода-вывода. Это только моя рабочая гипотеза на данный момент.

Сценарий 1:

  • Создайте базу данных с файлом журнала, который начинается с 1 МБ, увеличивается до 4 МБ и имеет максимальный размер 100 МБ.
  • Откройте явную транзакцию, запустите ее на 10 секунд, а затем вручную отмените в SSMS.
  • Посмотрите на число fn_dblog () и резервный размер журнала и проверьте DBCC SQLPERF (LOGSPACE)

Сценарий 2:

  • Создайте базу данных с файлом журнала, который начинается с 1 МБ, увеличивается до 4 МБ и имеет максимальный размер 100 МБ.
  • Откройте явную транзакцию и выполняйте ее до тех пор, пока не заполнится журнал.
  • Посмотрите на число fn_dblog () и резервный размер журнала и проверьте DBCC SQLPERF (LOGSPACE)

Результаты монитора производительности:

Сценарий 1: *** Сценарий 1 ***

Сценарий 2: *** Сценарий 2 ***

Код:

ИСПОЛЬЗОВАТЬ [мастер];
ИДТИ

IF DATABASEPROPERTYEX (N'SampleDB ', N'Version')> 0
НАЧАТЬ
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        С немедленным отступлением;
    DROP DATABASE [SampleDB];
КОНЕЦ;
ИДТИ

СОЗДАТЬ БАЗУ ДАННЫХ [SampleDB] НА ПЕРВИЧНОЙ 
( 
      NAME = N'SampleDB '
    , FILENAME = N'E: \ data \ SampleDB.mdf ' 
    , РАЗМЕР = 3 МБ 
    , FILEGROWTH = 1 МБ 
)
ВХОД 
( 
      NAME = N'SampleDB_log '
    , FILENAME = N'E: \ data \ SampleDB_log.ldf '
    , РАЗМЕР = 1 МБ 
    MAXSIZE = 100 МБ 
    , FILEGROWTH = 4 МБ 
);
ИДТИ

USE [SampleDB];
ИДТИ

- Добавить таблицу
CREATE TABLE dbo.test
(
    c1 CHAR (8000) NOT NULL ПО УМОЛЧАНИЮ (по умолчанию, 8000)
) НА [ПЕРВИЧНО];
ИДТИ

- Убедитесь, что мы не псевдо-простая модель восстановления
РЕЗЕРВНАЯ БАЗА ДАННЫХ SampleDB
TO DISK = 'NUL';
ИДТИ

- Резервное копирование файла журнала
РЕЗЕРВНЫЙ ЛОГ SampleDB
TO DISK = 'NUL';
ИДТИ

- Проверьте используемое пространство журнала
DBCC SQLPERF (LOGSPACE);
ИДТИ

- Сколько записей видно с помощью fn_dblog ()?
SELECT * FROM fn_dblog (NULL, NULL); - около 9 в моем случае

/ **********************************
             СЦЕНАРИЙ 1
********************************** /
- Откройте новую транзакцию, а затем откатите ее
НАЧАЛО СДЕЛКИ

    INSERT INTO dbo.test DEFAULT VALUES;
    GO 10000 - пусть выполняется в течение 10 секунд, а затем нажмите отмену в окне запроса SSMS

    - отменить транзакцию
    - Это должно занять пару секунд, чтобы закончить


- Нет необходимости откатывать транзакцию, так как отмена уже сделала это для вас.
-- Просто попробуйте. Вы получите эту ошибку
- Сообщение 3903, уровень 16, состояние 1, строка 1
- Запрос ROLLBACK TRANSACTION не имеет соответствующей BEGIN TRANSACTION.
ROLLBACK TRANSACTION;

- Какое место занимает журнал? Выше 100%.
DBCC SQLPERF (LOGSPACE);
ИДТИ

- Сколько записей видно с помощью fn_dblog ()?
ВЫБРАТЬ * 
FROM fn_dblog (NULL, NULL); - Около 91 926 в моем случае

Общий резерв журнала, показанный fn_dblog ()?
ВЫБЕРИТЕ СУММУ ([Log Reserve]) AS [Total Log Reserve]
FROM fn_dblog (NULL, NULL); - около 88,72 МБ


/ **********************************
             СЦЕНАРИЙ 2
********************************** /
- сдуть БД и начать все сначала
ИСПОЛЬЗОВАТЬ [мастер];
ИДТИ

IF DATABASEPROPERTYEX (N'SampleDB ', N'Version')> 0
НАЧАТЬ
    ALTER DATABASE [SampleDB] SET SINGLE_USER
        С немедленным отступлением;
    DROP DATABASE [SampleDB];
КОНЕЦ;
ИДТИ

СОЗДАТЬ БАЗУ ДАННЫХ [SampleDB] НА ПЕРВИЧНОЙ 
( 
      NAME = N'SampleDB '
    , FILENAME = N'E: \ data \ SampleDB.mdf ' 
    , РАЗМЕР = 3 МБ 
    , FILEGROWTH = 1 МБ 
)
ВХОД 
( 
      NAME = N'SampleDB_log '
    , FILENAME = N'E: \ data \ SampleDB_log.ldf '
    , РАЗМЕР = 1 МБ 
    MAXSIZE = 100 МБ 
    , FILEGROWTH = 4 МБ 
);
ИДТИ

USE [SampleDB];
ИДТИ

- Добавить таблицу
CREATE TABLE dbo.test
(
    c1 CHAR (8000) NOT NULL ПО УМОЛЧАНИЮ (по умолчанию, 8000)
) НА [ПЕРВИЧНО];
ИДТИ

- Убедитесь, что мы не псевдо-простая модель восстановления
РЕЗЕРВНАЯ БАЗА ДАННЫХ SampleDB
TO DISK = 'NUL';
ИДТИ

- Резервное копирование файла журнала
РЕЗЕРВНЫЙ ЛОГ SampleDB
TO DISK = 'NUL';
ИДТИ

- Теперь давайте взорвем файл журнала в нашей транзакции
НАЧАЛО СДЕЛКИ
    INSERT INTO dbo.test DEFAULT VALUES;
    GO 10000

- Откат никогда не срабатывает. Попытайся. Вы получите ошибку.
- Сообщение 3903, уровень 16, состояние 1, строка 1
- Запрос ROLLBACK TRANSACTION не имеет соответствующей BEGIN TRANSACTION.
ROLLBACK TRANSACTION;

- Файл журнала заполнен на 100%? 
DBCC SQLPERF (LOGSPACE);

- Сколько записей видно с помощью fn_dblog ()?
ВЫБРАТЬ * 
FROM fn_dblog (NULL, NULL); - Около 91 926 в моем случае
ИДТИ

Общий резерв журнала, показанный fn_dblog ()?
ВЫБЕРИТЕ СУММУ ([Log Reserve]) AS [Total Log Reserve]
FROM fn_dblog (NULL, NULL); - 88,72 МБ
ИДТИ
ooutwire
источник
Спасибо за подробные тесты. Проведя больше тестов, я пришел к аналогичному выводу. Итак, я написал сообщение в блоге . Для меня Сценарий 2 требует больше времени для отката, потому что объем работы, выполненной в Сценарии 2, прежде чем Sql Server осознает необходимость отката, больше, чем Сценарий 1.
ToC