一些有用的DMV

本文提供了一系列SQL性能优化的方法,包括如何查看正在运行的SQL语句及其执行情况、监控当前打开的游标、运行的事务、查询阻塞情况及推荐更新统计信息的方法。


---查看数据库中正在运行的SQL语句的执行情况(包括执行计划和等待类型等等)

SELECT
 sp.ecid,
    DB_NAME(sp.dbid) AS [Database],
 QP.query_plan,
 sp.nt_username,
    p.session_id ,
    p.request_id ,
    p.start_time ,
    p.status ,
    p.command ,
 sp.lastwaittype,
    p.blocking_session_id ,
    p.wait_type ,
    p.wait_time ,
    p.wait_resource ,
    p.total_elapsed_time ,
    p.open_transaction_count ,
    p.transaction_isolation_level ,
    SUBSTRING(qt.text, p.statement_start_offset / 2, ( CASE WHEN p.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
                                                            ELSE p.statement_end_offset
                                                       END - p.statement_start_offset ) / 2) AS "SQL stament" ,
    p.statement_start_offset ,
    p.statement_end_offset ,
    sp.program_name,
 sp.Hostname,
 sp.nt_domain,
    batch = qt.text
FROM
    master.sys.dm_exec_requests AS p
INNER JOIN sys.sysprocesses sp ON p.session_id=sp.spid
CROSS APPLY sys.dm_exec_sql_text(p.sql_handle) AS qt
CROSS APPLY sys.dm_exec_query_plan(p.plan_handle) AS QP
WHERE
    p.session_id > 50 and p.session_id<>@@SPID
  



--监控当前打开的游标

SELECT  S.host_name AS ClientMachine ,
        S.program_name AS ApplicationName ,
        S.original_login_name AS LoginName ,
        C.name AS CursorName ,
        C.properties AS CursorOptions ,
        C.creation_time AS CursorCreatinTime ,
        ST.text AS SQLQuery ,
        C.is_open AS IsCursorOpen ,
        C.worker_time / 1000 AS DurationInMiliSeconds ,
        C.reads AS NumberOfReads ,
        C.writes AS NumberOfWrites
FROM    sys.dm_exec_cursors (0) AS C
        INNER JOIN sys.dm_exec_sessions AS S ON C.session_id = S.session_id
        CROSS APPLY sys.dm_exec_sql_text(C.sql_handle) AS ST
ORDER BY DurationInMiliSeconds DESC
GO



--监控当前正在运行的事务
SELECT  ST.transaction_id AS TransactionID ,
        DB_NAME(DT.database_id) AS DatabaseName ,
        AT.transaction_begin_time AS TransactionStartTime ,
        DATEDIFF(SECOND, AT.transaction_begin_time, GETDATE()) AS TransactionDuration ,
        CASE AT.transaction_type
          WHEN 1 THEN 'Read/Write Transaction'
          WHEN 2 THEN 'Read-Only Transaction'
          WHEN 3 THEN 'System Transaction'
          WHEN 4 THEN 'Distributed Transaction'
        END AS TransactionType ,
        CASE AT.transaction_state
          WHEN 0 THEN 'Transaction Not Initialized'
          WHEN 1 THEN 'Transaction Initialized & Not Started'
          WHEN 2 THEN 'Active Transaction'
          WHEN 3 THEN 'Transaction Ended'
          WHEN 4 THEN 'Distributed Transaction Initiated Commit Process'
          WHEN 5 THEN 'Transaction in Prepared State & Waiting Resolution'
          WHEN 6 THEN 'Transaction Committed'
          WHEN 7 THEN 'Transaction Rolling Back'
          WHEN 8 THEN 'Transaction Rolled Back'
        END AS TransactionState
FROM    sys.dm_tran_session_transactions AS ST
        INNER JOIN sys.dm_tran_active_transactions AS AT ON ST.transaction_id = AT.transaction_id
        INNER JOIN sys.dm_tran_database_transactions AS DT ON ST.transaction_id = DT.transaction_id
ORDER BY TransactionStartTime
GO

--查询被阻塞和正在阻塞的查询
SELECT  R.session_id AS BlockedSessionID ,
        S.session_id AS BlockingSessionID ,
        Q1.text AS BlockedSession_TSQL ,
        Q2.text AS BlockingSession_TSQL ,
        C1.most_recent_sql_handle AS BlockedSession_SQLHandle ,
        C2.most_recent_sql_handle AS BlockingSession_SQLHandle ,
        S.original_login_name AS BlockingSession_LoginName ,
        S.program_name AS BlockingSession_ApplicationName ,
        S.host_name AS BlockingSession_HostName
