- 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 AdventureWorks
GO
/***************/
/* Preparation */
/***************/
CREATE SCHEMA DockOfTheBay
GO
CREATE FUNCTION DockOfTheBay.ufn_TempDb_Internal_Objects
(
@Session_id AS int
)
RETURNS int
AS
BEGIN
--
-- Returns the number of KBytes allocated to tempdb internal objects for a session
--
DECLARE @Result int
-- rem: Caching and Deferred Drop operations may influence counters
SELECT @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_id
AND database_id = db_id('tempdb')
RETURN @Result
END
GO
CREATE FUNCTION DockOfTheBay.ufn_Locks
(
@Session_id AS int
)
RETURNS NVARCHAR(200)
AS
BEGIN
--
-- 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_id
AND 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 @Result
END
GO
/*************/
/* 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 COMMITTED
BEGIN TRANSACTION
-- Table Variable to hold the measurement results
DECLARE @Measurements TABLE
(
[Step] NVARCHAR(50)
,[TempDb KBytes] INT
,[Locks] NVARCHAR(200)
)
INSERT @Measurements
SELECT 'Transaction started'
,DockOfTheBay.ufn_TempDb_Internal_Objects(@@spid)
,DockOfTheBay.ufn_Locks(@@spid)
-- Dummy variable for the FETCH operation
DECLARE @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 UPDATE
OPEN c1
INSERT @Measurements
SELECT '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 @Measurements
SELECT 'After Fetch' As [Step]
,DockOfTheBay.ufn_TempDb_Internal_Objects(@@spid) AS [TempDb KBytes]
,DockOfTheBay.ufn_Locks(@@spid) AS [Locks]
COMMIT TRANSACTION
INSERT @Measurements
SELECT '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 c1
DEALLOCATE c1
INSERT @Measurements
SELECT 'After Deallocate' As [Step]
,DockOfTheBay.ufn_TempDb_Internal_Objects(@@spid) AS [TempDb KBytes]
,DockOfTheBay.ufn_Locks(@@spid) AS [Locks]
-- Display results
SELECT * FROM @Measurements
GO
/***********/
/* Cleanup */
/***********/
DROP FUNCTION DockOfTheBay.ufn_TempDb_Internal_Objects
DROP FUNCTION DockOfTheBay.ufn_Locks
DROP SCHEMA DockOfTheBay
GO
No comments:
Post a Comment