SQL Server 查询存储允许你监视运行内存中 OLTP 工作负载的本机编译代码的性能。
编译和运行时统计与基于磁盘的工作负荷的收集和公开方式相同。
迁移到内存中 OLTP 时,可以继续使用 SQL Server Management Studio 中的查询存储视图,以及在迁移之前为基于磁盘的工作负载开发的自定义脚本。 这样既能保全你在学习查询存储(查询存储)技术上的投入,又能将其用于对所有工作负载进行故障排查。
有关使用查询存储的一般信息,请参阅 使用查询存储监视性能。
将查询存储与内存中 OLTP 一起使用时,无需进行任何额外的功能配置。 在数据库上启用时,适用于所有类型的工作负载。
但是,在将查询存储与内存中 OLTP 一起使用时,用户需要注意一些特定事项:
启用查询存储后,将按默认方式收集查询、计划和编译时统计信息。 但是,运行时统计信息收集不会被激活,除非通过 sys.sp_xtp_control_query_exec_stats (Transact-SQL) 显式启用它。
将 @new_collection_value 设置为 0 时,查询存储会停止为受影响的过程或整个 SQL Server 实例收集运行时统计信息。
使用 sys.sp_xtp_control_query_exec_stats (Transact-SQL) 配置的值不会持久保存。 请确保重新启动 SQL Server 后再次检查和配置统计信息收集。
与常规查询统计信息收集一样,使用查询存储跟踪工作负载执行时,性能可能会降低。 建议仅对本机编译的存储过程的重要子集启用统计信息收集。
在首次进行本机编译时将捕获并存储查询和计划,并将在每次重新编译时进行更新。
如果启用查询存储或在编译所有本机存储过程后清除了其内容,则必须手动进行重新编译,以使查询存储能够将其捕获。 如果使用 sp_query_store_remove_query (Transact-SQL) 或 sp_query_store_remove_plan (Transct-SQL) 手动移除查询,这同样适用。 使用 sp_recompile (Transact-SQL) 强制进行过程重新编译。
在编译过程中,查询存储利用内存中 OLTP 中的计划生成机制捕获查询执行计划。 存储的计划在语义上等效于使用
SET SHOWPLAN_XML ON所获取的计划,但有一处不同;查询存储中的计划按每个单独的语句进行拆分与存储。在具有混合工作负载的数据库中运行 查询存储 时,可以使用 is_natively_compiled 字段(来自 sys.query_store_plan (Transact-SQL))快速查找由原生代码编译生成的查询计划。
查询存储捕获模式(QUERY_CAPTURE_MODE语句中的ALTER TABLE参数)不会影响本机编译模块的查询,因为它们始终被捕获,而不管配置的值如何。 这包括设置
QUERY_CAPTURE_MODE = NONE。查询存储中捕获的查询编译持续时间仅包括在生成本机代码之前查询优化阶段所花费的时间。 更确切地说,其中不包括 C 代码编译时间,以及生成 C 代码所需内部结构的时间。
sys.query_store_runtime_stats (Transact-SQL) 中的内存授予指标对于本机编译的查询不会填充值;这些指标的值始终为 0。 内存授予列有:avg_query_max_used_memory、last_query_max_used_memory、min_query_max_used_memory、max_query_max_used_memory 和 stdev_query_max_used_memory。
启用并使用适用于内存中 OLTP 的查询存储
下面这个简单示例演示了在一个端到端用户场景中将 查询存储 与内存中 OLTP 结合使用。 在此示例中,我们假设为内存中 OLTP 启用了一个数据库 (MemoryOLTP)。
有关内存优化表先决条件的更多信息,请参阅“创建内存优化表和本机编译的存储过程”。
USE MemoryOLTP;
GO
-- Create a simple memory-optimized table
CREATE TABLE dbo.Ord
(OrdNo INTEGER not null PRIMARY KEY NONCLUSTERED,
OrdDate DATETIME not null,
CustCode NVARCHAR(5) not null)
WITH (MEMORY_OPTIMIZED=ON);
GO
-- Enable Query Store before native module compilation
ALTER DATABASE MemoryOLTP SET QUERY_STORE = ON;
GO
-- Create natively compiled stored procedure
CREATE PROCEDURE dbo.OrderInsert(@OrdNo integer, @CustCode nvarchar(5))
WITH NATIVE_COMPILATION, SCHEMABINDING
AS
BEGIN ATOMIC WITH
(TRANSACTION ISOLATION LEVEL = SNAPSHOT,
LANGUAGE = N'English')
DECLARE @OrdDate DATETIME = GETDATE();
INSERT INTO dbo.Ord (OrdNo, CustCode, OrdDate)
VALUES (@OrdNo, @CustCode, @OrdDate);
END;
GO
-- Enable runtime stats collection for queries from dbo.OrderInsert stored procedure
DECLARE @db_id INT = DB_ID()
DECLARE @proc_id INT = OBJECT_ID('dbo.OrderInsert');
DECLARE @collection_enabled BIT;
EXEC [sys].[sp_xtp_control_query_exec_stats] @new_collection_value = 1,
@database_id = @db_id, @xtp_object_id = @proc_id;
-- Check the state of the collection flag
EXEC sp_xtp_control_query_exec_stats @database_id = @db_id,
@xtp_object_id = @proc_id,
@old_collection_value= @collection_enabled output;
SELECT @collection_enabled AS 'collection status';
-- Execute natively compiled workload
EXEC dbo.OrderInsert 1, 'A';
EXEC dbo.OrderInsert 2, 'B';
EXEC dbo.OrderInsert 3, 'C';
EXEC dbo.OrderInsert 4, 'D';
EXEC dbo.OrderInsert 5, 'E';
-- Check Query Store Data
-- Compile time data
SELECT q.query_id, plan_id, object_id, query_hash, p.query_plan,
p.initial_compile_start_time, p.last_compile_start_time,
p.last_execution_time, p.avg_compile_duration,
p.last_force_failure_reason, p.force_failure_count
FROM sys.query_store_query AS q
JOIN sys.query_store_plan AS p
ON q.query_id = p.plan_id
WHERE q.object_id = OBJECT_ID('dbo.OrderInsert');
-- Get runtime stats
-- Check count_executions field to verify that runtime statistics
-- have been collected by the Query Store
SELECT q.query_id, p.plan_id, object_id, rsi.start_time, rsi.end_time,
p.last_force_failure_reason, p.force_failure_count, rs.*
FROM sys.query_store_query AS q
JOIN sys.query_store_plan AS p
ON q.query_id = p.plan_id
JOIN sys.query_store_runtime_stats AS rs
ON rs.plan_id = p.plan_id
JOIN sys.query_store_runtime_stats_interval AS rsi
ON rs.runtime_stats_interval_id = rsi.runtime_stats_interval_id
WHERE q.object_id = OBJECT_ID('dbo.OrderInsert');