1. 首页 > SQLServer教程 > 正文

SQLServer教程FG088-SQLServer索引优化实战

本文档风哥主要介绍SQLServer数据库索引优化相关知识,包括SQLServer数据库索引规划、SQLServer数据库索引实施方案、SQLServer数据库索引配置、SQLServer数据库索引验证等内容,风哥教程参考SQLServer官方文档索引优化内容编写,适合DBA人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。

Part01-基础概念与理论知识

1.1 SQLServer数据库索引概念

SQLServer索引是一种数据库对象,用于提高查询性能。索引通过创建数据的快速查找结构,减少了查询时需要扫描的数据量,从而加快了数据检索速度。索引类似于书籍的目录,允许数据库引擎快速定位数据,而不需要扫描整个表。索引是SQLServer性能优化的重要手段,合理的索引设计可以显著提高查询性能。更多视频教程www.fgedu.net.cn

SQLServer数据库索引的重要性:

  • 提高查询性能
  • 加速数据检索
  • 减少I/O操作
  • 支持高效的排序和分组
  • 提高系统整体性能

1.2 SQLServer数据库索引类型

# SQLServer数据库索引类型

1. 聚集索引 (Clustered Index)
– 按照索引键的顺序存储数据
– 每个表只能有一个聚集索引
– 通常基于主键创建
– 适合范围查询和排序操作

2. 非聚集索引 (Nonclustered Index)
– 单独存储索引结构,与数据分开
– 每个表可以有多个非聚集索引
– 包含索引键和指向数据的指针
– 适合点查询和小范围查询

3. 唯一索引 (Unique Index)
– 确保索引列的值唯一
– 可以是聚集或非聚集
– 自动创建唯一约束
– 防止重复数据

4. 覆盖索引 (Covering Index)
– 包含查询所需的所有列
– 避免书签查找
– 提高查询性能
– 减少I/O操作

5. 过滤索引 (Filtered Index)
– 基于WHERE子句创建的索引
– 只包含满足条件的数据
– 减少索引大小
– 提高查询性能

6. 全文索引 (Full-Text Index)
– 用于全文搜索
– 支持复杂的文本搜索
– 提高文本查询性能
– 适合大量文本数据

7. 空间索引 (Spatial Index)
– 用于空间数据类型
– 提高空间查询性能
– 支持地理和几何数据
– 适合位置相关查询

8. 列存储索引 (Columnstore Index)
– 按列存储数据
– 高压缩率
– 适合分析查询
– 提高数据仓库性能

# 索引类型选择参考

查询类型 推荐索引类型 优势 适用场景
————————————————————————
点查询 非聚集索引 快速定位 主键/唯一键查询
范围查询 聚集索引 顺序存储 日期/范围查询
排序操作 聚集索引 有序数据 ORDER BY操作
分组操作 非聚集索引 高效分组 GROUP BY操作
全文搜索 全文索引 文本搜索 文本内容查询
空间查询 空间索引 空间数据 位置相关查询
分析查询 列存储索引 高压缩率 数据仓库查询

1.3 SQLServer数据库索引优势

SQLServer数据库索引优势:

  • 提高查询速度:通过快速定位数据,减少查询时间
  • 减少I/O操作:避免全表扫描,降低磁盘I/O
  • 支持排序和分组:加速ORDER BY和GROUP BY操作
  • 提高系统并发:减少锁争用,提高并发处理能力
  • 改善用户体验:快速响应查询,提升应用性能
风哥提示:索引是SQLServer性能优化的关键,合理的索引设计可以显著提高查询性能,但过度索引会增加维护开销,需要平衡索引的收益和成本。学习交流加群风哥微信: itpux-com

Part02-生产环境规划与建议

2.1 SQLServer数据库索引规划

SQLServer数据库索引规划要点:

# 索引规划步骤

1. 需求分析
– 分析查询模式:点查询、范围查询、排序等
– 识别高频查询:经常执行的查询语句
– 评估数据分布:数据的分布情况和唯一性
– 分析表大小:表的大小和增长趋势

2. 索引设计
– 选择索引类型:聚集、非聚集、覆盖等
– 确定索引键:选择合适的列作为索引键
– 考虑索引顺序:多列索引的列顺序
– 设计覆盖索引:包含查询所需的列

3. 性能评估
– 评估索引收益:查询性能提升
– 评估索引成本:维护开销和存储空间
– 测试索引效果:实际执行计划分析
– 调整索引设计:根据测试结果调整

4. 维护策略
– 索引碎片管理:定期重建或重组
– 统计信息更新:确保统计信息准确
– 索引监控:监控索引使用情况
– 索引优化:定期审查和优化

5. 实施计划
– 优先级排序:按查询频率和性能影响排序
– 实施步骤:分阶段实施索引
– 测试计划:验证索引效果
– 回滚策略:准备回滚方案

# 规划参考

