在存储过程中启用 DELAYED_DURABILITY = ON 可显著提升小事务吞吐,但需确保数据库已设为 ALLOWED、使用原生编译及 BEGIN ATOMIC 块声明,且业务能容忍崩溃时的数据丢失。

直接结论:在存储过程中启用 DELAYED_DURABILITY = ON 可显著提升小事务吞吐,但必须确认业务能容忍日志缓冲区未刷盘前的潜在数据丢失。
存储过程级别开启延迟持久化的正确写法
SQL Server 2014+ 支持在原生编译存储过程中显式声明 DELAYED_DURABILITY,这是最精准、最可控的启用方式。普通解释型存储过程(CREATE PROCEDURE)不支持该选项,强行加会报错 Incorrect syntax near 'DELAYED_DURABILITY'。
必须满足三个前提:
- 数据库已启用延迟持久化支持:
ALTER DATABASE [dbname] SET DELAYED_DURABILITY = ALLOWED(DISABLED时该选项被忽略,FORCED时无需显式指定) - 存储过程需使用
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER - 必须放在
BEGIN ATOMIC块的 WITH 子句中
示例:
CREATE PROCEDURE dbo.usp_InsertOrder
@accountid INT, @amount DECIMAL(18,2)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS
BEGIN ATOMIC WITH (
DELAYED_DURABILITY = ON,
TRANSACTION ISOLATION LEVEL = SNAPSHOT,
LANGUAGE = N'English'
)
INSERT INTO dbo.OrderTransactions (accountid, amount, transactiontime)
VALUES (@accountid, @amount, SYSDATETIME());
END
为什么不能在普通存储过程中用 BEGIN TRAN ... COMMIT 加 hint?
因为 DELAYED_DURABILITY 是事务级控制,不是语句级 hint,且仅对“提交动作”生效。你在普通存储过程里写 BEGIN TRAN 后跟 COMMIT,SQL Server 仍按数据库级设置决定是否延迟——即使设了 ALLOWED,也得靠客户端显式指定 COMMIT WITH (DELAYED_DURABILITY = ON),而 T-SQL 本身不支持这种语法(只支持在原生编译过程或 sp_executesql 的参数中传递)。
常见错误现象:
- 写
COMMIT TRAN WITH (DELAYED_DURABILITY = ON)→ 报错Incorrect syntax near 'WITH' - 在普通存储过程中调用
sp_flush_log→ 无效,它只强制刷新当前数据库的延迟日志缓存,不改变事务提交行为 - 误以为
SET IMPLICIT_TRANSACTIONS ON能触发延迟 → 不影响,隐式事务仍走完全持久路径
性能收益与风险边界在哪里
延迟持久化真正起效的前提是:事务足够小、频率足够高、日志缓冲区能批量刷出。若单次事务修改大量行(比如插入 1000 行),日志缓冲区很快填满,反而触发频繁刷盘,吞吐提升不明显,甚至因上下文切换开销略降。
容易被忽略的关键点:
-
DELAYED_DURABILITY = ON仅作用于该原子块内的COMMIT,不影响ROLLBACK或其他 DML 语句本身 - 一旦发生实例崩溃或
SHUTDOWN WITH NOWAIT,所有尚未刷入磁盘的延迟日志全部丢失,对应事务“已提交但不可见” - 若该存储过程同时更新内存优化表(
MEMORY_OPTIMIZED)和磁盘表,延迟持久性只影响磁盘表部分;内存表的日志本身已高度优化,二者日志路径不同
验证是否真的生效了
不能只看执行时间变快——那可能是其他因素干扰。要查实际日志写入行为:
- 监控
sys.dm_tran_database_transactions中database_transaction_log_record_count和database_transaction_log_bytes_used是否在COMMIT后未立即增长 - 用 Extended Events 捕获
sqlserver.transaction_log事件,观察is_delayed_durability字段是否为 1 - 执行
sp_flush_log后再查sys.dm_io_virtual_file_stats,对比日志文件的num_of_writes是否突增
最直接的证据:在 COMMIT 返回后立刻断电重启,检查刚插入的数据是否丢失——这一步生产环境慎做,但测试环境必须验证。










