- cursor type,
- cursor locking behavior,
- locking hints in the query,
- transaction isolation level, and
- cursor close on commit setting.

Here are a couple of observations from several runs with different settings:
- Static and keyset cursors can consume a considerable amount of tempdb memory.
- Dynamic cursors don't use tempdb.
- In higher transaction isolation levels, keyset cursors can take a lot of locks, and for a long period.
- In higher transaction isolation levels, static cursors can take a lot of locks, and for a long period. [They copy everything to the tempdb and lock it in the user db !].
- FOR UPDATE apparently has no impact on resources.
- Cursor owned locks only appear with the SCROLL_LOCKS setting. All other locks belong to the transaction.
- If the connection option to keep cursors open on a commit is set on, all locks will be kept across a commit or rollback operation.
- A WITH (NOLOCK) hint avoids transaction as well as cursor owned locks. [So it actually overrides the SCROLL_LOCKS setting, which might no be what you wanted !]
Some final advice:
- Select as few as possible rows and columns in the cursor resultset (this advice actually applies to all possible resultsets in batches/transactions),
- whenever possible, prefer dynamic cursors over static ones, and
- explicitly set CURSOR_CLOSE_ON_COMMIT to ON in your session.
/*************************//* */
/* Cursor Resources Demo *//* */
/*************************/USE AdventureWorksGO
/***************//* Preparation *//***************/CREATE SCHEMA DockOfTheBayGO
CREATE FUNCTION DockOfTheBay.ufn_TempDb_Internal_Objects(
@Session_id AS int)
RETURNS intASBEGIN -- -- Returns the number of KBytes allocated to tempdb internal objects for a session -- DECLARE @Result int
-- rem: Caching and Deferred Drop operations may influence countersSELECT @Result = SUM((internal_objects_alloc_page_count - internal_objects_dealloc_page_count)) * 8
FROM tempdb.sys.dm_db_task_space_usage WHERE session_id = @Session_idAND database_id = db_id('tempdb')
RETURN @ResultENDGO
CREATE FUNCTION DockOfTheBay.ufn_Locks(
@Session_id AS int)
RETURNS NVARCHAR(200)ASBEGIN -- -- Returns the number of locks per type and owner for a session, as a comma-separated list --DECLARE @Result NVARCHAR(200)
;WITH Locks AS
(
SELECT resource_type AS [Type]
,CONVERT(NVARCHAR(03), COUNT(*)) AS [Number]
,request_owner_type AS [Owner] FROM sys.dm_tran_locks WHERE request_session_id = @Session_idAND resource_type IN ('ROW', 'PAGE', 'RID', 'KEY')
GROUP BY resource_type, request_owner_type)
SELECT @Result = COALESCE(@Result + ', ', '') + [Owner] + ' ' + [Type] + ' lock(s): ' + [Number]
FROM Locks RETURN @ResultENDGO
/*************//* Execution *//*************//* Switch option ON and OFF: */SET CURSOR_CLOSE_ON_COMMIT /* ON */ OFF
/* Uncomment ONE of the following options at a time: */SET TRANSACTION ISOLATION LEVEL SERIALIZABLE-- SET TRANSACTION ISOLATION LEVEL READ COMMITTEDBEGIN TRANSACTION -- Table Variable to hold the measurement resultsDECLARE @Measurements TABLE
(
[Step] NVARCHAR(50) ,[TempDb KBytes] INT ,[Locks] NVARCHAR(200))
INSERT @MeasurementsSELECT 'Transaction started'
,DockOfTheBay.ufn_TempDb_Internal_Objects(@@spid)
,DockOfTheBay.ufn_Locks(@@spid)
-- Dummy variable for the FETCH operationDECLARE @temp SQL_VARIANT
-- Declare and open a keyset-driven cursor.DECLARE c1 CURSOR
/* Please uncomment ONE of the following options at a time: */ -- DYNAMIC DYNAMIC SCROLL_LOCKS
-- KEYSET -- STATIC FOR SELECT * FROM Production.ProductModel /* Uncomment to see the impact: */ -- WITH (NOLOCK) WHERE ProductModelID > 1 /* Uncomment to see the impact: */ -- FOR UPDATEOPEN c1INSERT @MeasurementsSELECT 'After Open' As [Step]
,DockOfTheBay.ufn_TempDb_Internal_Objects(@@spid) AS [TempDb KBytes] ,DockOfTheBay.ufn_Locks(@@spid) AS [Locks]FETCH NEXT FROM c1 INTO @temp, @temp, @temp, @temp, @temp, @temp
INSERT @MeasurementsSELECT 'After Fetch' As [Step]
,DockOfTheBay.ufn_TempDb_Internal_Objects(@@spid) AS [TempDb KBytes] ,DockOfTheBay.ufn_Locks(@@spid) AS [Locks]COMMIT TRANSACTIONINSERT @MeasurementsSELECT 'After Commit' As [Step]
,DockOfTheBay.ufn_TempDb_Internal_Objects(@@spid) AS [TempDb KBytes] ,DockOfTheBay.ufn_Locks(@@spid) AS [Locks]-- Close and deallocate the cursor.CLOSE c1DEALLOCATE c1INSERT @MeasurementsSELECT 'After Deallocate' As [Step]
,DockOfTheBay.ufn_TempDb_Internal_Objects(@@spid) AS [TempDb KBytes] ,DockOfTheBay.ufn_Locks(@@spid) AS [Locks]-- Display resultsSELECT * FROM @Measurements
GO
/***********//* Cleanup *//***********/DROP FUNCTION DockOfTheBay.ufn_TempDb_Internal_ObjectsDROP FUNCTION DockOfTheBay.ufn_LocksDROP SCHEMA DockOfTheBayGO

No comments:
Post a Comment