1. 首页 > SQLServer教程 > 正文

SQLServer教程FG108-SQLServer查询性能高级优化实战

内容简介

本文章主要介绍SQLServer查询性能的高级优化技术,包括查询性能的基础概念、影响因素、优化策略、工具使用等。风哥教程参考SQLServer官方文档Query Performance、Query Tuning等相关内容,将官方理论知识转化为生产实用技能,帮助DBA和开发人员更好地优化查询性能,提高数据库系统的整体性能。

目录大纲

Part01-基础概念与理论知识

1.1 SQLServer查询性能基础概念

查询性能是指SQL语句执行的速度和效率,通常用执行时间、CPU使用率、I/O操作次数等指标来衡量。良好的查询性能对于数据库系统的整体性能至关重要,直接影响用户体验和系统的可扩展性。

查询性能优化的目标是:

  • 减少查询执行时间
  • 减少CPU和内存使用
  • 减少I/O操作
  • 提高查询的可扩展性
  • 确保查询在不同数据量下的稳定性

1.2 SQLServer查询性能影响因素

影响SQLServer查询性能的因素主要包括:

  • 数据模型设计:表结构、索引设计、关系设计等
  • SQL语句编写:查询语句的复杂度、连接方式、过滤条件等
  • 索引使用:索引的类型、设计、维护等
  • 统计信息:统计信息的准确性和及时性
  • 执行计划:查询优化器生成的执行计划质量
  • 硬件资源:CPU、内存、磁盘I/O等
  • 系统配置:SQLServer配置参数、操作系统配置等
  • 数据量:表的大小、数据分布等

1.3 SQLServer查询优化器工作原理

SQLServer查询优化器是一个基于成本的优化器,它的工作原理如下:

  • 解析SQL语句:将SQL语句解析为语法树
  • 生成候选执行计划:根据语法树生成多个可能的执行计划
  • 估算执行成本:根据统计信息估算每个执行计划的成本
  • 选择最优执行计划:选择成本最低的执行计划
  • 执行查询:按照选择的执行计划执行查询

查询优化器的性能直接影响查询的执行效率,因此了解查询优化器的工作原理对于查询性能优化至关重要。

Part02-生产环境规划与建议

2.1 查询性能优化环境规划

在生产环境中进行查询性能优化需要考虑以下因素:

  • 测试环境:与生产环境配置相似的测试环境,用于查询性能测试和优化
  • 工具配置:配置SQL Server Management Studio (SSMS)、SQL Server Profiler、Extended Events等工具
  • 监控系统:建立查询性能监控系统,实时监控查询执行情况
  • 基准测试:建立查询性能基准,用于比较优化前后的性能差异

2.2 查询优化策略建议

查询优化策略应考虑以下因素:

  • 数据模型优化:合理设计表结构、索引和关系
  • SQL语句优化:优化查询语句的结构、过滤条件、连接方式等
  • 索引优化:合理设计和维护索引,确保查询能够有效使用索引
  • 统计信息优化:定期更新统计信息,确保查询优化器生成准确的执行计划
  • 执行计划优化:分析执行计划,识别性能瓶颈并进行优化
  • 硬件资源优化:根据查询特点优化硬件配置,如增加内存、使用SSD等
  • 系统配置优化:根据查询特点优化SQLServer配置参数

2.3 查询性能监控与分析建议

查询性能监控与分析应注意:

  • 监控指标:执行时间、CPU使用率、I/O操作次数、内存使用等
  • 监控工具:使用SQL Server Profiler、Extended Events、Dynamic Management Views等工具
  • 监控频率:定期监控查询性能,特别是高频查询和关键业务查询
  • 分析方法:使用执行计划分析、查询存储、性能计数器等方法分析查询性能
  • 报警机制:建立查询性能报警机制,及时发现性能问题

Part03-生产环境项目实施方案

3.1 查询性能分析工具配置

# 启用Query Store
ALTER DATABASE fgedudb SET QUERY_STORE = ON;
GO

# 配置Query Store
ALTER DATABASE fgedudb SET QUERY_STORE (
OPERATION_MODE = READ_WRITE,
CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),
DATA_FLUSH_INTERVAL_SECONDS = 900,
MAX_STORAGE_SIZE_MB = 10240,
INTERVAL_LENGTH_MINUTES = 15,
QUERY_CAPTURE_MODE = AUTO,
MAX_PLANS_PER_QUERY = 200,
WAIT_STATS_CAPTURE_MODE = ON
);
GO

