最近阅读SQLskills SQL101,将Erin Stellato部分稍作整理。仅提取自己感兴趣的知识点,详细内容请阅读原文。
一、Trace Flags
推荐开启三个跟踪标记1118、3023、3226
跟踪标记1118(适用2016之前版本),避开对SGAM页的使用,在统一区分配新建对象的空间。
跟踪标记3023(适用2014之前版本),在默认情况下对实例上进行的所有备份启用CHECKSUM选项。
跟踪标记3226,成功备份信息不再写入SQL Server ERRORLOG
启动参数添加:-T3226
DBCC TRACEON(3226,-1) --第二个参数-1表示实例级别,否则会话级别
二、The SQL Server ERRORLOG
1、设定作业定期recycle错误日志(weekly)
EXEC master..sp_cycle_errorlog
EXEC msdb..sp_cycle_agent_errorlog
2、配置SQL Server 错误日志文件在回收之前的数目(30)
SSMS->实例->管理->SQL Server 日志->配置
三、Updating SQL Server Statistics Part I – Automatic Updates
为了自动更新统计信息,必须为数据库启用[自动更新统计信息]数据库选项
可以使用以下语句检查[自动更新统计信息]选项是否启用
SELECT
[name] [DatabaseName],
CASE
WHEN [is_auto_update_stats_on] = 1 THEN 'Enabled'
ELSE 'Disabled'
END [AutoUpdateStats]
FROM [sys].[databases]
ORDER BY [name];
GO
使用以下语句启用[自动更新统计信息]选项
USE [master]
GO
ALTER DATABASE [<database_name>] SET AUTO_UPDATE_STATISTICS ON WITH NO_WAIT
GO
启用选项后,SQL Server根据内部阈值将统计信息标记为过期:
对于SQL Server 2014及之前版本,阈值是500+(20%×表格数据总量)。当然也有例外(数据量小于500行的表格,表变量上不能建统计信息)
对于SQL Server 2008 R2 SP1及之后版本,可以使用跟踪标记2371降低这个阈值
对于SQL Server 2016,如果数据库的兼容级别为130,默认使用跟踪标志2371引入的阈值;如果数据库的兼容级别<=120,则需使用跟踪标记2371来降低阈值
如果统计信息被标记为过时,那么在下一次使用的时候,SQL Server将自动更新它们。注意,它们并不是在过期的时刻就更新,它们在需要的时候才会更新。
四、Updating SQL Server Statistics Part II – Scheduled Updates
4.1、Update Statistics Task
维护计划中的"更新统计信息"任务,你可以配置所有数据库或者特定数据库,你还可以确定更新选项(所有、列统计、索引统计),扫描类型(完全、按行数抽样、按百分比抽样)
/* Update Statistics Task 对应的脚本(截取部分) */
--所有现有统计信息,完全扫描
USE [AdventureWorks2008R2]
GO
UPDATE STATISTICS [dbo].[ErrorLog]
WITH FULLSCAN
GO
--仅限列统计信息,按行数抽样
USE [AdventureWorks2008R2]
GO
UPDATE STATISTICS [dbo].[ErrorLog]
WITH SAMPLE 50 ROWS,COLUMNS
GO
--仅限索引统计信息,按百分比抽样
USE [AdventureWorks2008R2]
GO
UPDATE STATISTICS [dbo].[ErrorLog]
WITH SAMPLE 50 PERCENT,INDEX
GO
经常有人在Rebuild Index Task之后添加Update Statistics Task。如果是在SQL Server 2014及之前的版本,你只需要更新列统计信息即可,因为Rebuild Index Task会Rebuild所有的索引,并且使用完全扫描更新统计信息。在SQL Server 2016,Rebuild Index Task提供更多选项,你可以配置当索引碎片达到指定数值时才Rebuild索引。这样部分索引Rebuild(统计信息更新),部分索引未Rebuild(统计信息没更新)。Update Statistics Task应该如何配置?这种情况你可能选择更新所有现有统计信息,部分统计信息会再次更新——真是浪费!
4.2、sp_updatestats
sp_updatestats命令用于数据库级别,它不能指定统计或者索引或者表。只要数据发生变化,sp_updatestats命令就会更新统计信息
/* sp_updatestats 测试 */
USE AdventureWorks2008R2
GO
--执行命令
exec sp_updatestats
......
正在更新 [Sales].[SalesOrderDetail]
[PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID],不需要更新...
[AK_SalesOrderDetail_rowguid],不需要更新...
[IX_SalesOrderDetail_ProductID],不需要更新...
[_WA_Sys_00000006_3587F3E0],不需要更新...
[_WA_Sys_00000002_3587F3E0],不需要更新...
[_WA_Sys_00000004_3587F3E0],不需要更新...
[_WA_Sys_00000007_3587F3E0],不需要更新...
[_WA_Sys_0000000B_3587F3E0],不需要更新...
已更新 0 条索引/统计信息,8 不需要更新。
......
--查看统计更新时间
DBCC SHOW_STATISTICS ('Sales.SalesOrderDetail',IX_SalesOrderDetail_ProductID)
Name Updated
IX_SalesOrderDetail_ProductID 08 16 2017 4:30PM --更新一行数据
UPDATE Sales.SalesOrderDetail
SET ProductID = ProductID
WHERE SalesOrderDetailID=1 --执行命令
exec sp_updatestats
......
正在更新 [Sales].[SalesOrderDetail]
[PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID],不需要更新...
[AK_SalesOrderDetail_rowguid],不需要更新...
[IX_SalesOrderDetail_ProductID] 已更新...
[_WA_Sys_00000006_3587F3E0],不需要更新...
[_WA_Sys_00000002_3587F3E0],不需要更新...
[_WA_Sys_00000004_3587F3E0],不需要更新...
[_WA_Sys_00000007_3587F3E0],不需要更新...
[_WA_Sys_0000000B_3587F3E0],不需要更新...
已更新 1 条索引/统计信息,7 不需要更新。
......
--查看统计更新时间
DBCC SHOW_STATISTICS ('Sales.SalesOrderDetail',IX_SalesOrderDetail_ProductID)
Name Updated
IX_SalesOrderDetail_ProductID 08 16 2017 4:32PM
可以看到哪怕只是更新一行,统计信息也会更新!显然这种更新统计没太大意义。
4.3、UPDATE STATISTICS
UPDATE STATISTICS命令可以针对单个统计信息或者单个表(更新表上的所有统计信息)。推荐使用此命令编写更新统计信息语句,我喜欢根据已经更改的数据量更新过时的统计信息。
我们通过使用sys.dm_db_stats_properties来确定是否更新统计信息。这个动态管理函数跟踪修改,并且告诉我们在上一次更新统计信息时表中有多少行,以及统计更新的时间,以及上一次更新统计信息后累计变更的行数。例如,我更新Sales.SalesOrderDetail表中的部分行,然后查看此DMF输出,你可以看到modification_counter和我修改的行数一致
/* UPDATE STATISTICS 测试 */
USE [AdventureWorks2008R2];
GO
--更新部分行,9831行受影响
UPDATE [Sales].[SalesOrderDetail]
SET [ProductID] = [ProductID]
WHERE [ProductID] IN (921,873,712);
GO
--从sys.dm_db_stats_properties中查看上次统计更新信息
SELECT [so].[name] [TableName],
[ss].[name] [StatisticName],
[ss].[stats_id] [StatisticID],
[sp].[last_updated] [LastUpdated],
[sp].[rows] [RowsInTableWhenUpdated],
[sp].[rows_sampled] [RowsSampled],
[sp].[modification_counter] [NumberOfModifications]
FROM [sys].[stats] [ss]
JOIN [sys].[objects] [so]
ON [ss].[object_id] = [so].[object_id]
CROSS APPLY [sys].[dm_db_stats_properties] ([so].[object_id], [ss].stats_id) [sp]
WHERE [so].[name] = N'SalesOrderDetail';
GO
连续执行三次更新,受影响行数累计达到9831*3=29493>24763=121317*20%+500,[自动更新统计信息]选项为True,下一次使用索引时就会触发更新统计信息。
有了上面的数据后,我们可以根据已修改行数所占比例决定是否更新统计信息。很有可能一些统计信息需要每天更新,因为数据变化很大,而其他统计信息只需要每周更新一次或每月更新一次,因为数据变化不大。
无论你使用哪种方式手动更新统计信息,确保通过代理作业定期执行,同时确保已启用[自动更新统计信息]选项。谁也不能保证作业始终正常执行,同时也无法保证作业异常时能及时通知你。