Снижает ли блок транзакций производительность в SQL Server? - PullRequest
6 голосов
/ 13 июня 2011

Теперь я и коллега спорим о влиянии не жизненно важных блоков BEGIN TRAN .... COMMIT TRAN. Я написал около 140 хранимых процедур для простых операций вставки-обновления-удаления, и, поскольку в дальнейшем нам, возможно, понадобится выполнить некоторые дополнительные операции, я уже включил, возможно, необходимые блоки BEGIN TRAN и COMMIT TRAN, например:

CREATE PROCEDURE [Users].[Login_Insert]

        @Username           nvarchar (50) OUTPUT,
        @Password           char (40),
        @FullName           nvarchar (150),
        @LoginTypeId        int

AS

SET NOCOUNT ON;

BEGIN TRY
BEGIN TRAN

INSERT [Users].[Login]
(
        [Username],
        [Password],
        [FullName],
        [LoginTypeId]
)
VALUES
(
        @Username,
        @Password,
        @FullName,
        @LoginTypeId
)

COMMIT TRAN
RETURN 1
END TRY

BEGIN CATCH
ROLLBACK TRAN

RETURN -1
END CATCH

GO

Теперь многие из этих транзакций могут никогда не понадобиться. Будут ли эти посторонние блоки заметно влиять на производительность? Заранее спасибо.

Ответы [ 3 ]

8 голосов
/ 13 июня 2011

Недостаточно, чтобы заметить.

То есть каждый TXN будет открыт в течение дополнительной OhNoSecond между BEGIN TRAN и INSERT.Я был бы впечатлен, если бы кто-нибудь мог измерить его.

Однако, если вы сделали BEGIN TRAN, а затем запросили ввод пользователя, ваши ноги должны сломаться ...

Хорошая идея: я делаю этотак что все мои процессы записи на 100% согласованы, имеют одинаковую обработку ошибок, могут быть вложенными и т. д.

Редактировать: После ответа Ремуса я вижу, что я не связывался с моим шаблоном TXN гнезда: Вложенный сохраненныйпроцедуры, содержащие шаблон TRY CATCH ROLLBACK? Это отличается от Remus 'тем, что он всегда откатывается назад и не имеет SAVEPOINTs

Edit, быстрый и грязный тест показывает, что он быстрее примерно в 2/3 времени странзакция

SET NOCOUNT ON
SET STATISTICS IO OFF

DECLARE @date DATETIME2
DECLARE @noTran INT
DECLARE @withTran INT

SET @noTran = 0
SET @withTran = 0

DECLARE @t TABLE (ColA INT)
INSERT @t VALUES (1)

DECLARE 
  @count INT,
  @value INT

SET @count = 1

WHILE @count < 100
BEGIN

  SET @date = GETDATE()
  UPDATE smalltable SET smalltablename = CASE smalltablename WHEN 'test1' THEN 'test' ELSE 'test2' END WHERE smalltableid = 1
  SET @noTran = @noTran + DATEDIFF(MICROSECOND, @date, GETDATE())

  SET @date = GETDATE()
  BEGIN TRAN
  UPDATE smalltable SET smalltablename = CASE smalltablename WHEN 'test1' THEN 'test' ELSE 'test2' END WHERE smalltableid = 1
  COMMIT TRAN
  SET @withTran = @withTran + DATEDIFF(MICROSECOND, @date, GETDATE())

  SET @count = @count + 1
END

SELECT 
  @noTran / 1000000. AS Seconds_NoTransaction, 
  @withTran / 1000000. AS Seconds_WithTransaction

Seconds_NoTransaction    Seconds_WithTransaction
2.63200000               2.70400000
2.16700000               2.12300000

Изменение порядка обновления сохраняет то же поведение

7 голосов
/ 13 июня 2011