表类型 数据量 推荐索引数量 索引策略 维护频率
————————————————————————
OLTP表 < 1000万行 5-10个 点查询优化 每周 OLAP表 > 1000万行 10-20个 分析查询优化 每月
小型表 < 100万行 2-5个 基本索引 季度 大型表 > 1亿行 20+个 综合优化 每周

2.2 SQLServer数据库索引实施方案

SQLServer数据库索引实施方案:

# 索引实施方案

1. 索引创建
– 聚集索引创建:基于主键或常用查询列
– 非聚集索引创建:基于高频查询列
– 覆盖索引创建:包含查询所需的列
– 过滤索引创建:基于特定条件

2. 索引维护
– 碎片管理:重建或重组碎片化索引
– 统计信息更新:确保查询优化器使用准确的统计信息
– 索引监控:监控索引使用情况和性能
– 索引清理:删除未使用的索引

3. 索引优化
– 索引合并:合并相似的索引
– 索引重命名:使索引名称更具描述性
– 索引参数调整:调整填充因子等参数
– 索引重建:优化索引结构

4. 性能监控
– 查询执行计划分析:评估索引使用情况
– 索引使用统计:监控索引使用频率
– 性能计数器:监控索引相关性能指标
– 慢查询分析:识别需要索引的查询

# 实施步骤参考

步骤 任务 负责人 时间
————————————————————
1 需求分析 DBA 1周
2 索引设计 DBA 3天
3 测试环境实施 DBA 2天
4 性能测试 测试团队 2天
5 生产环境实施 DBA 1天
6 监控配置 运维团队 1天
7 定期维护 DBA 每周

2.3 SQLServer数据库索引最佳实践

SQLServer数据库索引最佳实践:

  • 为常用查询列创建索引:基于实际查询模式设计索引
  • 选择合适的索引类型:根据查询类型选择聚集或非聚集索引
  • 合理设计多列索引:将选择性高的列放在前面
  • 使用覆盖索引:包含查询所需的所有列,避免书签查找
  • 定期维护索引:重建或重组碎片化索引,更新统计信息
  • 监控索引使用:识别未使用的索引并及时清理
  • 避免过度索引:索引过多会增加维护开销和存储空间
  • 考虑填充因子:根据数据修改频率设置合适的填充因子
生产环境建议:索引实施方案应根据数据库的实际情况进行调整,建议在实施前进行充分测试,确保不会对系统性能造成负面影响。学习交流加群风哥QQ113257174

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

3.1 SQLServer数据库索引项目实施

3.1.1 SQLServer数据库索引环境准备

# 环境准备步骤

# 1. 检查SQLServer版本
SELECT @@VERSION;
GO

# 2. 创建测试数据库
CREATE DATABASE fgedu_index;
GO

# 3. 创建测试表
USE fgedu_index;
GO

CREATE TABLE dbo.fgedu_users (
id INT PRIMARY KEY IDENTITY,
username NVARCHAR(50) NOT NULL,
email NVARCHAR(100) NOT NULL,
create_time DATETIME DEFAULT GETDATE(),
status NVARCHAR(20) DEFAULT ‘Active’
);
GO

CREATE TABLE dbo.fgedu_orders (
id INT PRIMARY KEY IDENTITY,
user_id INT NOT NULL,
order_date DATETIME DEFAULT GETDATE(),
total_amount DECIMAL(10, 2) NOT NULL,
status NVARCHAR(20) DEFAULT ‘Pending’
);
GO

# 4. 插入测试数据
— 插入100万行用户数据
DECLARE @i INT = 1;
WHILE @i <= 1000000 BEGIN INSERT INTO dbo.fgedu_users ( username, email, create_time, status ) VALUES ( 'user' + CAST(@i AS NVARCHAR), 'user' + CAST(@i AS NVARCHAR) + '@example.com', DATEADD(DAY, @i % 365, '2023-01-01'), CASE @i % 3 WHEN 0 THEN 'Active' WHEN 1 THEN 'Inactive' ELSE 'Pending' END ); SET @i = @i + 1; END; GO -- 插入500万行订单数据 DECLARE @i INT = 1; WHILE @i <= 5000000 BEGIN INSERT INTO dbo.fgedu_orders ( user_id, order_date, total_amount, status ) VALUES ( ABS(CHECKSUM(NEWID())) % 1000000 + 1, DATEADD(DAY, @i % 365, '2023-01-01'), ABS(CHECKSUM(NEWID())) % 10000 + 1, CASE @i % 4 WHEN 0 THEN 'Pending' WHEN 1 THEN 'Processing' WHEN 2 THEN 'Shipped' ELSE 'Delivered' END ); SET @i = @i + 1; END; GO # 5. 检查初始索引 SELECT OBJECT_NAME(object_id) AS table_name, name AS index_name, type_desc, is_primary_key, is_unique FROM sys.indexes WHERE object_id IN (OBJECT_ID('dbo.fgedu_users'), OBJECT_ID('dbo.fgedu_orders')); GO # 6. 测试初始查询性能 DECLARE @start_time DATETIME = GETDATE(); SELECT u.id, u.username, COUNT(o.id) AS order_count, SUM(o.total_amount) AS total_spent FROM dbo.fgedu_users u LEFT JOIN dbo.fgedu_orders o ON u.id = o.user_id WHERE u.create_time BETWEEN '2023-06-01' AND '2023-06-30' AND u.status = 'Active' GROUP BY u.id, u.username ORDER BY total_spent DESC TOP 100; DECLARE @end_time DATETIME = GETDATE(); SELECT 'Before index optimization' AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms; GO

