监视并排查加速数据库恢复问题

适用于: sql Server 2019 (15.x) 及更高版本Azure SQL 数据库Azure SQL 托管实例 Microsoft Fabric 中的 SQL 数据库

本文可帮助你监视、诊断和解决 SQL Server 2019 (15.x) 及更高版本中的加速数据库恢复 (ADR) 、Azure SQL 托管实例、Azure SQL 数据库和 Microsoft Fabric 中的 SQL 数据库的问题。

检查 PVS 的大小

使用 sys.dm_tran_persistent_version_store_stats DMV 来确定永久性版本存储 (PVS) 大小是否大于预期。

以下示例诊断查询显示有关当前 PVS 大小、清理过程和其他详细信息的所有数据库中的信息,其中 PVS 大小大于零:

SELECT pvss.database_id,
       DB_NAME(pvss.database_id) AS database_name,
       pvss.persistent_version_store_size_kb / 1024. / 1024 AS persistent_version_store_size_gb,
       100 * pvss.persistent_version_store_size_kb / df.total_db_size_kb AS pvs_percent_of_database_size,
       df.total_db_size_kb / 1024. / 1024 AS total_db_size_gb,
       pvss.online_index_version_store_size_kb / 1024. / 1024 AS online_index_version_store_size_gb,
       pvss.current_aborted_transaction_count,
       pvss.aborted_version_cleaner_start_time,
       pvss.aborted_version_cleaner_end_time,
       pvss.oldest_aborted_transaction_id,
       pvss.oldest_active_transaction_id,
       dt.database_transaction_begin_time AS oldest_transaction_begin_time,
       asdt.session_id AS active_transaction_session_id,
       asdt.elapsed_time_seconds AS active_transaction_elapsed_time_seconds,
       pvss.pvs_off_row_page_skipped_low_water_mark,
       pvss.pvs_off_row_page_skipped_min_useful_xts,
       pvss.pvs_off_row_page_skipped_oldest_aborted_xdesid
FROM sys.dm_tran_persistent_version_store_stats AS pvss
CROSS APPLY (
            SELECT SUM(size * 8.) AS total_db_size_kb
            FROM sys.master_files AS mf
            WHERE mf.database_id = pvss.database_id
                  AND
                  mf.state = 0
                  AND
                  mf.type = 0
            ) AS df
LEFT JOIN sys.dm_tran_database_transactions AS dt
ON pvss.oldest_active_transaction_id = dt.transaction_id
   AND
   pvss.database_id = dt.database_id
LEFT JOIN sys.dm_tran_active_snapshot_database_transactions AS asdt
ON pvss.min_transaction_timestamp = asdt.transaction_sequence_num
   OR
   pvss.online_index_min_transaction_timestamp = asdt.transaction_sequence_num
WHERE pvss.persistent_version_store_size_kb > 0
ORDER BY persistent_version_store_size_kb DESC;

检查 pvs_percent_of_database_size 列以查看 PVS 相对于数据库总大小的大小。 请注意典型 PVS 大小与在应用程序活动的典型期间看到的基线之间的任何差异。 如果 PVS 明显大于基线,或者接近数据库大小的 50%,则 PVS 被视为较大。

如果 PVS 的大小未减少,请使用以下故障排除步骤查找并解决大型 PVS 大小的原因。

小贴士

以下故障排除步骤中提到的列是指本部分中诊断查询的结果集中的列。

较大的 PVS 大小可能由以下任一原因引起:

检查长时间运行的活动事务

长时间运行的活动事务可能会阻止已启用 ADR 的数据库中的 PVS 清理。 使用 oldest_transaction_begin_time 列检查最早的活动事务的开始时间。 若要查找长时间运行的事务,请使用以下示例查询。 可以为事务持续时间和生成的事务日志量设置阈值:

DECLARE @LongTxThreshold int = 900;  /* The number of seconds to use as a duration threshold for long-running transactions */
DECLARE @LongTransactionLogBytes bigint = 1073741824; /* The number of bytes to use as the generated log threshold for long-running transactions */

