最近阅读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,下一次使用索引时就会触发更新统计信息。
有了上面的数据后,我们可以根据已修改行数所占比例决定是否更新统计信息。很有可能一些统计信息需要每天更新,因为数据变化很大,而其他统计信息只需要每周更新一次或每月更新一次,因为数据变化不大。
无论你使用哪种方式手动更新统计信息,确保通过代理作业定期执行,同时确保已启用[自动更新统计信息]选项。谁也不能保证作业始终正常执行,同时也无法保证作业异常时能及时通知你。