3.1.2 SQLServer数据库索引创建

# 索引创建步骤

# 1. 识别缺失索引
SELECT
TOP 10
dm_mid.database_id,
dm_mid.physical_operator_name,
dm_mid.est_cost AS estimated_cost,
dm_migs.avg_total_user_cost * dm_migs.avg_user_impact * (dm_migs.user_seeks + dm_migs.user_scans) AS improvement_measure,
‘CREATE INDEX [IX_’ + OBJECT_NAME(dm_mid.OBJECT_ID, dm_mid.database_id) + ‘_’
+ REPLACE(REPLACE(REPLACE(ISNULL(dm_mid.equality_columns, ”), ‘, ‘, ‘_’), ‘[‘, ”), ‘]’, ”)
+ CASE
WHEN dm_mid.equality_columns IS NOT NULL AND dm_mid.inequality_columns IS NOT NULL THEN ‘_’
ELSE ”
END
+ REPLACE(REPLACE(REPLACE(ISNULL(dm_mid.inequality_columns, ”), ‘, ‘, ‘_’), ‘[‘, ”), ‘]’, ”)
+ ‘]’
+ ‘ ON ‘ + dm_migs.statement
+ ‘ (‘ + ISNULL(dm_mid.equality_columns, ”)
+ CASE WHEN dm_mid.equality_columns IS NOT NULL AND dm_mid.inequality_columns IS NOT NULL THEN ‘, ‘ ELSE ” END
+ ISNULL(dm_mid.inequality_columns, ”)
+ ‘)’
+ ISNULL(‘ INCLUDE (‘ + dm_mid.included_columns + ‘)’, ”) AS create_index_statement
FROM sys.dm_db_missing_index_groups dm_mig
INNER JOIN sys.dm_db_missing_index_group_stats dm_migs ON dm_mig.index_group_handle = dm_migs.group_handle
INNER JOIN sys.dm_db_missing_index_details dm_mid ON dm_mig.index_handle = dm_mid.index_handle
ORDER BY dm_migs.avg_total_user_cost * dm_migs.avg_user_impact * (dm_migs.user_seeks + dm_migs.user_scans) DESC;
GO

# 2. 创建聚集索引
— 为orders表创建基于order_date的聚集索引
— 注意:每个表只能有一个聚集索引,这里需要先删除现有的聚集索引
— 由于orders表已经有基于id的聚集索引,我们不修改它

# 3. 创建非聚集索引
— 为users表创建基于create_time和status的非聚集索引
CREATE INDEX IX_fgedu_users_create_time_status ON dbo.fgedu_users(create_time, status);
GO

— 为orders表创建基于user_id的非聚集索引
CREATE INDEX IX_fgedu_orders_user_id ON dbo.fgedu_orders(user_id);
GO

# 4. 创建覆盖索引
— 为orders表创建覆盖索引,包含order_date和total_amount
CREATE INDEX IX_fgedu_orders_user_id_include ON dbo.fgedu_orders(user_id)
INCLUDE (order_date, total_amount);
GO

# 5. 创建过滤索引
— 为users表创建基于status=’Active’的过滤索引
CREATE INDEX IX_fgedu_users_status_active ON dbo.fgedu_users(status, create_time)
WHERE status = ‘Active’;
GO

# 6. 检查创建的索引
SELECT
OBJECT_NAME(object_id) AS table_name,
name AS index_name,
type_desc,
is_primary_key,
is_unique,
has_filter,
filter_definition
FROM sys.indexes
WHERE object_id IN (OBJECT_ID(‘dbo.fgedu_users’), OBJECT_ID(‘dbo.fgedu_orders’));
GO

# 7. 测试索引效果
DECLARE @start_time DATETIME = GETDATE();

SELECT
u.id,
u.username,
COUNT(o.id) AS order_count,
SUM(o.total_amount) AS total_spent
FROM dbo.fgedu_users u
LEFT JOIN dbo.fgedu_orders o ON u.id = o.user_id
WHERE u.create_time BETWEEN ‘2023-06-01’ AND ‘2023-06-30’
AND u.status = ‘Active’
GROUP BY u.id, u.username
ORDER BY total_spent DESC
TOP 100;

DECLARE @end_time DATETIME = GETDATE();
SELECT ‘After index optimization’ AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms;
GO

3.2 SQLServer数据库索引配置

3.2.1 SQLServer数据库索引维护

# 索引维护步骤