SELECT  dbtr.database_id,
        DB_NAME(dbtr.database_id) AS database_name,
        st.session_id,
        st.transaction_id,
        atr.name,
        sess.login_time,
        dbtr.database_transaction_log_bytes_used,
        CASE WHEN GETDATE() >= DATEADD(second, @LongTxThreshold, tr.transaction_begin_time) THEN 'DurationThresholdExceeded'
             WHEN dbtr.database_transaction_log_bytes_used >= @LongTransactionLogBytes THEN 'LogThresholdExceeded'
             ELSE 'Unknown'
        END
        AS reason
FROM sys.dm_tran_active_transactions AS tr
INNER JOIN sys.dm_tran_session_transactions AS st
ON tr.transaction_id = st.transaction_id
INNER JOIN sys.dm_exec_sessions AS sess
ON st.session_id = sess.session_id
INNER JOIN sys.dm_tran_database_transactions AS dbtr
ON tr.transaction_id = dbtr.transaction_id
INNER JOIN sys.dm_tran_active_transactions AS atr
ON atr.transaction_id = st.transaction_id
WHERE GETDATE() >= DATEADD(second, @LongTxThreshold, tr.transaction_begin_time)
      OR
      dbtr.database_transaction_log_bytes_used >= @LongTransactionLogBytes;

确定会话后,请考虑将其终止(如果允许)。 查看应用程序以确定有问题的事务的性质,以避免将来出现问题。

有关排除查询长时间运行问题的详细信息,请参阅:

检查长时间运行的活动快照扫描

长时间运行的活动快照扫描可能阻止已启用 ADR 的数据库中的 PVS 清理。 使用 READ COMMITTED 快照隔离 (RCSI) 或SNAPSHOT隔离级别的语句接收实例级时间戳。 快照扫描使用时间戳来决定 RCSI 或 SNAPSHOT 事务的版本行可见性。 使用 RCSI 的每个语句都有自己的时间戳,而 SNAPSHOT 隔离具有事务级时间戳。

这些实例级事务时间戳即使在单数据库事务中使用,因为任何事务都可以提升为跨数据库事务。 因此,快照扫描可以阻止在同一数据库引擎实例上的任何数据库中的 PVS 清理。 同样,即使未启用 ADR,快照扫描也可能会阻止清理 tempdb 中的版本存储。 因此,当存在使用 SNAPSHOT 或 RCSI 的长时间运行的事务时,PVS 可能会变大。

pvs_off_row_page_skipped_min_useful_xts 列显示清理期间由于快照扫描时间过长而导致跳过的页数。 如果此列显示得值较大,则表示长快照扫描在阻止 PVS 清理。

使用以下示例查询来查找包含长时间运行的 SNAPSHOT 或 RCSI 事务的数据库会话:

SELECT sdt.transaction_id,
       sdt.transaction_sequence_num,
       s.database_id,
       s.session_id,
       s.login_time,
       GETDATE() AS query_time,
       s.host_name,
       s.program_name,
       s.login_name,
       s.last_request_start_time
FROM sys.dm_tran_active_snapshot_database_transactions AS sdt
INNER JOIN sys.dm_exec_sessions AS s
ON sdt.session_id = s.session_id;

为防止 PVS 清理延迟:

  • 如果可能,请考虑终止延迟 PVS 清理的长期活动的事务会话。
  • 优化长时间运行的查询以缩短查询持续时间。
  • 查看应用程序以确定有问题的活动快照扫描的性质。 对于延迟 PVS 清理的长时间运行查询,请考虑使用不同的隔离级别,例如 READ COMMITTED,而不是 SNAPSHOT 或 RCSI。 在 SNAPSHOT 隔离级别中,此问题更频繁地发生。
  • 在 Azure SQL 数据库弹性池中,对于使用 SNAPSHOT 隔离或 RCSI 的长时间运行事务的数据库,应考虑将其移出弹性池,以避免导致同一池中的其他数据库出现 PVS 清理延迟。

检查辅助副本上的长时间运行查询