# 启用Extended Events会话
CREATE EVENT SESSION [QueryPerformance] ON SERVER
ADD EVENT sqlserver.query_execution_stats(
ACTION(sqlserver.database_id,sqlserver.query_hash,sqlserver.sql_text,sqlserver.username)
)
ADD TARGET package0.event_file(
SET filename=N’/sqlserver/app/QueryPerformance.xel’,max_file_size=(50),max_rollover_files=(5)
)
WITH (
MAX_MEMORY=4096 KB,
EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY=30 SECONDS,
MAX_EVENT_SIZE=0 KB,
MEMORY_PARTITION_MODE=NONE,
TRACK_CAUSALITY=OFF,
STARTUP_STATE=ON
);
GO

# 启动Extended Events会话
ALTER EVENT SESSION [QueryPerformance] ON SERVER STATE=START;
GO

3.2 查询优化实施步骤

# 查询性能优化实施步骤

# 1. 识别性能问题查询
SELECT
qs.query_hash,
qs.total_worker_time / qs.execution_count AS avg_cpu_time_ms,
qs.total_elapsed_time / qs.execution_count AS avg_elapsed_time_ms,
qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
qs.execution_count,
st.text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
ORDER BY avg_elapsed_time_ms DESC
TOP 10;
GO

# 2. 分析执行计划
SET SHOWPLAN_TEXT ON;
GO

— 执行需要优化的查询
SELECT * FROM dbo.fgedu_sales WHERE region = ‘North’ AND sale_date BETWEEN ‘2023-01-01’ AND ‘2023-01-31’;
GO

SET SHOWPLAN_TEXT OFF;
GO

# 3. 优化查询语句
— 原始查询
SELECT * FROM dbo.fgedu_sales WHERE region = ‘North’ AND sale_date BETWEEN ‘2023-01-01’ AND ‘2023-01-31’;
GO

— 优化后查询(只选择需要的列)
SELECT sales_id, product_id, customer_id, sale_date, total_amount
FROM dbo.fgedu_sales
WHERE region = ‘North’ AND sale_date BETWEEN ‘2023-01-01’ AND ‘2023-01-31’;
GO

# 4. 创建或优化索引
CREATE NONCLUSTERED INDEX IX_fgedu_sales_region_sale_date ON dbo.fgedu_sales(region, sale_date);
GO

# 5. 更新统计信息
UPDATE STATISTICS dbo.fgedu_sales;
GO

# 6. 验证优化效果
SELECT
qs.query_hash,
qs.total_worker_time / qs.execution_count AS avg_cpu_time_ms,
qs.total_elapsed_time / qs.execution_count AS avg_elapsed_time_ms,
qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
qs.execution_count,
st.text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
WHERE st.text LIKE ‘%SELECT * FROM dbo.fgedu_sales WHERE region = ”North”%’;
GO

3.3 查询性能监控与维护

# 创建查询性能监控表
CREATE TABLE dbo.fgedu_query_performance (
monitor_id INT PRIMARY KEY IDENTITY,
query_hash VARBINARY(8) NOT NULL,
sql_text NVARCHAR(MAX) NOT NULL,
avg_cpu_time_ms FLOAT NOT NULL,
avg_elapsed_time_ms FLOAT NOT NULL,
avg_logical_reads FLOAT NOT NULL,
execution_count INT NOT NULL,
monitor_time DATETIME NOT NULL DEFAULT GETDATE()
);
GO

# 创建查询性能监控存储过程
CREATE PROCEDURE dbo.usp_monitor_query_performance
AS
BEGIN
SET NOCOUNT ON;

INSERT INTO dbo.fgedu_query_performance (
query_hash,
sql_text,
avg_cpu_time_ms,
avg_elapsed_time_ms,
avg_logical_reads,
execution_count
)
SELECT
qs.query_hash,
st.text,
qs.total_worker_time / qs.execution_count AS avg_cpu_time_ms,
qs.total_elapsed_time / qs.execution_count AS avg_elapsed_time_ms,
qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
qs.execution_count
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
WHERE qs.execution_count > 10
ORDER BY avg_elapsed_time_ms DESC
TOP 100;
END;
GO

# 执行查询性能监控
EXEC dbo.usp_monitor_query_performance;
GO