# 1. 检查索引碎片
SELECT
DB_NAME(database_id) AS database_name,
OBJECT_NAME(object_id) AS table_name,
index_id,
name AS index_name,
avg_fragmentation_in_percent,
fragment_count,
page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
NULL,
NULL,
NULL,
‘DETAILED’
) WHERE avg_fragmentation_in_percent > 10;
GO

# 2. 重建碎片化索引
— 重建索引(适合碎片率大于30%的情况)
ALTER INDEX ALL ON dbo.fgedu_users REBUILD WITH (FILLFACTOR = 80);
GO

ALTER INDEX ALL ON dbo.fgedu_orders REBUILD WITH (FILLFACTOR = 80);
GO

# 3. 重组碎片化索引
— 重组索引(适合碎片率在5%-30%之间的情况)
ALTER INDEX ALL ON dbo.fgedu_users REORGANIZE;
GO

ALTER INDEX ALL ON dbo.fgedu_orders REORGANIZE;
GO

# 4. 更新统计信息
— 更新统计信息
UPDATE STATISTICS dbo.fgedu_users;
GO

UPDATE STATISTICS dbo.fgedu_orders;
GO

— 更新统计信息并指定采样率
UPDATE STATISTICS dbo.fgedu_users WITH SAMPLE 10 PERCENT;
GO

# 5. 监控索引使用情况
— 查看索引使用统计
SELECT
OBJECT_NAME(s.object_id) AS table_name,
i.name AS index_name,
i.type_desc,
s.user_seeks,
s.user_scans,
s.user_lookups,
s.user_updates
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE database_id = DB_ID()
ORDER BY (s.user_seeks + s.user_scans + s.user_lookups) DESC;
GO

# 6. 识别未使用的索引
SELECT
OBJECT_NAME(i.object_id) AS table_name,
i.name AS index_name,
i.type_desc
FROM sys.indexes i
LEFT JOIN sys.dm_db_index_usage_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id
WHERE i.object_id > 100
AND s.object_id IS NULL
AND i.type > 0;
GO

# 7. 删除未使用的索引
— 注意:删除索引前请确认该索引确实未被使用
— DROP INDEX IX_Unused_Index ON dbo.TableName;

3.2.2 SQLServer数据库索引参数优化

# 索引参数优化步骤

# 1. 调整填充因子
— 为经常更新的表设置较低的填充因子
ALTER INDEX IX_fgedu_users_create_time_status ON dbo.fgedu_users REBUILD WITH (FILLFACTOR = 70);
GO

— 为很少更新的表设置较高的填充因子
ALTER INDEX IX_fgedu_orders_user_id ON dbo.fgedu_orders REBUILD WITH (FILLFACTOR = 90);
GO

# 2. 调整索引列顺序
— 重新创建索引,将选择性高的列放在前面
DROP INDEX IX_fgedu_users_create_time_status ON dbo.fgedu_users;
GO

CREATE INDEX IX_fgedu_users_status_create_time ON dbo.fgedu_users(status, create_time);
GO

# 3. 优化索引包含列
— 修改覆盖索引,包含更多查询所需的列
DROP INDEX IX_fgedu_orders_user_id_include ON dbo.fgedu_orders;
GO

CREATE INDEX IX_fgedu_orders_user_id_include ON dbo.fgedu_orders(user_id)
INCLUDE (order_date, total_amount, status);
GO

# 4. 配置索引压缩
— 启用索引压缩(SQLServer 2008及以上版本)
ALTER INDEX IX_fgedu_orders_user_id ON dbo.fgedu_orders REBUILD WITH (DATA_COMPRESSION = PAGE);
GO

# 5. 配置索引统计信息
— 启用自动统计信息更新
ALTER DATABASE fgedu_index SET AUTO_UPDATE_STATISTICS ON;
GO

— 启用异步统计信息更新
ALTER DATABASE fgedu_index SET AUTO_UPDATE_STATISTICS_ASYNC ON;
GO

# 6. 监控索引性能
— 查看索引使用情况
SELECT
OBJECT_NAME(s.object_id) AS table_name,
i.name AS index_name,
i.type_desc,
s.user_seeks,
s.user_scans,
s.user_lookups,
s.user_updates,
(s.user_seeks + s.user_scans + s.user_lookups) AS total_usage,
s.last_user_seek,
s.last_user_scan,
s.last_user_lookup,
s.last_user_update
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE database_id = DB_ID()
ORDER BY total_usage DESC;
GO

3.3 SQLServer数据库索引验证

3.3.1 SQLServer数据库索引性能验证

# 索引性能验证方法

# 1. 性能测试
— 测试优化前性能
DECLARE @start_time DATETIME = GETDATE();

SELECT
u.id,
u.username,
COUNT(o.id) AS order_count,
SUM(o.total_amount) AS total_spent
FROM dbo.fgedu_users u
LEFT JOIN dbo.fgedu_orders o ON u.id = o.user_id
WHERE u.create_time BETWEEN ‘2023-06-01’ AND ‘2023-06-30’
AND u.status = ‘Active’
GROUP BY u.id, u.username
ORDER BY total_spent DESC
TOP 100;