如果数据库具有辅助副本,请检查辅助低水印是否在前进。

pvs_off_row_page_skipped_low_water_mark 列中的较大值可能表明存在清理延迟,原因是次要副本上长时间运行的查询。 除了阻碍 PVS 清理之外,次要副本上的长时间运行的查询还可能阻碍虚影清除

可以在主副本上使用以下示例查询,确定次要副本上的长时间运行的查询是否可能阻止 PVS 清理。 如果主副本上正在运行写入工作负荷,但 low_water_mark_for_ghosts 列中的值没有在示例查询多次执行之间增加,则 PVS 和虚影清理可能被次要副本上的长时间运行的查询所阻碍。

SELECT database_id,
       DB_NAME(database_id) AS database_name,
       low_water_mark_for_ghosts,
       synchronization_state_desc,
       synchronization_health_desc,
       is_suspended,
       suspend_reason_desc,
       secondary_lag_seconds
FROM sys.dm_hadr_database_replica_states
WHERE is_local = 1
      AND
      is_primary_replica = 1;

有关详细信息,请参阅 sys.dm_hadr_database_replica_stateslow_water_mark_for_ghosts 列的说明。

连接到每个可读次要副本,找到具有长时间运行的查询的会话,并在可行的情况下考虑终止会话。 有关详细信息,请参阅查找慢查询

检查是否存在大量中止的交易

检查 aborted_version_cleaner_start_timeaborted_version_cleaner_end_time 列,查看上次中止的事务清理是否完成。 中止事务清理完成后,oldest_aborted_transaction_id 应会移到更高的位置。 如果 oldest_aborted_transaction_id 远低于 oldest_active_transaction_id,并且 current_abort_transaction_count 的值较大,则可能存在阻止 PVS 清理的旧终止事务。

若要解决由于大量中止事务而导致的 PVS 清理延迟,请考虑以下事项:

  • 如果使用 SQL Server 2022 (16.x),请增加 ADR Cleaner Thread Count 服务器配置的值。 有关详细信息,请参阅服务器配置:ADR 清理器线程计数
  • 如果可能,请停止工作负载,以便版本清理工具取得进展。
  • 查看应用程序以识别并解决高事务终止率问题。 中止的原因可能是高死锁率、重复键、约束冲突或查询超时。
  • 优化工作负荷,以减少与 PVS 清理器所需的对象级或分区级 IX 锁不兼容的锁。 有关详细信息,请参阅锁定兼容性
  • 如果使用 SQL Server,请禁用 ADR 作为仅紧急步骤来控制 PVS 大小。 请参阅禁用 ADR
  • 如果使用 SQL Server,并且中止的事务清理尚未成功完成,请检查错误日志中是否存在报告 VersionCleaner 问题的消息。
  • 如果 PVS 大小在清理完成后未按预期减小,请检查 pvs_off_row_page_skipped_oldest_aborted_xdesid 列。 较大的值表示空间仍被中止事务的行版本占用。

控制 PVS 大小

如果有大量 DML 语句(INSERTUPDATEDELETEMERGE)的工作负荷(例如大容量 OLTP),并且观察到 PVS 大小较大,则可能需要增加 ADR Cleaner Thread Count 服务器配置的值,以使 PVS 大小保持控制。 有关详细信息,请参阅从 SQL Server 2022 (16.x) 开始提供的服务器配置:ADR 清理器线程计数

在 SQL Server 2019(15.x)中,或者如果增加 ADR Cleaner Thread Count 配置的值并不能充分减少 PVS 大小,则工作负荷可能需要一段时间的休息/恢复才能回收空间。

若要在工作负载之间或维护时段内手动激活 PVS 清理进程,请使用系统存储过程 sys.sp_persistent_version_cleanup

例如:

EXEC sys.sp_persistent_version_cleanup [WideWorldImporters];

捕获清理失败

从 SQL Server 2022(16.x 开始),在错误日志中记录了值得注意的 PVS 清理消息。 tx_mtvc2_sweep_stats扩展事件也会报告清理统计信息。