# 查看监控结果
SELECT * FROM dbo.fgedu_query_performance ORDER BY monitor_time DESC, avg_elapsed_time_ms DESC;
GO

# 创建查询性能报告存储过程
CREATE PROCEDURE dbo.usp_generate_query_performance_report
@start_date DATETIME,
@end_date DATETIME
AS
BEGIN
SET NOCOUNT ON;

SELECT
CONVERT(VARCHAR(10), monitor_time, 120) AS monitor_date,
COUNT(*) AS query_count,
AVG(avg_elapsed_time_ms) AS avg_avg_elapsed_time_ms,
MAX(avg_elapsed_time_ms) AS max_avg_elapsed_time_ms,
MIN(avg_elapsed_time_ms) AS min_avg_elapsed_time_ms
FROM dbo.fgedu_query_performance
WHERE monitor_time BETWEEN @start_date AND @end_date
GROUP BY CONVERT(VARCHAR(10), monitor_time, 120)
ORDER BY monitor_date;
END;
GO

# 生成查询性能报告
EXEC dbo.usp_generate_query_performance_report
@start_date = ‘2023-12-01’,
@end_date = ‘2023-12-31’;
GO

Part04-生产案例与实战讲解

4.1 查询性能分析实战案例

# 创建测试表
CREATE TABLE dbo.fgedu_orders (
order_id INT PRIMARY KEY IDENTITY,
customer_id INT NOT NULL,
order_date DATETIME NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
status NVARCHAR(50) NOT NULL,
create_time DATETIME DEFAULT GETDATE()
);
GO

# 插入测试数据
DECLARE @i INT = 1;
WHILE @i <= 100000 BEGIN INSERT INTO dbo.fgedu_orders ( customer_id, order_date, total_amount, status ) VALUES (@i % 10000, DATEADD(day, @i % 365, '2023-01-01'), 1000.00 + @i, CASE WHEN @i % 5 = 0 THEN 'Completed' WHEN @i % 5 = 1 THEN 'Pending' WHEN @i % 5 = 2 THEN 'Cancelled' WHEN @i % 5 = 3 THEN 'Processing' ELSE 'Shipped' END); SET @i = @i + 1; END; GO # 分析查询性能 -- 执行查询并查看执行计划 SET STATISTICS IO ON; SET STATISTICS TIME ON; GO SELECT * FROM dbo.fgedu_orders WHERE customer_id = 1000 AND order_date BETWEEN '2023-01-01' AND '2023-01-31'; GO -- 结果 /* (10 row(s) affected) Table 'fgedu_orders'. Scan count 1, logical reads 100000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. SQL Server Execution Times: CPU time = 156 ms, elapsed time = 160 ms. */ -- 查看执行计划 SET SHOWPLAN_TEXT ON; GO SELECT * FROM dbo.fgedu_orders WHERE customer_id = 1000 AND order_date BETWEEN '2023-01-01' AND '2023-01-31'; GO -- 执行计划 /* |--Clustered Index Scan(OBJECT:([fgedudb].[dbo].[fgedu_orders].[PK__fgedu_or__46596229A1234567]), WHERE:([fgedudb].[dbo].[fgedu_orders].[customer_id]=(1000) AND [fgedudb].[dbo].[fgedu_orders].[order_date]>=’2023-01-01′ AND [fgedudb].[dbo].[fgedu_orders].[order_date]<='2023-01-31')) */ SET SHOWPLAN_TEXT OFF; SET STATISTICS IO OFF; SET STATISTICS TIME OFF; GO

4.2 查询优化实战案例

# 优化查询

# 1. 创建索引
CREATE NONCLUSTERED INDEX IX_fgedu_orders_customer_date ON dbo.fgedu_orders(customer_id, order_date);
GO

# 2. 优化查询语句
— 原始查询
SELECT * FROM dbo.fgedu_orders WHERE customer_id = 1000 AND order_date BETWEEN ‘2023-01-01’ AND ‘2023-01-31’;
GO

— 优化后查询(只选择需要的列)
SELECT order_id, customer_id, order_date, total_amount, status
FROM dbo.fgedu_orders
WHERE customer_id = 1000 AND order_date BETWEEN ‘2023-01-01’ AND ‘2023-01-31’;
GO

# 3. 验证优化效果
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO

SELECT order_id, customer_id, order_date, total_amount, status
FROM dbo.fgedu_orders
WHERE customer_id = 1000 AND order_date BETWEEN ‘2023-01-01’ AND ‘2023-01-31’;
GO

— 结果
/*
(10 row(s) affected)
Table ‘fgedu_orders’. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.
*/

— 查看执行计划
SET SHOWPLAN_TEXT ON;
GO

SELECT order_id, customer_id, order_date, total_amount, status
FROM dbo.fgedu_orders
WHERE customer_id = 1000 AND order_date BETWEEN ‘2023-01-01’ AND ‘2023-01-31′;
GO

— 执行计划
/*
|–Index Seek(OBJECT:([fgedudb].[dbo].[fgedu_orders].[IX_fgedu_orders_customer_date]), SEEK:([fgedudb].[dbo].[fgedu_orders].[customer_id]=(1000) AND [fgedudb].[dbo].[fgedu_orders].[order_date]>=’2023-01-01’ AND [fgedudb].[dbo].[fgedu_orders].[order_date]<='2023-01-31'), ORDERED FORWARD) */ SET SHOWPLAN_TEXT OFF; SET STATISTICS IO OFF; SET STATISTICS TIME OFF; GO

4.3 查询性能问题排查案例

# 排查查询性能问题

# 1. 识别性能问题查询
SELECT
qs.query_hash,
qs.total_worker_time / qs.execution_count AS avg_cpu_time_ms,
qs.total_elapsed_time / qs.execution_count AS avg_elapsed_time_ms,
qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
qs.execution_count,
st.text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
ORDER BY avg_elapsed_time_ms DESC
TOP 5;
GO

# 2. 分析执行计划
SET SHOWPLAN_TEXT ON;
GO

— 执行性能问题查询
SELECT
c.customer_id,
c.customer_name,
COUNT(o.order_id) AS order_count,
SUM(o.total_amount) AS total_amount
FROM dbo.fgedu_customers c
LEFT JOIN dbo.fgedu_orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name
ORDER BY total_amount DESC;
GO

— 执行计划
/*
|–Sort(ORDER BY:([Expr1004] DESC))
|–Hash Match(Aggregate, HASH:([c].[customer_id], [c].[customer_name]), RESIDUAL:([fgedudb].[dbo].[fgedu_customers].[customer_id]=[fgedudb].[dbo].[fgedu_customers].[customer_id] AND [fgedudb].[dbo].[fgedu_customers].[customer_name]=[fgedudb].[dbo].[fgedu_customers].[customer_name]) DEFINE:([Expr1003]=COUNT([fgedudb].[dbo].[fgedu_orders].[order_id]), [Expr1004]=SUM([fgedudb].[dbo].[fgedu_orders].[total_amount]))
|–Hash Match(Left Outer Join, HASH:([c].[customer_id])=([o].[customer_id]))
|–Clustered Index Scan(OBJECT:([fgedudb].[dbo].[fgedu_customers].[PK__fgedu_cu__CD65CB85A1234567]))
|–Clustered Index Scan(OBJECT:([fgedudb].[dbo].[fgedu_orders].[PK__fgedu_or__46596229A1234567]))
*/

SET SHOWPLAN_TEXT OFF;
GO

# 3. 优化查询
— 创建索引
CREATE NONCLUSTERED INDEX IX_fgedu_orders_customer_id ON dbo.fgedu_orders(customer_id) INCLUDE (total_amount);
GO

— 优化查询语句
SELECT
c.customer_id,
c.customer_name,
COUNT(o.order_id) AS order_count,
SUM(o.total_amount) AS total_amount
FROM dbo.fgedu_customers c
LEFT JOIN dbo.fgedu_orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name
ORDER BY total_amount DESC;
GO

# 4. 验证优化效果
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO

SELECT
c.customer_id,
c.customer_name,
COUNT(o.order_id) AS order_count,
SUM(o.total_amount) AS total_amount
FROM dbo.fgedu_customers c
LEFT JOIN dbo.fgedu_orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name
ORDER BY total_amount DESC;
GO

— 结果
/*
(10000 row(s) affected)
Table ‘fgedu_orders’. Scan count 1, logical reads 10000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘fgedu_customers’. Scan count 1, logical reads 100, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

SQL Server Execution Times:
CPU time = 156 ms, elapsed time = 160 ms.
*/

— 查看执行计划
SET SHOWPLAN_TEXT ON;
GO