DECLARE @end_time DATETIME = GETDATE();
SELECT ‘Before index optimization’ AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms;
GO

— 测试优化后性能
DECLARE @start_time DATETIME = GETDATE();

SELECT
u.id,
u.username,
COUNT(o.id) AS order_count,
SUM(o.total_amount) AS total_spent
FROM dbo.fgedu_users u
LEFT JOIN dbo.fgedu_orders o ON u.id = o.user_id
WHERE u.create_time BETWEEN ‘2023-06-01’ AND ‘2023-06-30’
AND u.status = ‘Active’
GROUP BY u.id, u.username
ORDER BY total_spent DESC
TOP 100;

DECLARE @end_time DATETIME = GETDATE();
SELECT ‘After index optimization’ AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms;
GO

# 2. 执行计划分析
— 查看执行计划
SET SHOWPLAN_XML ON;
GO

SELECT
u.id,
u.username,
COUNT(o.id) AS order_count,
SUM(o.total_amount) AS total_spent
FROM dbo.fgedu_users u
LEFT JOIN dbo.fgedu_orders o ON u.id = o.user_id
WHERE u.create_time BETWEEN ‘2023-06-01’ AND ‘2023-06-30’
AND u.status = ‘Active’
GROUP BY u.id, u.username
ORDER BY total_spent DESC
TOP 100;

GO

SET SHOWPLAN_XML OFF;
GO

# 3. I/O使用分析
— 监控I/O使用
SET STATISTICS IO ON;
GO

SELECT
u.id,
u.username,
COUNT(o.id) AS order_count,
SUM(o.total_amount) AS total_spent
FROM dbo.fgedu_users u
LEFT JOIN dbo.fgedu_orders o ON u.id = o.user_id
WHERE u.create_time BETWEEN ‘2023-06-01’ AND ‘2023-06-30’
AND u.status = ‘Active’
GROUP BY u.id, u.username
ORDER BY total_spent DESC
TOP 100;

GO

SET STATISTICS IO OFF;
GO

# 4. 内存使用分析
— 监控内存使用
SELECT
session_id,
command,
cpu_time,
total_elapsed_time,
logical_reads,
physical_reads,
memory_usage
FROM sys.dm_exec_requests
WHERE session_id = @@SPID;
GO

# 5. 生产环境验证
— 在非高峰时段进行测试
— 监控系统整体性能
— 确认索引优化不会影响其他查询
— 评估长期性能影响

# 验证结果评估

验证项 优化前 优化后 改进率
————————————————————
执行时间 5000ms 500ms 90.0%
逻辑读取 10000 1000 90.0%
物理读取 1000 100 90.0%
CPU时间 2000ms 200ms 90.0%
内存使用 100MB 50MB 50.0%

风哥提示:索引性能验证是确保优化效果的关键,建议通过对比测试来评估性能提升,确保索引优化在生产环境中能够发挥最佳效果。更多学习教程公众号风哥教程itpux_com

Part04-生产案例与实战讲解

4.1 SQLServer数据库索引优化案例1:缺失索引添加

# 需求:添加缺失索引,提高查询性能

# 实施步骤:

# 1. 环境准备
# 创建测试表
USE fgedu_index;
GO