В опубликованном вами коде не будет никакого измеримого эффекта, но транзакции действительно влияют на производительность, они могут значительно повысить производительность из-за группирования фиксации журнала, или они могут значительно снизить производительность из-за неправильно управляемых проблем конкуренции.Но суть в том, что когда транзакции необходимы для корректности, вы не можете пропустить их .При этом ваш шаблон на самом деле довольно плох по отношению к транзакциям и блокам try-catch.Транзакция в блоке перехвата должна иметь логическую проверку трех состояний для XACT_STATE возвращаемых значений (-1, 0, 1) и правильно обрабатывать обреченные транзакции.См., Например, Обработка исключений и вложенные транзакции .

. Никогда не следует смешивать обработку ошибок try-catch с обработкой ошибок кода возврата.Выберите один и придерживайтесь его, предпочтительно попробуйте поймать.Другими словами, ваша хранимая процедура должна RAISE , а не возвращать -1.Смешивание исключений с кодами ошибок делает ваш код кошмаром для обслуживания и правильного вызова.

2 голосов
/ 04 мая 2018

TL; DR - хранимая процедура, содержащая два запроса на выборку, относящихся к 33 миллионам записей, заняла у меня 45 секунд без выполнения транзакции, 48 секунд с.

Отказ от ответственности: я написал хранимую процедуру в течение примерно 4 часов и натолкнулся на несколько измеримый ответ на этот вопрос (ПРИМЕЧАНИЕ: это не так уж важно!) Пробелы в логике запросов намеренно опущены из-за чувствительности данныхЯ работал с.

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

1) Я написал 2 общих табличных выражения с 1 SQL SELECT во временную таблицу, а затем запросил его снова.Я должен был сделать это, потому что мои требования требовали реализовать пару скалярных функций, которые в противном случае попытались бы запустить функцию на более чем 33 миллионах записей вместо 355.

2) Я прикрепил скалярное значениеФункция ПОСЛЕ первого запроса, чтобы он не пытался просмотреть 30 миллионов записей (если вам интересно, это имело огромное значение).

Запрос: Для читателей я вырезал большую частьзапроса (оператор case).

CREATE PROC GET_PAYMENT_SUMS_BY_CLIENT
AS

--Required for reporting in a specific Business Intelligence later; Optional
SET FMTONLY OFF; 

BEGIN TRANSACTION

--Query 1
--This CTE checks over 30 million records
WITH CTE1 AS(
SELECT CASE VARIABLE
--170 case conditions go here
END AS TheType, 
Amount,
PK1 FROM TABLE1),

--THIS CTE Pivots the sums to get the data in the style I want it in
CTE2 AS(
SELECT PK1, [PIVOT1], [PIVOT2], [PIVOT3]
FROM
  (SELECT * FROM CTE1) AS BaseTable --Alias was just to get it to execute
)
PIVOT(
  SUM(Amount)
FOR TheType IN ([PIVOT1], [PIVOT2], [PIVOT3])
        ) AS PivotTable
    )
 )

SELECT TABLE2.NAME, CTE2.* INTO #TEMPORARY_TABLE 
FROM CTE2 
JOIN TABLE2 ON CTE2.PK1 = TABLE2.PK2

--Query 2
--Written to force the function to look at 355 records instead of 33 million
SELECT *, dbo.SCALAR_VALUED_FUNCTION(PK2) FROM #TEMPORARY_TABLE

COMMIT TRANSACTION

Выводы:
С транзакцией - если используется логика транзакции, результирующий запрос из этого набора занимает 48 секунд для обработки более 33 миллионов записей воператор case, содержащий 170 строк, сводит данные по сумме, помещает данные во временную таблицу и присоединяет скалярную функцию ПОСЛЕ первого выполнения запроса.

Без транзакции - если закомментированные строки в коде оставляются закомментированными, все вышеупомянутые шаги выполняются за 45 секунд.Это примерно на 7% быстрее, чем с блоком транзакций: 3/45 = 0,0666 .... ~ 7% быстрее.

Вывод: хотя мои усилия не могут сказать вам, даст ли один и тот же запрос для 10 записей,та же пропорция различий может сказать вам, что это начинает иметь большее значение, когда вы начинаете с более крупными наборами данных и / или более сложными запросами.

У меня есть эта информация, которая послужила цели кому-то там!

...