Tôi đã thấy vấn đề mật độ tương tự này trên một số chỉ mục không bao gồm trên các cơ sở dữ liệu lớn nhất mà tôi có quyền truy cập. Trước tiên tôi sẽ bắt đầu với một vài quan sát mà tôi đã thực hiện về biểu đồ và tính toán mật độ:
- SQL Server có thể sử dụng khóa chính trên bảng để suy luận điều gì đó về mật độ của cả hai cột. Điều này có nghĩa là mật độ bao gồm các cột PK thường sẽ rất chính xác.
- Tính toán mật độ cho cột đầu tiên trong thống kê phù hợp với biểu đồ. Nếu biểu đồ không mô hình hóa dữ liệu tốt thì mật độ có thể bị tắt.
- Để tạo biểu đồ,
StatMan
hàm sẽ suy luận về dữ liệu bị thiếu. Hành vi có thể thay đổi tùy thuộc vào loại dữ liệu của cột.
Đối với một cách để xem xét vấn đề, giả sử rằng bạn lấy mẫu 100 hàng từ bảng 10000 hàng và bạn nhận được 100 giá trị riêng biệt. Người ta đoán những gì còn lại của dữ liệu trong bảng là có 10000 giá trị duy nhất. Một dự đoán khác là có 100 giá trị riêng biệt nhưng mỗi giá trị được lặp lại 100 lần. Dự đoán thứ hai có vẻ không hợp lý với bạn, mà tôi sẽ đồng ý. Tuy nhiên, làm thế nào để bạn cân bằng hai cách tiếp cận khi dữ liệu được lấy mẫu trở lại phân phối không đều? Có một số thuật toán được Microsoft phát triển cho StatMan
hàm này. Các thuật toán có thể không hoạt động cho tất cả các gián đoạn dữ liệu và tất cả các cấp mẫu.
Chúng ta hãy đi qua một ví dụ tương đối đơn giản. Tôi sẽ sử dụng VARCHAR
các cột như trong bảng của bạn để xem một số hành vi tương tự. Tuy nhiên, tôi sẽ chỉ thêm một giá trị sai lệch vào bảng. Tôi đang thử nghiệm với SQL Server 2016 SP1. Bắt đầu với 100k hàng với 100k giá trị duy nhất cho FK
cột:
DROP TABLE IF EXISTS X_STATS_SMALL;
CREATE TABLE X_STATS_SMALL (
ID VARCHAR(10) NOT NULL,
FK VARCHAR(10) NOT NULL,
PADDING VARCHAR(900) NOT NULL,
PRIMARY KEY (ID)
);
-- insert 100k rows
INSERT INTO X_STATS_SMALL WITH (TABLOCK)
SELECT N, N, REPLICATE('Z', 900)
FROM dbo.GetNums(100000);
CREATE INDEX IX_X_STATS_SMALL ON X_STATS_SMALL (FK);
-- get sampled stats
UPDATE STATISTICS X_STATS_SMALL IX_X_STATS_SMALL;
Dưới đây là một số mẫu từ số liệu thống kê:
╔═════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠═════════════╬════════════════╬═════════╣
║ 1.00001E-05 ║ 4.888205 ║ FK ║
║ 1.00001E-05 ║ 9.77641 ║ FK, ID ║
╚═════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦═════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬═════════╬═════════════════════╬════════════════╣
║ 1005 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 10648 ║ 665.0898 ║ 1 ║ 664 ║ 1.002173 ║
║ 10968 ║ 431.6008 ║ 1 ║ 432 ║ 1 ║
║ 11182 ║ 290.0924 ║ 1 ║ 290 ║ 1 ║
║ 1207 ║ 445.7517 ║ 1 ║ 446 ║ 1 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 99989 ║ 318.3941 ║ 1 ║ 318 ║ 1 ║
╚══════════════╩════════════╩═════════╩═════════════════════╩════════════════╝
Đối với dữ liệu được phân phối đồng đều với một giá trị duy nhất trên mỗi hàng, chúng tôi có được mật độ chính xác, ngay cả với VARCHAR
cột biểu đồ và kích thước mẫu là 14294 hàng.
Bây giờ, hãy thêm một giá trị sai lệch và cập nhật lại số liệu thống kê:
-- add 70k rows with a FK value of '35000'
INSERT INTO X_STATS_SMALL WITH (TABLOCK)
SELECT N + 100000 , '35000', REPLICATE('Z', 900)
FROM dbo.GetNums(70000);
UPDATE STATISTICS X_STATS_SMALL IX_X_STATS_SMALL;
Với kích thước mẫu là 17010 hàng, mật độ của cột đầu tiên nhỏ hơn:
╔══════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠══════════════╬════════════════╬═════════╣
║ 6.811061E-05 ║ 4.935802 ║ FK ║
║ 5.882353E-06 ║ 10.28007 ║ FK, ID ║
╚══════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦══════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬══════════╬═════════════════════╬════════════════╣
║ 10039 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 10978 ║ 956.9945 ║ 1 ║ 138 ║ 6.954391 ║
║ 11472 ║ 621.0283 ║ 1 ║ 89 ║ 6.941863 ║
║ 1179 ║ 315.6046 ║ 1 ║ 46 ║ 6.907561 ║
║ 11909 ║ 91.62713 ║ 1 ║ 14 ║ 6.74198 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 35000 ║ 376.6893 ║ 69195.05 ║ 54 ║ 6.918834 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 99966 ║ 325.7854 ║ 1 ║ 47 ║ 6.909731 ║
╚══════════════╩════════════╩══════════╩═════════════════════╩════════════════╝
Thật đáng ngạc nhiên khi độ AVG_RANGE_ROWS
đồng đều khá cao cho tất cả các bước vào khoảng 6,9, ngay cả đối với các xô khóa mà mẫu không thể tìm thấy các giá trị trùng lặp. Tôi không biết tại sao lại như vậy. Giải thích có khả năng nhất là thuật toán được sử dụng để đoán tại các trang bị thiếu không hoạt động tốt với phân phối dữ liệu và kích thước mẫu này.
Như đã nêu trước đó, có thể tính mật độ cho cột FK bằng biểu đồ. Tổng các DISTINCT_RANGE_ROWS
giá trị cho tất cả các bước là 14497. Có 179 bước biểu đồ nên mật độ nên khoảng 1 / (179 + 14497) = 0,00006813845 khá gần với giá trị được báo cáo.
Thử nghiệm với một bảng lớn hơn có thể cho thấy vấn đề có thể trở nên tồi tệ hơn khi bảng trở nên lớn hơn. Lần này chúng ta sẽ bắt đầu với các hàng 1 M:
DROP TABLE IF EXISTS X_STATS_LARGE;
CREATE TABLE X_STATS_LARGE (
ID VARCHAR(10) NOT NULL,
FK VARCHAR(10) NOT NULL,
PADDING VARCHAR(900) NOT NULL,
PRIMARY KEY (ID));
INSERT INTO X_STATS_LARGE WITH (TABLOCK)
SELECT N, N, REPLICATE('Z', 900)
FROM dbo.Getnums(1000000);
CREATE INDEX IX_X_STATS_LARGE ON X_STATS_LARGE (FK);
-- get sampled stats
UPDATE STATISTICS X_STATS_LARGE IX_X_STATS_LARGE;
Đối tượng thống kê chưa thú vị. Mật độ cho FK
là 1.025289E-06 gần với chính xác (1.0E-06).
Bây giờ, hãy thêm một giá trị sai lệch và cập nhật lại số liệu thống kê:
INSERT INTO X_STATS_LARGE WITH (TABLOCK)
SELECT N + 1000000 , '350000', REPLICATE('Z', 900)
FROM dbo.Getnums(700000);
UPDATE STATISTICS X_STATS_LARGE IX_X_STATS_LARGE;
Với kích thước mẫu là 45627 hàng, mật độ của cột đầu tiên kém hơn trước:
╔══════════════╦════════════════╦═════════╗
║ All density ║ Average Length ║ Columns ║
╠══════════════╬════════════════╬═════════╣
║ 2.60051E-05 ║ 5.93563 ║ FK ║
║ 5.932542E-07 ║ 12.28485 ║ FK, ID ║
╚══════════════╩════════════════╩═════════╝
╔══════════════╦════════════╦═════════╦═════════════════════╦════════════════╗
║ RANGE_HI_KEY ║ RANGE_ROWS ║ EQ_ROWS ║ DISTINCT_RANGE_ROWS ║ AVG_RANGE_ROWS ║
╠══════════════╬════════════╬═════════╬═════════════════════╬════════════════╣
║ 100023 ║ 0 ║ 1 ║ 0 ║ 1 ║
║ 107142 ║ 8008.354 ║ 1 ║ 306 ║ 26.17787 ║
║ 110529 ║ 4361.357 ║ 1 ║ 168 ║ 26.02392 ║
║ 114558 ║ 3722.193 ║ 1 ║ 143 ║ 26.01217 ║
║ 116696 ║ 2556.658 ║ 1 ║ 98 ║ 25.97568 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 350000 ║ 5000.522 ║ 700435 ║ 192 ║ 26.03268 ║
║ ... ║ ... ║ ... ║ ... ║ ... ║
║ 999956 ║ 2406.266 ║ 1 ║ 93 ║ 25.96841 ║
╚══════════════╩════════════╩═════════╩═════════════════════╩════════════════╝
AVG_RANGE_ROWS
lên đến 26. Thật thú vị, nếu tôi thay đổi kích thước mẫu thành 170100 hàng (gấp 10 lần bảng khác) thì giá trị trung bình AVG_RANGE_ROWS
lại một lần nữa vào khoảng 6,9. Khi bảng của bạn trở nên lớn hơn, SQL Server sẽ chọn kích thước mẫu nhỏ hơn, điều đó có nghĩa là nó cần đoán về tỷ lệ trang lớn hơn trong bảng. Điều này có thể phóng đại các vấn đề thống kê cho một số loại sai lệch dữ liệu.
Tóm lại, điều quan trọng cần nhớ là SQL Server không tính toán mật độ như thế này:
SELECT COUNT(DISTINCT FK) * 1700000. / COUNT(*) -- 1071198.9 distinct values for one run
FROM X_STATS_LARGE TABLESAMPLE (45627 ROWS);
Mà đối với một số phân phối dữ liệu sẽ rất chính xác. Thay vào đó, nó sử dụng các thuật toán không có giấy tờ . Trong câu hỏi của bạn, bạn nói rằng dữ liệu của bạn không bị sai lệch, nhưng INSTANCEELEMENTID
giá trị có số ID liên kết cao nhất có 12 và số phổ biến nhất là 1. Đối với các mục đích của thuật toán được sử dụng Statman
có thể bị sai lệch.
Tại thời điểm đó, bạn không thể làm gì về điều đó ngoại trừ việc thu thập các số liệu thống kê với tỷ lệ mẫu cao hơn. Một chiến lược phổ biến là thu thập số liệu thống kê với FULLSCAN
và NORECOMPUTE
. Bạn có thể làm mới các số liệu thống kê với một công việc trên bất kỳ khoảng thời gian nào có ý nghĩa đối với tốc độ thay đổi dữ liệu của bạn. Theo kinh nghiệm của tôi, một FULLSCAN
bản cập nhật không tệ như hầu hết mọi người nghĩ, đặc biệt là so với chỉ số. SQL Server chỉ có thể quét toàn bộ chỉ mục thay vì toàn bộ bảng (như cách thực hiện đối với bảng lưu trữ đối với cột không được lập chỉ mục). Ngoài ra, trong SQL Serer 2014, chỉ có các FULLSCAN
cập nhật thống kê được thực hiện song song, do đó, một FULLSCAN
bản cập nhật có thể hoàn thành nhanh hơn một số cập nhật được lấy mẫu.
tablesample