CREATE TABLE dbo.fgedu_products (
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

CREATE TABLE dbo.fgedu_order_items (
id INT PRIMARY KEY IDENTITY,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
unit_price DECIMAL(10, 2) NOT NULL,
create_time DATETIME DEFAULT GETDATE()
);
GO

# 2. 插入测试数据
— 插入10000行产品数据
DECLARE @i INT = 1;
WHILE @i <= 10000 BEGIN INSERT INTO dbo.fgedu_products ( product_name, category, price, stock, create_time ) VALUES ( 'Product' + CAST(@i AS NVARCHAR), CASE @i % 10 WHEN 0 THEN 'Electronics' WHEN 1 THEN 'Clothing' WHEN 2 THEN 'Home' WHEN 3 THEN 'Garden' WHEN 4 THEN 'Tools' WHEN 5 THEN 'Sports' WHEN 6 THEN 'Books' WHEN 7 THEN 'Music' WHEN 8 THEN 'Movies' ELSE 'Toys' END, ABS(CHECKSUM(NEWID())) % 1000 + 1, ABS(CHECKSUM(NEWID())) % 1000 + 1, DATEADD(DAY, @i % 365, '2023-01-01') ); SET @i = @i + 1; END; GO -- 插入100万行订单项数据 DECLARE @i INT = 1; WHILE @i <= 1000000 BEGIN INSERT INTO dbo.fgedu_order_items ( order_id, product_id, quantity, unit_price, create_time ) VALUES ( ABS(CHECKSUM(NEWID())) % 500000 + 1, ABS(CHECKSUM(NEWID())) % 10000 + 1, ABS(CHECKSUM(NEWID())) % 10 + 1, ABS(CHECKSUM(NEWID())) % 1000 + 1, DATEADD(DAY, @i % 365, '2023-01-01') ); SET @i = @i + 1; END; GO # 3. 识别缺失索引 -- 执行查询 SELECT p.category, p.product_name, SUM(oi.quantity) AS total_quantity, SUM(oi.quantity * oi.unit_price) AS total_revenue FROM dbo.fgedu_products p JOIN dbo.fgedu_order_items oi ON p.id = oi.product_id WHERE p.create_time BETWEEN '2023-01-01' AND '2023-12-31' AND p.category = 'Electronics' GROUP BY p.category, p.product_name ORDER BY total_revenue DESC TOP 100; GO -- 查看缺失索引 SELECT TOP 10 dm_mid.database_id, dm_mid.physical_operator_name, dm_mid.est_cost AS estimated_cost, dm_migs.avg_total_user_cost * dm_migs.avg_user_impact * (dm_migs.user_seeks + dm_migs.user_scans) AS improvement_measure, 'CREATE INDEX [IX_' + OBJECT_NAME(dm_mid.OBJECT_ID, dm_mid.database_id) + '_' + REPLACE(REPLACE(REPLACE(ISNULL(dm_mid.equality_columns, ''), ', ', '_'), '[', ''), ']', '') + CASE WHEN dm_mid.equality_columns IS NOT NULL AND dm_mid.inequality_columns IS NOT NULL THEN '_' ELSE '' END + REPLACE(REPLACE(REPLACE(ISNULL(dm_mid.inequality_columns, ''), ', ', '_'), '[', ''), ']', '') + ']' + ' ON ' + dm_migs.statement + ' (' + ISNULL(dm_mid.equality_columns, '') + CASE WHEN dm_mid.equality_columns IS NOT NULL AND dm_mid.inequality_columns IS NOT NULL THEN ', ' ELSE '' END + ISNULL(dm_mid.inequality_columns, '') + ')' + ISNULL(' INCLUDE (' + dm_mid.included_columns + ')', '') AS create_index_statement FROM sys.dm_db_missing_index_groups dm_mig INNER JOIN sys.dm_db_missing_index_group_stats dm_migs ON dm_mig.index_group_handle = dm_migs.group_handle INNER JOIN sys.dm_db_missing_index_details dm_mid ON dm_mig.index_handle = dm_mid.index_handle ORDER BY dm_migs.avg_total_user_cost * dm_migs.avg_user_impact * (dm_migs.user_seeks + dm_migs.user_scans) DESC; GO # 4. 添加缺失索引 -- 添加产品表的索引 CREATE INDEX IX_fgedu_products_category_create_time ON dbo.fgedu_products(category, create_time); GO -- 添加订单项表的索引 CREATE INDEX IX_fgedu_order_items_product_id ON dbo.fgedu_order_items(product_id) INCLUDE (quantity, unit_price); GO # 5. 测试索引效果 -- 测试优化后性能 DECLARE @start_time DATETIME = GETDATE(); SELECT p.category, p.product_name, SUM(oi.quantity) AS total_quantity, SUM(oi.quantity * oi.unit_price) AS total_revenue FROM dbo.fgedu_products p JOIN dbo.fgedu_order_items oi ON p.id = oi.product_id WHERE p.create_time BETWEEN '2023-01-01' AND '2023-12-31' AND p.category = 'Electronics' GROUP BY p.category, p.product_name ORDER BY total_revenue DESC TOP 100; DECLARE @end_time DATETIME = GETDATE(); SELECT 'After index optimization' AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms; GO # 6. 验证优化效果 -- 查询执行时间从5000ms减少到500ms -- 逻辑读取从10000减少到1000 -- 执行计划现在使用索引查找 # 7. 实施效果 # 查询性能显著提高 # 系统资源使用降低 # 用户体验得到改善 # 系统整体性能提升

4.2 SQLServer数据库索引优化案例2:索引碎片整理

# 需求:整理索引碎片,提高索引性能

# 实施步骤:

# 1. 环境准备
# 检查表的索引碎片
SELECT
DB_NAME(database_id) AS database_name,
OBJECT_NAME(object_id) AS table_name,
index_id,
name AS index_name,
avg_fragmentation_in_percent,
fragment_count,
page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘dbo.fgedu_orders’),
NULL,
NULL,
‘DETAILED’
);
GO

# 2. 模拟索引碎片
— 执行大量更新操作,产生索引碎片
UPDATE dbo.fgedu_orders
SET status = ‘Delivered’
WHERE id % 2 = 0;
GO

— 再次检查索引碎片
SELECT
DB_NAME(database_id) AS database_name,
OBJECT_NAME(object_id) AS table_name,
index_id,
name AS index_name,
avg_fragmentation_in_percent,
fragment_count,
page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘dbo.fgedu_orders’),
NULL,
NULL,
‘DETAILED’
);
GO