FROM    sys.dm_exec_requests AS R
        INNER JOIN sys.dm_exec_sessions AS S ON R.blocking_session_id = S.session_id
        INNER JOIN sys.dm_exec_connections AS C1 ON R.session_id = C1.most_recent_session_id
        INNER JOIN sys.dm_exec_connections AS C2 ON S.session_id = C2.most_recent_session_id
        CROSS APPLY sys.dm_exec_sql_text(C1.most_recent_sql_handle) AS Q1
        CROSS APPLY sys.dm_exec_sql_text(C2.most_recent_sql_handle) AS Q2


--建议需要更新的统计信息
SELECT DISTINCT
        OBJECT_NAME(SI.object_id) AS Table_Name ,
        SI.name AS Statistics_Name ,
        STATS_DATE(SI.object_id, SI.index_id) AS Last_Stat_Update_Date ,
        SSI.rowmodctr AS RowModCTR ,
        SP.rows AS Total_Rows_In_Table ,
        'UPDATE STATISTICS [' + SCHEMA_NAME(SO.schema_id) + '].['
        + OBJECT_NAME(SI.object_id) + ']' + SPACE(2) + SI.name AS Update_Stats_Script
FROM    sys.indexes AS SI( NOLOCK )
        INNER JOIN sys.objects AS SO( NOLOCK ) ON SI.object_id = SO.object_id
        INNER JOIN sys.sysindexes SSI( NOLOCK ) ON SI.object_id = SSI.id
                                                    AND SI.index_id = SSI.indid
        INNER JOIN sys.partitions AS SP ON SI.object_id = SP.object_id
WHERE   SSI.rowmodctr > 0
        AND STATS_DATE(SI.object_id, SI.index_id) IS NOT NULL
        AND SO.type = 'U'
ORDER BY RowModCTR DESC


