Автор: Aaron Bertrand, Delayed Durability in SQL Server 2014
Отложенная устойчивость (Delayed Durability) — это появившаяся в последний момент в SQL Server 2014, но интересная возможность; её краткая суть в лифтовой презентации звучит буквально так:
«Обменяйте устойчивость на производительность».
Сначала немного предыстории. По умолчанию SQL Server использует журнал упреждающей записи (write-ahead log, WAL), что означает, что изменения записываются в журнал до того, как им разрешено быть зафиксированными. В системах, где записи в журнал транзакций становятся узким местом и где есть умеренная терпимость к потере данных, у вас теперь есть возможность временно приостановить требование ждать сброса журнала и подтверждения. Это буквально убирает букву D из ACID, по крайней мере для небольшой части данных (подробнее об этом позже).
Вы в некотором роде уже идёте на эту жертву сейчас. В полной модели восстановления всегда есть некоторый риск потери данных, просто он измеряется во времени, а не в размере. Например, если вы резервируете журнал транзакций каждые пять минут, вы можете потерять почти до 5 минут данных, если произойдёт что-то катастрофическое. Я говорю здесь не о простом переключении при сбое, а о том, что сервер буквально загорится или кто-то споткнётся о шнур питания — база данных вполне может оказаться невосстановимой, и вам придётся вернуться к моменту последней резервной копии журнала. И это при условии, что вы вообще тестируете свои резервные копии, восстанавливая их куда-нибудь — в случае критического сбоя у вас может не оказаться той точки восстановления, о которой вы думаете. Мы, конечно, склонны не задумываться об этом сценарии, потому что никогда не ожидаем, что плохие вещи™ произойдут.
Как это работает
Отложенная устойчивость позволяет пишущим транзакциям продолжать выполняться так, как если бы журнал уже был сброшен на диск; в действительности записи на диск были сгруппированы и отложены для обработки в фоновом режиме. Транзакция оптимистична; она предполагает, что сброс журнала произойдёт. Система использует блок буфера журнала размером 60 КБ и пытается сбросить журнал на диск, когда этот блок заполняется (в крайнем случае — это может и часто произойдёт раньше). Вы можете установить эту опцию на уровне базы данных, на уровне отдельной транзакции или — в случае изначально компилируемых процедур в In-Memory OLTP — на уровне процедуры. В случае конфликта побеждает настройка базы данных; например, если база данных установлена в disabled, попытка зафиксировать транзакцию с использованием отложенной опции будет просто проигнорирована без сообщения об ошибке. Кроме того, некоторые транзакции всегда полностью устойчивы, независимо от настроек базы данных или параметров фиксации; например, системные транзакции, межбазовые транзакции и операции, затрагивающие FileTable, отслеживание изменений (Change Tracking), отслеживание изменённых данных (Change Data Capture) и репликацию.
На уровне базы данных вы можете использовать:
ALTER DATABASE dbname SET DELAYED_DURABILITY = DISABLED | ALLOWED | FORCED;
Если вы установите ALLOWED, это означает, что любая отдельная транзакция может использовать отложенную устойчивость; FORCED означает, что все транзакции, которые могут её использовать, будут её использовать (исключения выше всё ещё актуальны и в этом случае). Скорее всего, вы захотите использовать ALLOWED, а не FORCED — но последнее может быть полезно в случае существующего приложения, где вы хотите использовать эту опцию повсеместно и при этом минимизировать объём кода, который придётся затронуть. Важно отметить в отношении ALLOWED, что полностью устойчивым транзакциям, возможно, придётся ждать дольше, поскольку они сначала вынудят сбросить любые отложенные устойчивые транзакции.
На уровне транзакции вы можете сказать:
COMMIT TRANSACTION WITH (DELAYED_DURABILITY = ON);
А в изначально компилируемой процедуре In-Memory OLTP вы можете добавить следующую опцию в блок BEGIN ATOMIC:
BEGIN ATOMIC WITH (DELAYED_DURABILITY = ON, ...)
Частый вопрос касается того, что происходит с семантикой блокировок и изоляции. На самом деле ничего не меняется. Блокировки и блокирование всё ещё происходят, и транзакции фиксируются тем же способом и по тем же правилам. Единственная разница в том, что, позволяя фиксации происходить без ожидания сброса журнала на диск, соответствующие блокировки освобождаются гораздо раньше.
Когда её следует использовать
В дополнение к выгоде от того, что транзакциям позволяется продолжаться без ожидания записи в журнал, вы также получаете меньше записей в журнал большего размера. Это может очень хорошо сработать, если в вашей системе высокая доля транзакций, которые фактически меньше 60 КБ, особенно когда диск журнала медленный (хотя я обнаружил схожие преимущества и на SSD, и на традиционном HDD). Это не так хорошо сработает, если ваши транзакции по большей части больше 60 КБ, если они обычно длительные или если у вас высокая пропускная способность и высокая конкурентность. Что может произойти в этом случае: вы можете заполнить весь буфер журнала до завершения сброса, что просто означает перенос ваших ожиданий на другой ресурс и, в конечном итоге, отсутствие улучшения воспринимаемой производительности пользователями приложения.
Другими словами, если ваш журнал транзакций в данный момент не является узким местом, не включайте эту функцию. Как определить, является ли ваш журнал транзакций в данный момент узким местом? Первым индикатором будут высокие ожидания WRITELOG, особенно в сочетании с PAGEIOLATCH_*. У Пола Рэндала есть отличная серия из четырёх частей по выявлению проблем с журналом транзакций, а также по настройке для оптимальной производительности:
- Программа похудения для журнала транзакций
- Ещё больше ЗОЖ для журнала транзакций
- Проблемы конфигурации журнала транзакций
- Transaction Log Monitoring
Также см. эту статью в блоге Кимберли Трипп 8 Steps to Better Transaction Log Throughput» пост команды SQL CAT Diagnosing Transaction Log Performance Issues and Limits of the Log Manager.
Это расследование может привести вас к выводу, что отложенная устойчивость стоит изучения; а может и нет. Тестирование вашей рабочей нагрузки будет самым надёжным способом узнать наверняка. Как и многие другие возможности в SQL Server, эта функция НЕ предназначена для улучшения каждой отдельной рабочей нагрузки — и, как отмечено выше, она может фактически сделать некоторые рабочие нагрузки хуже. См. эту статью в блоге Саймона Харви (Simon Harvey) о других вопросах, которые вам следует задать себе о вашей рабочей нагрузке, чтобы определить, возможно ли пожертвовать частью устойчивости ради лучшей производительности.
Потенциал потери данных
Я упомяну это несколько раз и буду каждый раз добавлять акцент: вам нужно быть терпимыми к потере данных. На хорошо работающем диске максимум, который вы должны потерять при катастрофе — или даже при плановом и корректном завершении работы — это до одного полного блока (60 КБ). Однако в случае, когда ваша подсистема ввода-вывода не справляется, возможно, вы можете потерять столько, сколько весит буфер журнала (~7 МБ).
Для ясности, из документации (выделение моё):
Для отложенной устойчивости нет разницы между неожиданным завершением работы и ожидаемым завершением/перезапуском SQL Server. Как и в случае катастрофических событий, вам следует планировать потерю данных. При плановом завершении/перезапуске некоторые транзакции, которые не были записаны на диск, сначала могут быть сохранены на диск, но вы не должны на это рассчитывать. Планируйте так, как будто завершение/перезапуск, плановый или внеплановый, теряет данные так же, как катастрофическое событие.
Поэтому очень важно взвесить ваш риск потери данных с вашей потребностью облегчить проблемы производительности журнала транзакций. Если вы управляете банком или чем-то, связанным с деньгами, для вас может быть гораздо безопаснее и уместнее переместить журнал на более быстрый диск, чем играть в кости с использованием этой функции. Если вы пытаетесь улучшить время отклика в вашем приложении «Чат-комната веб-геймеров», возможно, риск менее серьёзен.
Вы можете в некоторой степени контролировать это поведение, чтобы минимизировать риск потери данных. Вы можете принудительно сбросить все отложенные устойчивые транзакции на диск одним из двух способов:
- Зафиксировать любую полностью устойчивую транзакцию.
- Вызвать sys.sp_flush_log вручную.
Это позволяет вам вернуться к контролю потери данных во времени, а не в размере; вы могли бы, например, планировать сброс каждые 5 секунд. Но вам нужно будет найти свою «золотую середину»; слишком частый сброс может свести на нет саму выгоду от отложенной устойчивости. В любом случае вам всё равно нужно быть терпимыми к потере данных, даже если это всего лишь <n> секунд.
Вы могли бы подумать, что CHECKPOINT может здесь помочь, но эта операция фактически технически не гарантирует, что журнал будет сброшен на диск.
Взаимодействие с высокой доступностью и аварийным восстановлением
Возможно, вам интересно, как отложенная устойчивость работает с функциями высокой доступности/аварийного восстановления, такими как доставка журнала, репликация и группы доступности. С большинством из них она работает без изменений. Доставка журнала и репликация будут воспроизводить записи журнала, которые были зафиксированы, поэтому там существует тот же потенциал потери данных. С группами доступности в асинхронном режиме мы в любом случае не ждём подтверждения от вторичной реплики, так что она будет вести себя так же, как всегда. Однако с синхронной мы не можем зафиксировать на первичной реплике, пока транзакция не зафиксирована и не закреплена в удалённом журнале. Даже в этом сценарии мы можем получить некоторую выгоду локально, не ожидая записи в локальный журнал, но всё равно должны ждать удалённой активности. Так что в этом сценарии выгоды меньше и, возможно, её вообще нет; кроме, пожалуй, редкого сценария, когда диск журнала первичной реплики действительно медленный, а диск журнала вторичной — действительно быстрый. Подозреваю, что те же условия верны для синхронного/асинхронного зеркалирования, но вы не получите от меня никаких официальных обязательств о том, как эта блестящая функция работает с устаревшей технологией. :-)
Наблюдения за производительностью
Это была бы не правильная статья, если бы я не показал некоторые фактические наблюдения за производительностью. Я настроил 8 баз данных для тестирования эффектов двух различных шаблонов рабочей нагрузки со следующими атрибутами:
- Модель восстановления: simple или full
- Расположение журнала: SSD или HDD
- Устойчивость: отложенная или полностью устойчивая
Я действительно, действительно, действительно лениво-эффективен в таких вещах. Поскольку я хочу избежать повторения одних и тех же операций в каждой базе данных, я временно создал следующую таблицу в базе model:
USE model;
GO
CREATE TABLE dbo.TheTable
(
TheID INT IDENTITY(1,1) PRIMARY KEY,
TheDate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
RowGuid UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID()
);
Затем я построил набор команд динамического SQL для создания этих 8 баз данных, вместо того чтобы создавать их по отдельности и потом возиться с настройками:
-- C and D are SSD, G is HDD
DECLARE @sql NVARCHAR(MAX) = N'';
;WITH l AS (SELECT l FROM (VALUES('D'),('G')) AS l(l)),
r AS (SELECT r FROM (VALUES('FULL'),('SIMPLE')) AS r(r)),
d AS (SELECT d FROM (VALUES('FORCED'),('DISABLED')) AS d(d)),
x AS (SELECT l.l, r.r, d.d, n = CONVERT(CHAR(1),ROW_NUMBER() OVER
(ORDER BY d.d DESC, l.l)) FROM l CROSS JOIN r CROSS JOIN d)
SELECT @sql += N'
CREATE DATABASE dd' + n + ' ON '
+ '(name = ''dd' + n + '_data'','
+ ' filename = ''C:\SQLData\dd' + n + '.mdf'', size = 1024MB)
LOG ON (name = ''dd' + n + '_log'','
+ ' filename = ''' + l + ':\SQLLog\dd' + n + '.ldf'', size = 1024MB);
ALTER DATABASE dd' + n + ' SET RECOVERY ' + r + ';
ALTER DATABASE dd' + n + ' SET DELAYED_DURABILITY = ' + d + ';'
FROM x ORDER BY d, l;
PRINT @sql;
-- EXEC sp_executesql @sql;
Не стесняйтесь запустить этот код сами (с всё ещё закомментированным EXEC), чтобы увидеть, что он создал бы 4 базы данных с отложенной устойчивостью OFF (две в FULL recovery, две в SIMPLE, по одной каждой конфигурации с журналом на медленном диске и по одной с журналом на SSD). Повторите этот шаблон для 4 баз данных с отложенной устойчивостью FORCED — я сделал это для упрощения кода в тесте, а не для отражения того, что я делал бы в реальной жизни (где я, вероятно, хотел бы считать одни транзакции критичными, а другие, скажем так, менее чем критичными).
Для проверки здравости я выполнил следующий запрос, чтобы убедиться, что базы данных имеют правильную матрицу атрибутов:
SELECT d.name, d.recovery_model_desc, d.delayed_durability_desc,
log_disk = CASE WHEN mf.physical_name LIKE N'D%' THEN 'SSD' else 'HDD' END
FROM sys.databases AS d
INNER JOIN sys.master_files AS mf
ON d.database_id = mf.database_id
WHERE d.name LIKE N'dd[1-8]'
AND mf.[type] = 1; -- log
Результаты:
| name | recovery_model | delayed_durability | log_disk |
|---|---|---|---|
| dd1 | FULL | FORCED | SSD |
| dd2 | SIMPLE | FORCED | SSD |
| dd3 | FULL | FORCED | HDD |
| dd4 | SIMPLE | FORCED | HDD |
| dd5 | FULL | DISABLED | SSD |
| dd6 | SIMPLE | DISABLED | SSD |
| dd7 | FULL | DISABLED | HDD |
| dd8 | SIMPLE | DISABLED | HDD |
Релевантная конфигурация 8 тестовых баз данных
Я также несколько раз чисто прогонял тест, чтобы убедиться, что 1 ГБ файла данных и 1 ГБ файла журнала будет достаточно для выполнения всего набора рабочих нагрузок без внесения каких-либо событий автоматического роста в уравнение. Как лучшую практику, я обычно намеренно добиваюсь того, чтобы у систем заказчиков было достаточно выделенного пространства (и правильные оповещения), чтобы событие роста никогда не происходило в неожиданный момент. В реальном мире я знаю, что так бывает не всегда, но это идеал.
Я настроил систему для мониторинга с помощью SQL Sentry — это позволило бы мне легко показать большинство метрик производительности, которые я хотел выделить. Но я также создал временную таблицу для хранения метрик по пакетам, включая длительность и очень конкретный вывод из sys.dm_io_virtual_file_stats:
SELECT test = 1, cycle = 1, start_time = GETDATE(), *
INTO #Metrics
FROM sys.dm_io_virtual_file_stats(DB_ID('dd1'), 2) WHERE 1 = 0;
Это позволило бы мне записывать время начала и окончания каждого отдельного пакета и измерять разницу в DMV между временем начала и временем окончания (надёжно только в этом случае, потому что я знаю, что я единственный пользователь системы).
Много маленьких транзакций
Первый тест, который я хотел выполнить, — это много маленьких транзакций. Для каждой базы данных я хотел получить 500 000 отдельных пакетов, каждый из которых состоял из одной вставки:
INSERT #Metrics SELECT 1, 1, GETDATE(), *
FROM sys.dm_io_virtual_file_stats(DB_ID('dd1'), 2);
GO
INSERT dbo.TheTable DEFAULT VALUES;
GO 500000
INSERT #Metrics SELECT 1, 2, GETDATE(), *
FROM sys.dm_io_virtual_file_stats(DB_ID('dd1'), 2);
Помните, я стараюсь быть лениво-эффективным в таких вещах. Поэтому, чтобы сгенерировать код для всех 8 баз данных, я выполнил это:
;WITH x AS
(
SELECT TOP (8) number FROM master..spt_values
WHERE type = N'P' ORDER BY number
)
SELECT CONVERT(NVARCHAR(MAX), N'') + N'
INSERT #Metrics SELECT 1, 1, GETDATE(), *
FROM sys.dm_io_virtual_file_stats(DB_ID(''dd' + RTRIM(number+1) + '''), 2);
GO
INSERT dbo.TheTable DEFAULT VALUES;
GO 500000
INSERT #Metrics SELECT 1, 2, GETDATE(), *
FROM sys.dm_io_virtual_file_stats(DB_ID(''dd' + RTRIM(number+1) + '''), 2);'
FROM x;
Я запустил этот тест, а затем посмотрел на таблицу #Metrics следующим запросом:
SELECT
[database] = db_name(m1.database_id),
num_writes = m2.num_of_writes - m1.num_of_writes,
write_bytes = m2.num_of_bytes_written - m1.num_of_bytes_written,
bytes_per_write = (m2.num_of_bytes_written - m1.num_of_bytes_written)*1.0
/(m2.num_of_writes - m1.num_of_writes),
io_stall_ms = m2.io_stall_write_ms - m1.io_stall_write_ms,
m1.start_time,
end_time = m2.start_time,
duration = DATEDIFF(SECOND, m1.start_time, m2.start_time)
FROM #Metrics AS m1
INNER JOIN #Metrics AS m2
ON m1.database_id = m2.database_id
WHERE m1.cycle = 1 AND m2.cycle = 2
AND m1.test = 1 AND m2.test = 1;
Это дало следующие результаты (и я подтвердил в нескольких тестах, что результаты были схожими):
| database | writes | bytes | bytes/write | io_stall_ms | start_time | end_time | duration (seconds) |
|---|---|---|---|---|---|---|---|
| dd1 | 8,068 | 261,894,656 | 32,460.91 | 6,232 | 2014-04-26 17:20:00 | 2014-04-26 17:21:08 | 68 |
| dd2 | 8,072 | 261,682,688 | 32,418.56 | 2,740 | 2014-04-26 17:21:08 | 2014-04-26 17:22:16 | 68 |
| dd3 | 8,246 | 262,254,592 | 31,803.85 | 3,996 | 2014-04-26 17:22:16 | 2014-04-26 17:23:24 | 68 |
| dd4 | 8,055 | 261,688,320 | 32,487.68 | 4,231 | 2014-04-26 17:23:24 | 2014-04-26 17:24:32 | 68 |
| dd5 | 500,012 | 526,448,640 | 1,052.87 | 35,593 | 2014-04-26 17:24:32 | 2014-04-26 17:26:32 | 120 |
| dd6 | 500,014 | 525,870,080 | 1,051.71 | 35,435 | 2014-04-26 17:26:32 | 2014-04-26 17:28:31 | 119 |
| dd7 | 500,015 | 526,120,448 | 1,052.20 | 50,857 | 2014-04-26 17:28:31 | 2014-04-26 17:30:45 | 134 |
| dd8 | 500,017 | 525,886,976 | 1,051.73 | 49,680 | 2014-04-26 17:30:45 | 2014-04-26 17:32:58 | 133 |
Маленькие транзакции: длительность и результаты из sys.dm_io_virtual_file_stats
Здесь определённо есть интересные наблюдения:
- Количество отдельных операций записи было очень маленьким для баз данных с отложенной устойчивостью (~в 60 раз меньше, чем для традиционных).
- Общее количество записанных байт сократилось вдвое с использованием отложенной устойчивости (я предполагаю, потому что все записи в традиционном случае содержали много потраченного впустую пространства).
- Количество байт на запись было намного выше для отложенной устойчивости. Это не было слишком удивительным, поскольку вся цель функции — объединять записи в более крупные пакеты.
- Общая длительность задержек ввода-вывода была изменчивой, но примерно на порядок ниже для отложенной устойчивости. Задержки при полностью устойчивых транзакциях были гораздо более чувствительны к типу диска.
- Если что-то до сих пор вас не убедило, столбец длительности очень показателен. Полностью устойчивые пакеты, занимающие две минуты или больше, сокращаются почти вдвое.
Столбцы времени начала/окончания позволили мне сосредоточиться на панели Performance Advisor для точного периода, когда происходили эти транзакции, где мы можем извлечь много дополнительных визуальных индикаторов:
Дальнейшие наблюдения здесь:
- На нескольких графиках вы можете ясно видеть, когда именно вступила в действие часть пакета без отложенной устойчивости (~17:24:32).
- Нет наблюдаемого влияния на CPU или память при использовании отложенной устойчивости.
- Вы можете видеть огромное влияние на количество пакетов/транзакций в секунду на первом графике под SQL Server Activity.
- Ожидания SQL Server взлетают до небес, когда начались полностью устойчивые транзакции. Они почти исключительно состояли из ожиданий WRITELOG с небольшим количеством ожиданий PAGEIOLATCH_EX и PAGEIOLATCH_UP для полноты картины.
- Общее количество сбросов журнала во время операций с отложенной устойчивостью было довольно маленьким (низкие сотни в секунду), в то время как это подскочило до более чем 4 000/сек для традиционного поведения (и немного ниже для HDD-части теста).
Меньше, но крупнее транзакций
Для следующего теста я хотел увидеть, что произойдёт, если мы выполним меньше операций, но убедимся, что каждый оператор затрагивает больший объём данных. Я хотел, чтобы этот пакет выполнялся в каждой базе данных:
CREATE TABLE dbo.Rnd
(
batch TINYINT,
TheID INT
);
INSERT dbo.Rnd SELECT TOP (1000) 1, TheID FROM dbo.TheTable ORDER BY NEWID();
INSERT dbo.Rnd SELECT TOP (10) 2, TheID FROM dbo.TheTable ORDER BY NEWID();
INSERT dbo.Rnd SELECT TOP (300) 3, TheID FROM dbo.TheTable ORDER BY NEWID();
GO
INSERT #Metrics SELECT 1, GETDATE(), *
FROM sys.dm_io_virtual_file_stats(DB_ID('dd1'), 2);
GO
UPDATE t SET TheDate = DATEADD(MINUTE, 1, TheDate)
FROM dbo.TheTable AS t
INNER JOIN dbo.Rnd AS r
ON t.TheID = r.TheID
WHERE r.batch = 1;
GO 10000
UPDATE t SET RowGuid = NEWID()
FROM dbo.TheTable AS t
INNER JOIN dbo.Rnd AS r
ON t.TheID = r.TheID
WHERE r.batch = 2;
GO 10000
DELETE dbo.TheTable WHERE TheID IN (SELECT TheID FROM dbo.Rnd WHERE batch = 3);
DELETE dbo.TheTable WHERE TheID IN (SELECT TheID+1 FROM dbo.Rnd WHERE batch = 3);
DELETE dbo.TheTable WHERE TheID IN (SELECT TheID-1 FROM dbo.Rnd WHERE batch = 3);
GO
INSERT #Metrics SELECT 2, GETDATE(), *
FROM sys.dm_io_virtual_file_stats(DB_ID('dd1'), 2);
Итак, снова я использовал ленивый метод, чтобы создать 8 копий этого скрипта, по одной на базу данных:
;WITH x AS (SELECT TOP (8) number FROM master..spt_values WHERE type = N'P' ORDER BY number)
SELECT N'
USE dd' + RTRIM(Number+1) + ';
GO
CREATE TABLE dbo.Rnd
(
batch TINYINT,
TheID INT
);
INSERT dbo.Rnd SELECT TOP (1000) 1, TheID FROM dbo.TheTable ORDER BY NEWID();
INSERT dbo.Rnd SELECT TOP (10) 2, TheID FROM dbo.TheTable ORDER BY NEWID();
INSERT dbo.Rnd SELECT TOP (300) 3, TheID FROM dbo.TheTable ORDER BY NEWID();
GO
INSERT #Metrics SELECT 2, 1, GETDATE(), *
FROM sys.dm_io_virtual_file_stats(DB_ID(''dd' + RTRIM(number+1) + '''), 2);
GO
UPDATE t SET TheDate = DATEADD(MINUTE, 1, TheDate)
FROM dbo.TheTable AS t
INNER JOIN dbo.rnd AS r
ON t.TheID = r.TheID
WHERE r.cycle = 1;
GO 10000
UPDATE t SET RowGuid = NEWID()
FROM dbo.TheTable AS t
INNER JOIN dbo.rnd AS r
ON t.TheID = r.TheID
WHERE r.cycle = 2;
GO 10000
DELETE dbo.TheTable WHERE TheID IN (SELECT TheID FROM dbo.rnd WHERE cycle = 3);
DELETE dbo.TheTable WHERE TheID IN (SELECT TheID+1 FROM dbo.rnd WHERE cycle = 3);
DELETE dbo.TheTable WHERE TheID IN (SELECT TheID-1 FROM dbo.rnd WHERE cycle = 3);
GO
INSERT #Metrics SELECT 2, 2, GETDATE(), *
FROM sys.dm_io_virtual_file_stats(DB_ID(''dd' + RTRIM(number+1) + '''), 2);'
FROM x;
Я запустил этот пакет, а затем изменил запрос к #Metrics выше, чтобы посмотреть на второй тест вместо первого. Результаты:
| database | writes | bytes | bytes/write | io_stall_ms | start_time | end_time | duration (seconds) |
|---|---|---|---|---|---|---|---|
| dd1 | 20,970 | 1,271,911,936 | 60,653.88 | 12,577 | 2014-04-26 17:41:21 | 2014-04-26 17:43:46 | 145 |
| dd2 | 20,997 | 1,272,145,408 | 60,587.00 | 14,698 | 2014-04-26 17:43:46 | 2014-04-26 17:46:11 | 145 |
| dd3 | 20,973 | 1,272,982,016 | 60,696.22 | 12,085 | 2014-04-26 17:46:11 | 2014-04-26 17:48:33 | 142 |
| dd4 | 20,958 | 1,272,064,512 | 60,695.89 | 11,795 | 2014-04-26 17:48:33 | 2014-04-26 17:50:56 | 143 |
| dd5 | 30,138 | 1,282,231,808 | 42,545.35 | 7,402 | 2014-04-26 17:50:56 | 2014-04-26 17:53:23 | 147 |
| dd6 | 30,138 | 1,282,260,992 | 42,546.31 | 7,806 | 2014-04-26 17:53:23 | 2014-04-26 17:55:53 | 150 |
| dd7 | 30,129 | 1,281,575,424 | 42,536.27 | 9,888 | 2014-04-26 17:55:53 | 2014-04-26 17:58:25 | 152 |
| dd8 | 30,130 | 1,281,449,472 | 42,530.68 | 11,452 | 2014-04-26 17:58:25 | 2014-04-26 18:00:55 | 150 |
Крупные транзакции: длительность и результаты из sys.dm_io_virtual_file_stats
На этот раз влияние отложенной устойчивости гораздо менее заметно. Мы видим немного меньшее количество операций записи при немного большем количестве байт на запись, при почти идентичном общем объёме записанных байт. В этом случае мы фактически видим более высокие задержки ввода-вывода при отложенной устойчивости, и это, вероятно, объясняет тот факт, что длительности тоже были почти идентичными.
На панели Performance Advisor мы видим некоторые сходства с предыдущим тестом, а также некоторые резкие различия:
Одно из больших различий, которое стоит отметить здесь, — это то, что разница в статистике ожиданий не столь выражена, как в предыдущем тесте — по-прежнему гораздо более высокая частота ожиданий WRITELOG для полностью устойчивых пакетов, но далеко не на том уровне, что наблюдался с меньшими транзакциями. Ещё одна вещь, которую вы можете сразу заметить, — это то, что ранее наблюдавшееся влияние на пакеты и транзакции в секунду больше не присутствует. И, наконец, хотя сбросов журнала при полностью устойчивых транзакциях больше, чем при отложенных, это расхождение гораздо менее выражено, чем с меньшими транзакциями.
Заключение
Должно быть ясно, что существуют определённые типы рабочих нагрузок, которые могут получить огромную выгоду от отложенной устойчивости — при условии, конечно, что у вас есть терпимость к потере данных. Эта функция не ограничена In-Memory OLTP, доступна во всех редакциях SQL Server и может быть реализована с малыми изменениями кода или вообще без них. Это, безусловно, может быть мощным приёмом, если ваша рабочая нагрузка может его поддержать. Но, опять же, вам нужно протестировать вашу рабочую нагрузку, чтобы убедиться, что она выиграет от этой функции, а также серьёзно подумать о том, не увеличивает ли это вашу подверженность риску потери данных.



Комментариев нет:
Отправить комментарий