SELECT
c.customer_id,
c.customer_name,
COUNT(o.order_id) AS order_count,
SUM(o.total_amount) AS total_amount
FROM dbo.fgedu_customers c
LEFT JOIN dbo.fgedu_orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name
ORDER BY total_amount DESC;
GO

— 执行计划
/*
|–Sort(ORDER BY:([Expr1004] DESC))
|–Hash Match(Aggregate, HASH:([c].[customer_id], [c].[customer_name]), RESIDUAL:([fgedudb].[dbo].[fgedu_customers].[customer_id]=[fgedudb].[dbo].[fgedu_customers].[customer_id] AND [fgedudb].[dbo].[fgedu_customers].[customer_name]=[fgedudb].[dbo].[fgedu_customers].[customer_name]) DEFINE:([Expr1003]=COUNT([fgedudb].[dbo].[fgedu_orders].[order_id]), [Expr1004]=SUM([fgedudb].[dbo].[fgedu_orders].[total_amount]))
|–Hash Match(Left Outer Join, HASH:([c].[customer_id])=([o].[customer_id]))
|–Clustered Index Scan(OBJECT:([fgedudb].[dbo].[fgedu_customers].[PK__fgedu_cu__CD65CB85A1234567]))
|–Index Scan(OBJECT:([fgedudb].[dbo].[fgedu_orders].[IX_fgedu_orders_customer_id]))
*/

SET SHOWPLAN_TEXT OFF;
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
GO

Part05-风哥经验总结与分享

5.1 查询性能优化最佳实践

  • 分析查询模式:了解常见的查询模式,优先优化高频查询和关键业务查询
  • 优化数据模型:合理设计表结构、索引和关系,确保数据模型适合查询需求
  • 编写高效SQL:使用合适的查询结构、过滤条件和连接方式,避免不必要的计算和数据扫描
  • 合理使用索引:为频繁查询的列创建合适的索引,确保查询能够有效使用索引
  • 维护统计信息:定期更新统计信息,确保查询优化器生成准确的执行计划
  • 分析执行计划:使用执行计划分析工具,识别性能瓶颈并进行优化
  • 监控查询性能:建立查询性能监控系统,实时监控查询执行情况
  • 持续优化:根据业务需求和数据量的变化,持续优化查询性能

5.2 查询性能常见问题与解决方案

  • 全表扫描:查询没有使用索引,导致全表扫描。解决方案:创建合适的索引,优化查询语句。
  • 索引失效:查询条件使用了函数、表达式或类型转换,导致索引失效。解决方案:修改查询条件,避免在索引列上使用函数或表达式。
  • 连接效率低:连接方式选择不当,导致连接效率低。解决方案:优化连接方式,使用合适的连接类型。
  • 排序开销大:查询需要大量排序操作,导致性能下降。解决方案:创建包含排序字段的索引,减少排序开销。
  • 子查询性能差:子查询执行效率低,导致整体查询性能下降。解决方案:使用连接替代子查询,或优化子查询结构。
  • 统计信息过期:统计信息过期,导致查询优化器生成不准确的执行计划。解决方案:定期更新统计信息。
  • 参数嗅探:参数嗅探导致执行计划不适合所有参数值。解决方案:使用 OPTION (RECOMPILE) 或 OPTIMIZE FOR 提示。

5.3 查询性能监控与维护建议

  • 建立监控体系:使用SQL Server Profiler、Extended Events、Dynamic Management Views等工具建立查询性能监控体系
  • 设置性能基线:建立查询性能基线,用于比较优化前后的性能差异
  • 定期分析:定期分析查询性能数据,识别性能问题并及时优化
  • 自动化维护:使用作业定期执行索引维护、统计信息更新等操作
  • 报警机制:建立查询性能报警机制,及时发现和处理性能问题
  • 知识共享:建立查询性能优化的最佳实践文档,共享经验和技巧
  • 持续改进:根据业务需求和技术发展,持续改进查询性能优化策略

更多视频教程www.fgedu.net.cn

学习交流加群风哥微信: itpux-com

学习交流加群风哥QQ113257174

风哥提示:查询性能优化是SQLServer性能调优的核心,掌握查询性能优化技术对于提高数据库系统的整体性能至关重要。

更多学习教程公众号风哥教程itpux_com

from SQLServer视频:www.itpux.com

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息