普通t-sql存储过程对内存优化表无效,因其仍走传统查询处理器路径,保留锁、日志、sql解析等开销;而原生编译存储过程跳过解析、直接执行机器码,并利用无锁mvcc机制,这才是吞吐翻倍的关键。

直接结论:必须用原生编译存储过程(NATIVE_COMPILATION),且只能操作内存优化表;普通 T-SQL 存储过程对内存表的 INSERT 几乎不提速。
为什么普通存储过程对内存优化表无效?
SQL Server 对内存优化表的访问分两条路径:解释执行(常规 T-SQL)和原生编译(NATIVE_COMPILATION)。前者仍走传统查询处理器,锁、日志、解析开销全在;后者跳过解析、直接执行机器码,并利用无锁 MVCC 机制——这才是吞吐翻倍的关键。
常见错误现象:INSERT INTO dbo.MyMemTable ... 在普通存储过程中执行时,sys.dm_exec_requests 显示 wait_type = 'PREEMPTIVE_XE_CALLBACK' 或高 cpu_time,说明没走原生路径。
- 原生编译存储过程不支持动态 SQL、临时表、游标、
EXEC、PRINT等多数运行时功能 - 必须绑定到具体数据库和模式(
SCHEMABINDING) - 事务隔离级别强制为
SNAPSHOT或REPEATABLE READ,不能用READ COMMITTED
创建原生编译存储过程的硬性条件
缺一不可,否则 CREATE PROCEDURE 直接报错:Msg 12325, Level 15, State 10: Native compilation is only supported for procedures that access memory-optimized tables.
- 目标表必须是内存优化表(
MEMORY_OPTIMIZED = ON),且DURABILITY = SCHEMA_AND_DATA - 存储过程中所有 DML 操作的表都得是内存优化表(不能混用磁盘表)
- 必须显式指定
ATOMIC块,包含TRANSACTION ISOLATION LEVEL和LANGUAGE - 参数类型需明确,不支持
sql_variant、xml(无 schema collection)、geography等
示例最小可用结构:
CREATE PROCEDURE dbo.InsertOrder_Native
@OrderID INT,
@CustomerID INT,
@OrderDate DATETIME2
WITH NATIVE_COMPILATION, SCHEMABINDING
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english')
INSERT INTO dbo.Orders_InMemory (OrderID, CustomerID, OrderDate)
VALUES (@OrderID, @CustomerID, @OrderDate);
END;
INSERT 性能瓶颈常出在哪儿?
即使用了原生编译,吞吐仍上不去,大概率卡在以下三处:
-
BUCKET_COUNT设置不合理:主键是 HASH 索引时,若设为 1000 但实际要存 50 万行,哈希桶严重冲突,退化成链表遍历,sys.dm_db_xtp_hash_index_stats中avg_chain_length > 10就危险了 - 未启用延迟持久性(
DELAYED_DURABILITY = ON):默认SCHEMA_AND_DATA是完全持久的,每次 INSERT 都触发 checkpoint 写盘;高频小批量场景可改用DELAYED_DURABILITY = ON(需数据库级开启),牺牲极小可靠性换吞吐 - 客户端连接未启用 MARS(Multiple Active Result Sets):PHP 或 .NET 客户端若未显式打开 MARS,批量调用原生存储过程会串行阻塞,
SET ARITHABORT ON也建议加上(避免计划缓存污染)
SQL Server 2022 的特别注意点
2022 版对内存 OLTP 的改进集中在稳定性而非语法扩展,但有两点容易被忽略:
- 哈希索引现在支持
ONLINE = ON重建(仅限非聚集哈希索引),但重建期间表不可写,别在高峰期做 -
sys.dm_db_xtp_table_memory_stats新增memory_used_by_indexes_kb字段,比旧版更准——别再只看memory_used_by_table_kb估算总内存占用 - 如果表含
SEQUENCE默认值(替代IDENTITY),确保序列对象本身也在同一数据库,且权限正确;否则原生过程编译失败,报错信息模糊(Msg 41307)
真正卡住性能的,往往不是语法写错,而是 BUCKET_COUNT 估错、持久性策略没对齐业务容忍度、或客户端驱动没配 MARS —— 这些地方没日志报错,但吞吐就是上不去。











