Khi tôi ở Chicago vài tuần trước cho một trong những Sự kiện hòa nhập của chúng tôi, một người tham dự đã có một câu hỏi về số liệu thống kê. Tôi sẽ không đi sâu vào tất cả các chi tiết xung quanh vấn đề này, nhưng người tham dự đã đề cập rằng số liệu thống kê đã được cập nhật bằng cách sử dụng sp_updatestats
. Đây là một phương pháp để cập nhật số liệu thống kê mà tôi chưa bao giờ đề xuất; Tôi luôn đề xuất kết hợp các bản xây dựng lại chỉ mục và UPDATE STATISTICS
để giữ cho số liệu thống kê được cập nhật. Nếu bạn không quen với sp_updatestats
, đó là một lệnh được chạy cho toàn bộ cơ sở dữ liệu để cập nhật số liệu thống kê. Nhưng như Kimberly đã chỉ ra cho người tham dự, sp_updatestats
sẽ cập nhật thống kê miễn là nó đã được sửa đổi một hàng. Ái chà. Tôi ngay lập tức mở Sách trực tuyến và cho sp_updatestats
bạn sẽ thấy cái này:
Bây giờ, tôi thừa nhận, tôi đã đưa ra một giả định về những gì “… yêu cầu cập nhật dựa trên thông tin rowmodctr trong chế độ xem danh mục sys.sysindexes…” nghĩa là gì. Tôi đã giả định rằng quyết định cập nhật sẽ tuân theo cùng một logic mà tùy chọn Thống kê cập nhật tự động tuân theo, đó là:
- Kích thước bảng đã tăng từ 0 thành> 0 hàng (thử nghiệm 1).
- Số hàng trong bảng khi thống kê được thu thập là 500 hoặc ít hơn và cột điều chỉnh của cột đầu tiên của đối tượng thống kê đã thay đổi hơn 500 kể từ đó (thử nghiệm 2).
- Bảng có hơn 500 hàng khi thu thập thống kê và cột của cột đầu tiên của đối tượng thống kê đã thay đổi hơn 500 + 20% số hàng trong bảng khi thống kê được thu thập ( thử nghiệm 3).
Logic này không được tuân theo cho sp_updatestats
. Trên thực tế, logic vô cùng đơn giản đến mức đáng sợ:Nếu một hàng được sửa đổi, thống kê sẽ được cập nhật. Một hàng. MỘT HÀNG. Mối quan tâm của tôi là gì? Tôi lo lắng về chi phí cập nhật thống kê cho một loạt thống kê không thực sự cần được cập nhật. Hãy xem xét kỹ hơn sp_updatestats
.
Chúng tôi sẽ bắt đầu với một bản sao mới của cơ sở dữ liệu AdventureWorks2012 mà bạn có thể tải xuống từ Codeplex. Đầu tiên tôi sẽ cập nhật các hàng trong ba bảng khác nhau:
USE [AdventureWorks2012]; GO SET NOCOUNT ON; GO UPDATE [Production].[Product] SET [Name] = 'Bike Chain' WHERE [ProductID] = 952; UPDATE [Person].[Person] SET [LastName] = 'Cameron' WHERE [LastName] = 'Diaz'; GO INSERT INTO Sales.SalesReason (Name, ReasonType, ModifiedDate) VALUES('Stats', 'Test', GETDATE()); GO 10000
Chúng tôi đã sửa đổi một hàng trong Production.Product
, 211 hàng trong Person.Person
và chúng tôi đã thêm 10.000 hàng vào Sales.SalesReason
. Nếu sp_updatestats
quy trình tuân theo cùng một logic đối với các bản cập nhật như tùy chọn Thống kê cập nhật tự động, sau đó chỉ Sales.SalesReason
sẽ cập nhật vì nó có 10 hàng để bắt đầu (trong khi 211 hàng được cập nhật trong Person.Person
đại diện cho khoảng một phần trăm của bảng). Tuy nhiên, nếu chúng ta tìm hiểu kỹ về sp_updatestats
, chúng ta có thể thấy rằng logic được sử dụng là khác nhau. Lưu ý rằng tôi chỉ trích xuất các câu lệnh từ bên trong sp_updatestats
được sử dụng để xác định những thống kê nào được cập nhật.
Một con trỏ lặp qua tất cả các bảng do người dùng xác định và các bảng nội bộ trong cơ sở dữ liệu:
declare ms_crs_tnames cursor local fast_forward read_only for select name, object_id, schema_id, type from sys.objects o where o.type = 'U' or o.type = 'IT' open ms_crs_tnames fetch next from ms_crs_tnames into @table_name, @table_id, @sch_id, @table_type
Một con trỏ khác lặp lại các thống kê cho mỗi bảng và loại trừ các đống cũng như các chỉ mục và thống kê giả định. Lưu ý rằng sys.sysindexes
được sử dụng trong sp_helpstats
. Sysindexes
là bảng hệ thống SQL Server 2000 và được lên lịch xóa trong phiên bản SQL Server trong tương lai. Điều này thật thú vị, vì phương pháp khác để xác định hàng được cập nhật là sys.dm_db_stats_properties
DMF, chỉ khả dụng trong SQL 2008 R2 SP2 và SQL 2012 SP1.
set @index_names = cursor local fast_forward read_only for select name, indid, rowmodctr from sys.sysindexes where id = @table_id and indid > 0 and indexproperty(id, name, 'ishypothetical') = 0 order by indid
Sau một chút chuẩn bị và logic bổ sung, chúng ta đi đến IF
câu lệnh tiết lộ rằng sp_updatestats
lọc ra các thống kê chưa có bất kỳ hàng nào được cập nhật… xác nhận rằng ngay cả khi chỉ một hàng được sửa đổi, thống kê sẽ được cập nhật. Ngoài ra còn có một séc cho @is_ver_current
, được xác định bởi một chức năng nội bộ được tích hợp sẵn.
if ((@ind_rowmodctr <> 0) or ((@is_ver_current is not null) and (@is_ver_current = 0)))
Một vài lần kiểm tra khác liên quan đến lấy mẫu và mức độ tương thích, sau đó là UPDATE
câu lệnh thực thi cho thống kê. Trước khi thực sự chạy sp_updatestats, chúng ta có thể truy vấn sys.sysindexes
để xem những thống kê nào sẽ cập nhật:
SELECT [o].[name], [si].[indid], [si].[name], [si].[rowmodctr], [si].[rowcnt], [o].[type] FROM [sys].[objects] [o] JOIN [sys].[sysindexes] [si] ON [o].[object_id] = [si].[id] WHERE ([o].[type] = 'U' OR [o].[type] = 'IT') AND [si].[indid] > 0 AND [si].[rowmodctr] <> 0 ORDER BY [o].[type] DESC, [o].[name];
Ngoài ba bảng mà chúng tôi đã sửa đổi, có một thống kê khác cho bảng người dùng (dbo.DatabaseLog
) và ba thống kê nội bộ sẽ được cập nhật:
Thống kê sẽ được cập nhật
Nếu chúng tôi chạy sp_updatestats
đối với cơ sở dữ liệu AdventureWorks, đầu ra liệt kê mọi bảng và (các) thống kê được cập nhật. Kết quả bên dưới được sửa đổi để chỉ hiển thị thống kê cập nhật:
Đang cập nhật [sys]. [Fulltext_avdl_1589580701]
[clust] đã được cập nhật…
1 chỉ mục / (các) thống kê đã được cập nhật, 0 không yêu cầu cập nhật.
…
Đang cập nhật [dbo]. [DatabaseLog]
[PK_DatabaseLog_DatabaseLogID] đã được cập nhật…
1 (các) chỉ mục / thống kê đã được cập nhật, 0 không yêu cầu cập nhật.
…
Đang cập nhật [sys]. [Fulltext_avdl_1077578877]
[clust] đã được cập nhật…
1 chỉ mục / (các) thống kê đã được cập nhật, 0 không yêu cầu cập nhật.
…
Đang cập nhật [Person]. [Person]
[PK_Person_BusinessEntityID], không cần cập nhật…
[IX_Person_LastName_FirstName_MiddleName] đã được cập nhật…
[AK_Person_rowguid], không cần cập nhật…
1 (các) chỉ mục / (các) thống kê đã được cập nhật, 2 không yêu cầu cập nhật.
…
Đang cập nhật [Bán hàng]. [Bán hàng]
[PK_SalesReason_SalesReasonID] đã được cập nhật…
1 (các) chỉ mục / thống kê đã được cập nhật, 0 không yêu cầu cập nhật.
…
Đang cập nhật [Sản xuất]. [Sản phẩm]
[PK_Product_ProductID], không cần cập nhật…
[AK_Product_ProductNumber], không cần cập nhật…
[AK_Product_Name] đã được cập nhật…
[ AK_Product_rowguid], không cần cập nhật…
[_WA_Sys_00000013_75A278F5], không cần cập nhật…
[_WA_Sys_00000014_75A278F5], không cần cập nhật…
[_WA_Sys_0000000D_75A278F5], cập nhật là không cần thiết…
[_ WA_Sys_0000000C_75A278F5], không cần cập nhật…
1 (các) chỉ mục / thống kê đã được cập nhật, 7 không yêu cầu cập nhật.
…
Thống kê cho tất cả các bảng đã được cập nhật.
Dòng cuối cùng của kết quả có một chút sai lệch - thống kê cho tất cả các bảng chưa được cập nhật, chỉ những thống kê có một hàng hoặc nhiều hàng đã được sửa đổi mới được cập nhật. Và một lần nữa, hạn chế của điều đó là có thể các tài nguyên đã được sử dụng mà không cần thiết. Nếu một thống kê chỉ có một hàng được sửa đổi, thì nó có nên được cập nhật không? Không. Nếu nó có 10.000 hàng được cập nhật, nó có nên được cập nhật không? Vâng, điều đó phụ thuộc. Nếu bảng chỉ có 5.000 hàng, thì tuyệt đối; nếu bảng có 1 triệu hàng thì không, vì chỉ một phần trăm của bảng đã được sửa đổi.
Lợi ích ở đây là nếu bạn đang sử dụng sp_updatestats
để cập nhật số liệu thống kê của mình, rất có thể bạn đang lãng phí tài nguyên, bao gồm CPU, I / O và tempdb. Hơn nữa, cần có thời gian để cập nhật từng thống kê và nếu bạn có thời gian bảo trì chặt chẽ, bạn có thể có các nhiệm vụ bảo trì khác có thể thực hiện trong thời gian đó, thay vì cập nhật không cần thiết. Cuối cùng, bạn có thể không cung cấp bất kỳ lợi ích hiệu suất nào bằng cách cập nhật thống kê khi quá ít hàng đã thay đổi. Thay đổi phân phối có thể không đáng kể nếu chỉ một phần trăm nhỏ hàng được sửa đổi, do đó, các giá trị biểu đồ và mật độ sẽ không thay đổi nhiều như vậy. Ngoài ra, hãy nhớ rằng việc cập nhật số liệu thống kê sẽ làm mất hiệu lực của các kế hoạch truy vấn sử dụng các số liệu thống kê đó. Khi những truy vấn đó thực thi, các kế hoạch sẽ được tạo lại và kế hoạch có thể sẽ giống hệt như trước đó, vì không có thay đổi đáng kể nào trong biểu đồ. Có một khoản chi phí để biên soạn lại các kế hoạch truy vấn - không phải lúc nào cũng dễ dàng đo lường được, nhưng bạn không nên bỏ qua chi phí này.
Một phương pháp tốt hơn để quản lý thống kê - bởi vì bạn cần quản lý thống kê - là triển khai công việc đã lên lịch cập nhật dựa trên tỷ lệ phần trăm của các hàng đã được sửa đổi. Bạn có thể sử dụng truy vấn nói trên để thẩm vấn sys.sysindexes
hoặc bạn có thể sử dụng truy vấn bên dưới để tận dụng DMF mới được thêm vào SQL Server 2008 R2 SP2 và SQL Server 2012 SP1:
SELECT [sch].[name] + '.' + [so].[name] AS [TableName] , [ss].[name] AS [Statistic], [sp].[last_updated] AS [StatsLastUpdated] , [sp].[rows] AS [RowsInTable] , [sp].[rows_sampled] AS [RowsSampled] , [sp].[modification_counter] AS [RowModifications] FROM [sys].[stats] [ss] JOIN [sys].[objects] [so] ON [ss].[object_id] = [so].[object_id] JOIN [sys].[schemas] [sch] ON [so].[schema_id] = [sch].[schema_id] OUTER APPLY [sys].[dm_db_stats_properties]([so].[object_id], [ss].[stats_id]) sp WHERE [so].[type] = 'U' AND [sp].[modification_counter] > 0 ORDER BY [sp].[last_updated] DESC;
Nhận ra rằng các bảng khác nhau có thể có các ngưỡng khác nhau và bạn sẽ cần điều chỉnh truy vấn ở trên cho cơ sở dữ liệu của mình. Đối với một số bảng, đợi cho đến khi 15% hoặc 20% số hàng đã được sửa đổi có thể được. Nhưng đối với những người khác, bạn có thể cần cập nhật ở mức 10% hoặc thậm chí 5%, tùy thuộc vào giá trị thực tế và độ lệch của chúng. Không có viên đạn bạc. Dù chúng ta yêu thích sự tuyệt đối nhưng chúng hiếm khi tồn tại trong SQL Server và số liệu thống kê cũng không ngoại lệ. Bạn vẫn muốn bật Thống kê tự động cập nhật - đó là một biện pháp an toàn sẽ có hiệu lực nếu bạn bỏ lỡ điều gì đó, giống như Tự động phát triển cho các tệp cơ sở dữ liệu của bạn. Nhưng cách tốt nhất của bạn là biết dữ liệu của mình và triển khai phương pháp cho phép bạn cập nhật thống kê dựa trên phần trăm số hàng đã thay đổi.