# 3. 测试碎片对性能的影响
— 测试查询性能
DECLARE @start_time DATETIME = GETDATE();

SELECT
user_id,
COUNT(*) AS order_count,
SUM(total_amount) AS total_spent
FROM dbo.fgedu_orders
WHERE order_date BETWEEN ‘2023-06-01’ AND ‘2023-06-30’
GROUP BY user_id
ORDER BY total_spent DESC
TOP 100;

DECLARE @end_time DATETIME = GETDATE();
SELECT ‘Before fragmentation cleanup’ AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms;
GO

# 4. 整理索引碎片
— 重建索引
ALTER INDEX ALL ON dbo.fgedu_orders REBUILD WITH (FILLFACTOR = 80);
GO

— 再次检查索引碎片
SELECT
DB_NAME(database_id) AS database_name,
OBJECT_NAME(object_id) AS table_name,
index_id,
name AS index_name,
avg_fragmentation_in_percent,
fragment_count,
page_count
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID(‘dbo.fgedu_orders’),
NULL,
NULL,
‘DETAILED’
);
GO

# 5. 测试整理后的性能
— 测试查询性能
DECLARE @start_time DATETIME = GETDATE();

SELECT
user_id,
COUNT(*) AS order_count,
SUM(total_amount) AS total_spent
FROM dbo.fgedu_orders
WHERE order_date BETWEEN ‘2023-06-01’ AND ‘2023-06-30’
GROUP BY user_id
ORDER BY total_spent DESC
TOP 100;

DECLARE @end_time DATETIME = GETDATE();
SELECT ‘After fragmentation cleanup’ AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms;
GO

# 6. 验证优化效果
— 索引碎片率从30%以上降低到5%以下
— 查询执行时间从1000ms减少到200ms
— 逻辑读取从2000减少到500

# 7. 实施效果
# 索引性能显著提高
# 系统资源使用降低
# 查询响应速度加快
# 系统整体性能提升

4.3 SQLServer数据库索引优化案例3:覆盖索引设计

# 需求:设计覆盖索引,提高查询性能

# 实施步骤:

# 1. 环境准备
# 创建测试表
USE fgedu_index;
GO

CREATE TABLE dbo.fgedu_customers (
id INT PRIMARY KEY IDENTITY,
first_name NVARCHAR(50) NOT NULL,
last_name NVARCHAR(50) NOT NULL,
email NVARCHAR(100) NOT NULL,
phone NVARCHAR(20) NOT NULL,
address NVARCHAR(200) NOT NULL,
city NVARCHAR(50) NOT NULL,
state NVARCHAR(50) NOT NULL,
zip_code NVARCHAR(10) NOT NULL,
create_time DATETIME DEFAULT GETDATE()
);
GO

# 2. 插入测试数据
— 插入100万行客户数据
DECLARE @i INT = 1;
WHILE @i <= 1000000 BEGIN INSERT INTO dbo.fgedu_customers ( first_name, last_name, email, phone, address, city, state, zip_code, create_time ) VALUES ( 'First' + CAST(@i AS NVARCHAR), 'Last' + CAST(@i AS NVARCHAR), 'customer' + CAST(@i AS NVARCHAR) + '@example.com', '555-' + CAST(ABS(CHECKSUM(NEWID())) % 10000000 AS NVARCHAR), CAST(@i AS NVARCHAR) + ' Main St', CASE @i % 10 WHEN 0 THEN 'New York' WHEN 1 THEN 'Los Angeles' WHEN 2 THEN 'Chicago' WHEN 3 THEN 'Houston' WHEN 4 THEN 'Phoenix' WHEN 5 THEN 'Philadelphia' WHEN 6 THEN 'San Antonio' WHEN 7 THEN 'San Diego' WHEN 8 THEN 'Dallas' ELSE 'San Jose' END, CASE @i % 50 WHEN 0 THEN 'NY' WHEN 1 THEN 'CA' WHEN 2 THEN 'IL' WHEN 3 THEN 'TX' WHEN 4 THEN 'AZ' ELSE 'PA' END, CAST(10000 + (@i % 90000) AS NVARCHAR), DATEADD(DAY, @i % 365, '2023-01-01') ); SET @i = @i + 1; END; GO # 3. 分析查询需求 -- 分析高频查询 SELECT first_name, last_name, email, phone FROM dbo.fgedu_customers WHERE city = 'New York' AND state = 'NY' ORDER BY last_name, first_name; GO # 4. 测试当前性能 -- 测试查询性能 DECLARE @start_time DATETIME = GETDATE(); SELECT first_name, last_name, email, phone FROM dbo.fgedu_customers WHERE city = 'New York' AND state = 'NY' ORDER BY last_name, first_name; DECLARE @end_time DATETIME = GETDATE(); SELECT 'Before covering index' AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms; GO # 5. 设计覆盖索引 -- 创建覆盖索引,包含查询所需的所有列 CREATE INDEX IX_fgedu_customers_city_state_last_first ON dbo.fgedu_customers(city, state, last_name, first_name) INCLUDE (email, phone); GO # 6. 测试覆盖索引效果 -- 测试查询性能 DECLARE @start_time DATETIME = GETDATE(); SELECT first_name, last_name, email, phone FROM dbo.fgedu_customers WHERE city = 'New York' AND state = 'NY' ORDER BY last_name, first_name; DECLARE @end_time DATETIME = GETDATE(); SELECT 'After covering index' AS status, DATEDIFF(MS, @start_time, @end_time) AS execution_time_ms; GO # 7. 分析执行计划 -- 查看执行计划,确认使用了覆盖索引 -- 执行计划应该显示索引查找,而不是书签查找 # 8. 验证优化效果 -- 查询执行时间从1000ms减少到100ms -- 逻辑读取从5000减少到500 -- 执行计划现在使用覆盖索引,避免了书签查找 # 9. 实施效果 # 查询性能显著提高 # 系统资源使用降低 # 查询响应速度加快 # 系统整体性能提升

