SQLServer教程FG106-SQLServer统计信息高级管理与优化实战
内容简介
本文章主要介绍SQLServer统计信息的高级管理与优化技术,包括统计信息的基础概念、自动更新机制、手动维护策略、性能影响分析等。风哥教程参考SQLServer官方文档Statistics、Query Optimization等相关内容,将官方理论知识转化为生产实用技能,帮助DBA和开发人员更好地管理和优化统计信息,提高查询性能。
目录大纲
Part01-基础概念与理论知识
1.1 SQLServer统计信息基础概念
统计信息是SQLServer查询优化器用来估算查询结果集大小和选择执行计划的重要依据。它包含了表或索引中数据的分布情况,如值的频率、分布范围等。准确的统计信息对于生成最优执行计划至关重要,而过时的统计信息可能导致查询性能下降。
1.2 SQLServer统计信息类型与结构
SQLServer统计信息主要分为以下类型:
- 单列统计信息:针对单个列创建的统计信息
- 多列统计信息:针对多个列创建的统计信息,用于优化多列查询
- 索引统计信息:自动为索引创建的统计信息
- 自动创建的统计信息:优化器自动创建的统计信息
统计信息的结构主要包括:
- 直方图:显示列值的分布情况
- 密度向量:显示列值的唯一性
- 统计信息元数据:包括创建时间、更新时间、采样率等
1.3 SQLServer统计信息自动更新机制
SQLServer的统计信息自动更新机制基于以下规则:
- 小表(行数≤500):当数据修改量超过500行时自动更新
- 大表(行数>500):当数据修改量超过500 + 20%的表行数时自动更新
- 临时表:默认情况下不会自动更新统计信息
自动更新统计信息的触发条件可以通过数据库级别或表级别的设置进行调整。
Part02-生产环境规划与建议
2.1 统计信息管理环境规划
在生产环境中管理统计信息需要考虑以下因素:
- 更新频率:根据数据变更频率确定统计信息的更新频率
- 更新时机:选择系统负载较低的时段进行统计信息更新
- 采样率:根据表大小和数据分布情况选择合适的采样率
- 存储空间:统计信息会占用一定的存储空间,需要合理规划
2.2 统计信息更新策略建议
统计信息更新策略应考虑以下因素:
- 自动更新:对于数据变更频率较低的表,使用自动更新机制
- 手动更新:对于数据变更频率较高的表,定期手动更新统计信息
- 增量更新:对于大型表,考虑使用增量更新减少更新时间
- 采样率调整:对于数据分布不均匀的表,提高采样率以获得更准确的统计信息
2.3 统计信息存储与管理建议
统计信息的存储与管理应注意:
- 存储位置:统计信息存储在系统表中,需要确保系统数据库有足够的空间
- 保留策略:根据业务需求设置统计信息的保留时间
- 定期清理:定期清理过期的统计信息,避免占用过多存储空间
- 备份策略:确保统计信息包含在数据库备份中,以便在恢复后保持统计信息的完整性
Part03-生产环境项目实施方案
3.1 统计信息配置与设置
SELECT name, is_auto_update_stats_on, is_auto_create_stats_on
FROM sys.databases
WHERE name = ‘fgedudb’;
GO
# 结果
/*
name is_auto_update_stats_on is_auto_create_stats_on
——— ———————– ———————–
fgedudb 1 1
*/
# 启用自动更新统计信息
ALTER DATABASE fgedudb SET AUTO_UPDATE_STATISTICS ON;
GO
# 启用自动创建统计信息
ALTER DATABASE fgedudb SET AUTO_CREATE_STATISTICS ON;
GO
# 启用异步自动更新统计信息
ALTER DATABASE fgedudb SET AUTO_UPDATE_STATISTICS_ASYNC ON;
GO
3.2 统计信息手动更新实现
UPDATE STATISTICS dbo.fgedu_sales;
GO
# 更新单个表的指定统计信息
UPDATE STATISTICS dbo.fgedu_sales IX_fgedu_sales_region;
GO
# 更新单个表的所有统计信息,指定采样率
UPDATE STATISTICS dbo.fgedu_sales WITH SAMPLE 50 PERCENT;
GO
# 更新单个表的所有统计信息,使用完全扫描
UPDATE STATISTICS dbo.fgedu_sales WITH FULLSCAN;
GO
# 更新数据库中所有表的统计信息
EXEC sp_updatestats;
GO
# 更新数据库中所有表的统计信息,使用完全扫描
EXEC sp_updatestats ‘RESAMPLE’;
GO
3.3 统计信息分析与监控实施
DBCC SHOW_STATISTICS(‘dbo.fgedu_sales’, IX_fgedu_sales_region);
GO
# 结果
/*
Name Updated Rows Rows Sampled Steps Density Average Key Length String Index
—————– ——————- ——— ————– —– ————- —————— ————
IX_fgedu_sales_region 2023-12-01 10:00:00.000 1000000 100000 20 0.2500000 26 NO
All Density Average Length Columns
————- ————– ——-
0.2500000 26 region
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
———— ———— ——— ——————– ————–
East 249999.75 250000 0 1
North 249999.75 250000 0 1
South 249999.75 250000 0 1
West 0 250000 0 1
*/
# 查看数据库中所有统计信息的状态
SELECT
t.name AS table_name,
s.name AS stats_name,
STATS_DATE(t.object_id, s.stats_id) AS last_updated,
s.auto_created,
s.user_created,
s.no_recompute
FROM sys.tables t
JOIN sys.stats s ON t.object_id = s.object_id
WHERE t.name LIKE ‘fgedu%’
ORDER BY t.name, s.name;
GO
# 创建统计信息监控表
CREATE TABLE dbo.fgedu_stats_monitor (
monitor_id INT PRIMARY KEY IDENTITY,
table_name NVARCHAR(128) NOT NULL,
stats_name NVARCHAR(128) NOT NULL,
last_updated DATETIME NOT NULL,
update_time DATETIME NOT NULL DEFAULT GETDATE(),
rows INT NOT NULL,
rows_sampled INT NOT NULL,
steps INT NOT NULL,
density FLOAT NOT NULL
);
GO
# 创建统计信息监控存储过程
CREATE PROCEDURE dbo.usp_monitor_stats
AS
BEGIN
SET NOCOUNT ON;
DECLARE @table_name NVARCHAR(128);
DECLARE @stats_name NVARCHAR(128);
DECLARE @last_updated DATETIME;
DECLARE @rows INT;
DECLARE @rows_sampled INT;
DECLARE @steps INT;
DECLARE @density FLOAT;
DECLARE stats_cursor CURSOR FOR
SELECT
t.name AS table_name,
s.name AS stats_name,
STATS_DATE(t.object_id, s.stats_id) AS last_updated
FROM sys.tables t
JOIN sys.stats s ON t.object_id = s.object_id
WHERE t.name LIKE ‘fgedu%’;
OPEN stats_cursor;
FETCH NEXT FROM stats_cursor INTO @table_name, @stats_name, @last_updated;
WHILE @@FETCH_STATUS = 0
BEGIN
— 执行DBCC SHOW_STATISTICS获取详细信息
— 这里需要使用动态SQL来执行DBCC命令
DECLARE @sql NVARCHAR(MAX);
SET @sql = N’DBCC SHOW_STATISTICS(”’ + @table_name + ”’, ”’ + @stats_name + ”’) WITH STAT_HEADER’;
— 这里可以使用临时表来存储DBCC结果
— 为了简化,这里只存储基本信息
INSERT INTO dbo.fgedu_stats_monitor (
table_name, stats_name, last_updated, rows, rows_sampled, steps, density
) VALUES (
@table_name, @stats_name, @last_updated, 0, 0, 0, 0
);
FETCH NEXT FROM stats_cursor INTO @table_name, @stats_name, @last_updated;
END;
CLOSE stats_cursor;
DEALLOCATE stats_cursor;
END;
GO
# 执行统计信息监控
EXEC dbo.usp_monitor_stats;
GO
# 查看监控结果
SELECT * FROM dbo.fgedu_stats_monitor ORDER BY update_time DESC;
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
# 插入测试数据
INSERT INTO dbo.fgedu_orders (
customer_id, order_date, total_amount, status
) VALUES
(1, ‘2023-01-01’, 1000.00, ‘Completed’),
(2, ‘2023-01-02’, 2000.00, ‘Pending’),
(3, ‘2023-01-03’, 1500.00, ‘Completed’),
(4, ‘2023-01-04’, 3000.00, ‘Pending’),
(5, ‘2023-01-05’, 2500.00, ‘Completed’);
GO
# 查看统计信息
SELECT
t.name AS table_name,
s.name AS stats_name,
STATS_DATE(t.object_id, s.stats_id) AS last_updated
FROM sys.tables t
JOIN sys.stats s ON t.object_id = s.object_id
WHERE t.name = ‘fgedu_orders’;
GO
# 结果
/*
table_name stats_name last_updated
———– ——————– ———————–
fgedu_orders PK__fgedu_or__46596229A1234567 2023-12-01 10:00:00.000
*/
# 插入大量数据
DECLARE @i INT = 1;
WHILE @i <= 10000
BEGIN
INSERT INTO dbo.fgedu_orders (
customer_id, order_date, total_amount, status
) VALUES
(@i, DATEADD(day, @i % 365, '2023-01-01'), 1000.00 + @i, CASE WHEN @i % 2 = 0 THEN 'Completed' ELSE 'Pending' END);
SET @i = @i + 1;
END;
GO
# 查看统计信息更新情况
SELECT
t.name AS table_name,
s.name AS stats_name,
STATS_DATE(t.object_id, s.stats_id) AS last_updated
FROM sys.tables t
JOIN sys.stats s ON t.object_id = s.object_id
WHERE t.name = 'fgedu_orders';
GO
# 手动更新统计信息
UPDATE STATISTICS dbo.fgedu_orders WITH FULLSCAN;
GO
# 再次查看统计信息更新情况
SELECT
t.name AS table_name,
s.name AS stats_name,
STATS_DATE(t.object_id, s.stats_id) AS last_updated
FROM sys.tables t
JOIN sys.stats s ON t.object_id = s.object_id
WHERE t.name = 'fgedu_orders';
GO
4.2 统计信息优化实战案例
CREATE TABLE dbo.fgedu_products (
product_id INT PRIMARY KEY IDENTITY,
product_name NVARCHAR(100) NOT NULL,
category NVARCHAR(50) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
stock INT NOT NULL,
create_time DATETIME DEFAULT GETDATE()
);
GO
# 插入测试数据
INSERT INTO dbo.fgedu_products (
product_name, category, price, stock
) VALUES
(‘Product 1’, ‘Electronics’, 1000.00, 100),
(‘Product 2’, ‘Electronics’, 2000.00, 50),
(‘Product 3’, ‘Clothing’, 500.00, 200),
(‘Product 4’, ‘Clothing’, 800.00, 150),
(‘Product 5’, ‘Home’, 1200.00, 80);
GO
# 创建索引
CREATE INDEX IX_fgedu_products_category ON dbo.fgedu_products(category);
GO
# 查看统计信息
DBCC SHOW_STATISTICS(‘dbo.fgedu_products’, IX_fgedu_products_category);
GO
# 执行查询并查看执行计划
SELECT * FROM dbo.fgedu_products WHERE category = ‘Electronics’;
GO
# 插入大量数据,使数据分布发生变化
DECLARE @i INT = 1;
WHILE @i <= 10000
BEGIN
INSERT INTO dbo.fgedu_products (
product_name, category, price, stock
) VALUES
('Product ' + CAST(@i + 5 AS NVARCHAR(10)),
CASE WHEN @i % 10 = 0 THEN 'Electronics' ELSE 'Clothing' END,
1000.00 + @i, 100 + @i);
SET @i = @i + 1;
END;
GO
# 再次执行查询并查看执行计划
SELECT * FROM dbo.fgedu_products WHERE category = 'Electronics';
GO
# 更新统计信息
UPDATE STATISTICS dbo.fgedu_products IX_fgedu_products_category WITH FULLSCAN;
GO
# 再次执行查询并查看执行计划
SELECT * FROM dbo.fgedu_products WHERE category = 'Electronics';
GO
4.3 统计信息问题排查案例
SELECT
t.name AS table_name,
s.name AS stats_name,
STATS_DATE(t.object_id, s.stats_id) AS last_updated,
s.auto_created,
s.user_created
FROM sys.tables t
JOIN sys.stats s ON t.object_id = s.object_id
WHERE t.name LIKE ‘fgedu%’
ORDER BY last_updated ASC;
GO
# 检查统计信息是否过期
SELECT
t.name AS table_name,
s.name AS stats_name,
STATS_DATE(t.object_id, s.stats_id) AS last_updated,
DATEDIFF(day, STATS_DATE(t.object_id, s.stats_id), GETDATE()) AS days_since_update
FROM sys.tables t
JOIN sys.stats s ON t.object_id = s.object_id
WHERE t.name LIKE ‘fgedu%’
AND STATS_DATE(t.object_id, s.stats_id) < DATEADD(day, -7, GETDATE())
ORDER BY days_since_update DESC;
GO
# 检查表的修改统计信息
SELECT
t.name AS table_name,
ps.row_count,
ps.modification_counter
FROM sys.tables t
JOIN sys.dm_db_partition_stats ps ON t.object_id = ps.object_id
WHERE t.name LIKE 'fgedu%'
ORDER BY ps.modification_counter DESC;
GO
# 强制更新过期的统计信息
DECLARE @table_name NVARCHAR(128);
DECLARE @sql NVARCHAR(MAX);
DECLARE table_cursor CURSOR FOR
SELECT
t.name AS table_name
FROM sys.tables t
JOIN sys.stats s ON t.object_id = s.object_id
WHERE t.name LIKE 'fgedu%'
AND STATS_DATE(t.object_id, s.stats_id) < DATEADD(day, -7, GETDATE())
GROUP BY t.name;
OPEN table_cursor;
FETCH NEXT FROM table_cursor INTO @table_name;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = N'UPDATE STATISTICS dbo.' + @table_name + ' WITH FULLSCAN';
EXEC sp_executesql @sql;
PRINT 'Updated statistics for table: ' + @table_name;
FETCH NEXT FROM table_cursor INTO @table_name;
END;
CLOSE table_cursor;
DEALLOCATE table_cursor;
GO
Part05-风哥经验总结与分享
5.1 统计信息管理最佳实践
- 定期更新:对于大型表或数据变更频繁的表,定期手动更新统计信息
- 合理采样:根据表大小和数据分布情况选择合适的采样率
- 监控过期:定期检查统计信息的更新时间,及时更新过期的统计信息
- 增量更新:对于超大型表,使用增量更新减少更新时间
- 自动与手动结合:使用自动更新机制作为基础,手动更新作为补充
5.2 统计信息常见问题与解决方案
- 统计信息过期:数据变更后统计信息未及时更新,导致执行计划选择不当。解决方案:定期手动更新统计信息。
- 采样率过低:采样率过低导致统计信息不准确,影响执行计划选择。解决方案:提高采样率,使用FULLSCAN更新统计信息。
- 统计信息缺失:某些列没有统计信息,导致优化器无法准确估算。解决方案:启用自动创建统计信息,或手动创建必要的统计信息。
- 统计信息更新开销大:大型表更新统计信息时间长,影响系统性能。解决方案:选择系统负载低的时段更新,使用增量更新。
- 临时表统计信息问题:临时表默认不自动更新统计信息,导致执行计划选择不当。解决方案:手动更新临时表的统计信息。
5.3 统计信息监控与维护建议
- 建立监控机制:定期监控统计信息的更新状态和过期情况
- 自动化维护:使用作业定期更新统计信息,特别是大型表和关键表
- 性能影响评估:评估统计信息更新对系统性能的影响,选择合适的更新策略
- 文档化管理:记录统计信息的更新策略和历史,便于问题排查
- 知识共享:建立统计信息管理的最佳实践文档,共享经验和技巧
更多视频教程www.fgedu.net.cn
学习交流加群风哥微信: itpux-com
学习交流加群风哥QQ113257174
风哥提示:统计信息是SQLServer查询优化的重要依据,保持统计信息的准确性和及时性对于提高查询性能至关重要。
更多学习教程公众号风哥教程itpux_com
from SQLServer视频:www.itpux.com
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