<think>嗯,用户的问题是关于SQL Server事务在访问版本存储区时被回滚的解决方案,特别是tempdb空间不足导致收缩和提前标记为牺牲品的情况。这个问题涉及SQL Server的并发控制机制,需要深入理解版本存储区的运作原理。 用户可能是一位DBA或开发人员,正在处理生产环境中的性能问题。从ta的提问方式看,对SQL Server事务机制有一定了解,但遇到了具体的技术瓶颈。ta的情绪可能比较焦虑,因为事务回滚会影响系统稳定性。 参考引用材料中,[^1]提到事务在MARS会话中会变成批范围事务,[^3]解释了SERIALIZABLE隔离级别的严格性,这些都与快照隔离有关。但用户的核心痛点在于tempdb空间管理。 首先需要明确几个技术要点:版本存储区位于tempdb中,用于存储行版本以实现快照隔离。当空间不足时,SQL Server会终止长时间运行的事务来释放空间。解决思路应该从tempdb资源优化和事务设计两方面入手。 在tempdb优化方面,重点考虑文件配置。建议用户将tempdb拆分成多个等大文件(通常与CPU核心数一致),避免自动增长带来的性能波动。同时要设置合理的初始大小,减少文件扩容次数。监控空间使用情况也很关键,可以部署警报机制。 事务设计优化需要调整隔离级别使用策略。READ_COMMITTED_SNAPSHOT虽然能减少阻塞,但会增加版本存储负担。对于长时间运行的报表查询,可能需要改用其他方案如列存储索引。还要注意控制事务时间,避免超大事务。 监控手段上,sys.dm_tran_version_store_space_usage这个DMV特别有用,能直接查看版本存储的空间占用。结合空间不足的错误日志,可以精确定位问题事务。如果发现特定会话长期占用版本,可能需要优化其查询逻辑。 最后考虑应急方案。当空间不足警报触发时,除了终止会话外,还可以临时增加tempdb文件。但扩容只是权宜之计,根本还是要找到空间激增的根源。对于确实需要大版本存储的业务,可能需要单独部署实例。 用户可能还需要知道如何区分普通空间不足和版本存储导致的不足。这可以通过查询sys.dm_db_file_space_usage的version_store_reserved_page_count字段来判断。</think>事务在访问版本存储区时因tempdb空间不足被标记为牺牲品(牺牲品死锁),本质是SQL Server为保护系统稳定性而主动终止事务。以下是系统化解决方案: --- ### **根本原因分析** 1. **版本存储区溢出** - 启用快照隔离(如`READ_COMMITTED_SNAPSHOT`)时,所有数据修改会在tempdb的版本存储区生成行版本 - 长时间事务或高频更新导致版本链过长,tempdb空间耗尽 - SQL Server强制回滚最早访问版本存储区的事务(错误号3967) 2. **空间回收延迟** - 版本行仅在所有活动事务不再需要时才被清除 - 存在长事务时,版本数据持续累积 --- ### **解决方案与优化措施** #### **1. TempDB空间优化** - **文件配置** ```sql -- 检查当前配置 SELECT name, physical_name, size*8/1024 AS size_MB FROM sys.master_files WHERE database_id = DB_ID('tempdb'); -- 最佳实践:创建多个等大文件(通常=CPU核心数) ALTER DATABASE tempdb MODIFY FILE (NAME = tempdev, SIZE = 4096MB, FILEGROWTH = 1024MB); ALTER DATABASE tempdb ADD FILE (NAME = tempdev2, SIZE = 4096MB, FILEGROWTH = 1024MB); ``` - **效果**:减少分配争用,提升并发能力[^1] - **自动增长策略** - 避免使用百分比增长(易导致突发性空间不足) - 设置固定大小增长(如每次1GB) #### **2. 版本存储管理** - **监控版本存储** ```sql -- 实时查看版本存储占用 SELECT DB_NAME(database_id) AS DatabaseName, reserved_page_count * 8/1024 AS VersionStoreSize_MB, min_trans_timestamp FROM sys.dm_tran_version_store_space_usage; ``` - **警报阈值**:当VersionStoreSize > 50% tempdb总空间时需干预 - **控制事务粒度** - 避免在快照隔离下运行超长事务(>1分钟) - 拆分大事务为小批量操作(如每1000行提交一次) - 示例优化: ```sql WHILE EXISTS(SELECT 1 FROM LargeTable WHERE Processed=0) BEGIN UPDATE TOP (1000) LargeTable SET Processed=1 WHERE Processed=0; COMMIT; -- 分批提交 END ``` #### **3. 隔离级别调整** - **评估必要性** - 非必需场景关闭快照隔离: ```sql ALTER DATABASE MyDB SET READ_COMMITTED_SNAPSHOT OFF; ``` - **替代方案** - 使用`NOLOCK`提示(需承担脏读风险): ```sql SELECT * FROM Orders WITH (NOLOCK) WHERE Status=1; ``` #### **4. 主动维护机制** - **定期清理长事务** ```sql -- 查找运行超过30分钟的事务 SELECT session_id, transaction_id, elapsed_time_seconds/60 AS duration_min FROM sys.dm_tran_active_snapshot_database_transactions WHERE elapsed_time_seconds > 1800; ``` - 强制终止高危会话:`KILL [session_id]` - **TempDB自动清理** 启用`tempdb`日志备份(仅限完整恢复模式),触发版本存储清理 --- ### **紧急故障处理** 当出现错误3967时: 1. **立即扩容** ```sql ALTER DATABASE tempdb MODIFY FILE (NAME = tempdev, SIZE = [当前大小+50%]); ``` 2. **终止阻塞源** ```sql -- 找到消耗版本存储最多的会话 SELECT top 5 session_id, transaction_id, elapsed_time_seconds FROM sys.dm_tran_active_snapshot_database_transactions ORDER BY elapsed_time_seconds DESC; KILL [高危session_id]; ``` --- ### **预防性监控脚本** ```sql -- 综合监控模板 SELECT vsu.reserved_page_count * 8/1024 AS VersionStore_MB, tsu.user_objects_alloc_page_count * 8/1024 AS UserObj_MB, (SELECT COUNT(*) FROM sys.dm_tran_active_snapshot_database_transactions) AS ActiveSnapshotTxns, (SELECT COUNT(*) FROM sys.dm_tran_version_store) AS VersionChains FROM sys.dm_db_file_space_usage tsu CROSS JOIN sys.dm_tran_version_store_space_usage vsu; ``` **警报规则**:当`VersionStore_MB > tempdb总空间30%`时触发告警 > 关键点:**版本存储的本质是空间换时间**。通过文件优化、事务拆分、主动监控三管齐下,可降低90%牺牲品死锁概率[^2][^4]。高频更新系统需定期评估快照隔离的必要性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值