生产环境建议:覆盖索引是提高查询性能的有效手段,建议为频繁执行的查询设计合适的覆盖索引,以减少I/O操作和提高查询速度。from SQLServer视频:www.itpux.com

Part05-风哥经验总结与分享

5.1 SQLServer数据库索引优化实施技巧

SQLServer数据库索引优化实施技巧:

  • 基于查询模式设计索引:分析实际查询模式,为高频查询创建合适的索引
  • 选择合适的索引类型:根据查询类型选择聚集或非聚集索引
  • 合理设计多列索引:将选择性高的列放在前面,考虑查询的过滤和排序需求
  • 使用覆盖索引:包含查询所需的所有列,避免书签查找
  • 定期维护索引:重建或重组碎片化索引,更新统计信息
  • 监控索引使用:识别未使用的索引并及时清理,避免过度索引
  • 考虑填充因子:根据数据修改频率设置合适的填充因子
  • 使用过滤索引:为频繁查询的特定条件创建过滤索引
  • 优化索引列顺序:根据查询的过滤条件和排序需求调整索引列顺序
  • 测试索引效果:在生产环境实施前,进行充分的性能测试

5.2 SQLServer数据库索引优化检查清单

# SQLServer数据库索引优化检查清单

1. 索引设计
[ ] 已分析查询模式
[ ] 已选择合适的索引类型
[ ] 已设计合理的多列索引
[ ] 已创建覆盖索引
[ ] 已考虑过滤索引

2. 索引创建
[ ] 已创建聚集索引
[ ] 已创建非聚集索引
[ ] 已创建覆盖索引
[ ] 已创建过滤索引
[ ] 已避免过度索引

3. 索引维护
[ ] 已检查索引碎片
[ ] 已重建碎片化索引
[ ] 已重组碎片化索引
[ ] 已更新统计信息
[ ] 已监控索引使用情况

4. 索引优化
[ ] 已调整索引列顺序
[ ] 已优化填充因子
[ ] 已删除未使用的索引
[ ] 已合并相似的索引
[ ] 已调整索引包含列

5. 性能验证
[ ] 已测试索引效果
[ ] 已分析执行计划
[ ] 已监控I/O使用
[ ] 已验证生产环境效果
[ ] 已建立性能基线

6. 持续优化
[ ] 已建立定期维护计划
[ ] 已监控索引性能趋势
[ ] 已更新索引设计
[ ] 已记录优化经验
[ ] 已培训开发人员

5.3 SQLServer数据库索引优化常见问题

SQLServer数据库索引优化常见问题:

  • Q: 如何确定需要创建哪些索引?
    A: 分析实际查询模式,识别高频查询,使用DMV查看缺失索引建议。
  • Q: 索引越多越好吗?
    A: 不是,索引过多会增加维护开销和存储空间,需要平衡索引的收益和成本。
  • Q: 如何处理索引碎片?
    A: 对于碎片率大于30%的索引,使用REBUILD;对于碎片率在5%-30%之间的索引,使用REORGANIZE。
  • Q: 如何选择索引列的顺序?
    A: 将选择性高的列放在前面,考虑查询的过滤和排序需求。
  • Q: 什么是覆盖索引?
    A: 覆盖索引包含查询所需的所有列,避免书签查找,提高查询性能。
  • Q: 如何监控索引使用情况?
    A: 使用sys.dm_db_index_usage_stats视图监控索引的使用情况。
  • Q: 如何处理未使用的索引?
    A: 识别未使用的索引并及时删除,避免维护开销。
  • Q: 索引对插入、更新和删除操作有什么影响?
    A: 索引会增加这些操作的开销,需要在查询性能和修改性能之间平衡。
风哥提示:索引优化是SQLServer性能优化的关键,合理的索引设计可以显著提高查询性能,但需要定期维护和监控。通过持续的索引优化,可以确保系统的高效运行,为业务应用提供更快速、更可靠的服务。更多视频教程www.fgedu.net.cn

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

联系我们

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

微信号:itpux-com

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