Kết quả mật độ kỳ lạ trong thống kê được lấy mẫu


8

Một chỉ số NC có được phân phối thống kê hoàn toàn khác khi được ước tính với lấy mẫu so với fullscan; người được lấy mẫu có một vectơ mật độ kỳ quái. Điều này dẫn đến kế hoạch thực hiện kém.


Tôi có một bảng gồm ~ 27 triệu hàng, với cột FK không null được hỗ trợ bởi một chỉ mục không bao gồm. Bảng được nhóm trên khóa chính của nó. Cả hai cột là varchar.

Một bản cập nhật thống kê toàn màn hình cho cột FK của chúng tôi cung cấp một vectơ mật độ tìm kiếm bình thường:

All density Average Length  Columns
6,181983E-08    45,99747    INSTANCEELEMENTID
3,615442E-08    95,26874    INSTANCEELEMENTID, ID

Đó là, chúng tôi dự kiến ​​sẽ đọc khoảng 1,7 hàng cho mỗi khác biệt INSTANCELEMENTIDmà chúng tôi đang tham gia.

Một thùng điển hình từ biểu đồ trông như thế này:

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
FOOBAR          133053      10      71366               1,679318

Tuy nhiên, nếu chúng tôi thực hiện cập nhật được lấy mẫu (sử dụng số mẫu mặc định là 230 nghìn hàng cho bảng này), mọi thứ sẽ lần lượt trở nên kỳ quái:

4,773657E-06    45,99596    INSTANCEELEMENTID
3,702179E-08    95,30183    INSTANCEELEMENTID, ID

Mật độ trên INSTANCEELEMENTIDbây giờ là hai bậc lớn hơn. (Tuy nhiên, mật độ cho cả hai cột đã được ước tính là một giá trị khá chấp nhận được).

Một thùng điển hình từ biểu đồ bây giờ trông như thế này;

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS     DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
FOOBAR          143870,4    766,2573    1247                115,3596
ZOTZOT          131560,7    1           969                 135,7092

đó là một phân phối hoàn toàn khác nhau. Lưu ý rằng INSTANCEELEMENTIDsố IDs có số liên kết cao nhất có 12, số phổ biến nhất là 1. Một điều rất lạ là một số thùng có EQ_lawS = 1, điều này xảy ra với khoảng 10% số thùng.

Không có bản vẽ "không may mắn" nào của các hàng lạ có thể đóng góp cho việc này.

Tôi có đang đọc biểu đồ chính xác không? Có vẻ như việc lấy mẫu đã thay đổi tỷ lệ EQ_lawS, DISTINCT_RANGE_lawS và AVG_RANGE_lawS bằng cách nào đó?

Cái bàn là, theo như tôi có thể nói, không bị xáo trộn. Tôi đã cố gắng mô phỏng bộ lấy mẫu bằng cách tự ước tính các giá trị tablesample. Đếm các kết quả này theo cách thông thường sẽ cho kết quả phù hợp với phiên bản fullscan chứ không phải bộ lấy mẫu.

Hơn nữa, tôi không thể tái tạo hành vi này trên các chỉ số cụm.


Tôi đã thu hẹp điều này xuống để tái tạo:

CREATE TABLE F_VAL (
    id varchar(100) primary key,
    num_l_val int not null
)

set nocount on

declare @rowlimit integer = 20000000;

Bảng phải đủ lớn để điều này được quan sát. Tôi đã thấy điều này với uniqueidentifervarchar(100)không int.

declare @i integer = 1;

declare @r float = rand()

while @i < @rowlimit
begin
set @r = rand()
insert f_val (id,num_l_val)
values (
   cast(@i as varchar(100)) + REPLICATE('f', 40 - len(@i)),
   case when @r > 0.8 then 4 when @r > 0.5 then 3 when @r > 0.4 then 2 else 1 end
)
  set @i = @i + 1

end

create table k_val (
 id int identity primary key,
 f_val varchar(100) not null,
)

insert into k_val(f_val)
select id from F_VAL
union all select id from f_val where num_l_val - 1 = 1
union all select id from f_val where num_l_val - 2 = 1
union all select id from f_val where num_l_val - 3 = 1
order by id

create nonclustered index IX_K_VAL_F_VAL  ON K_VAL (F_VAL)

update statistics K_VAL(IX_K_VAL_F_VAL) 
dbcc show_statistics (k_val,IX_k_VAL_F_VAL)

update statistics K_VAL(IX_K_VAL_F_VAL) WITH FULLSCAN
dbcc show_statistics (k_val,IX_k_VAL_F_VAL)

So sánh hai thống kê; cái được lấy mẫu hiện đại diện cho một vectơ mật độ khác nhau và các thùng biểu đồ đã tắt. Lưu ý rằng bảng không bị lệch.

Sử dụng intnhư kiểu dữ liệu không gây ra điều này, SQL Server không kiểm tra toàn bộ biểu dữ liệu khi sử dụng varchar?

Đáng để đề cập rằng vấn đề dường như mở rộng, tăng tỷ lệ mẫu giúp.

Câu trả lời:


3

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 đồ, StatManhà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 StatManhà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 VARCHARcá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 FKcộ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 VARCHARcộ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_ROWSgiá 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 FKlà 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_ROWSlê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_ROWSlạ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 INSTANCEELEMENTIDgiá 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 Statmancó 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 FULLSCANNORECOMPUTE. 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 FULLSCANbả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 FULLSCANcập nhật thống kê được thực hiện song song, do đó, một FULLSCANbả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.


Cảm ơn đã trả lời, Joe! Điều này có vẻ như một lỗi hoặc khoảng cách tính năng; hãy nhớ rằng hành vi này không xảy ra khi bạn đang sử dụng các giá trị dựa trên INT. Trên INT, hệ thống hoạt động tốt hơn nhiều và bạn có được ước tính phân phối thống kê tiếp cận phân phối thực tốt hơn nhiều. Trong khi StatMan rõ ràng làm một số làm mịn / heuristic; Tôi muốn nói rằng thật đáng lo ngại khi bạn có thể tự mình nhận được kết quả tốt hơn bằng cách trực tiếp tính toán biểu đồ, vẫn sử dụng cùng một dữ liệu nguồn như người dùng có đượctablesample

@JohanBenumEvensberget IMO không phải là không hợp lý khi nó hành xử khác nhau đối với các cột INT. Với INT, bạn có một miền hạn chế hơn nhiều cho các giá trị bị thiếu. Đối với chuỗi, nó thực sự có thể là bất cứ điều gì đến giới hạn chiều dài. Nó có thể gây bối rối khi chúng ta không có được một biểu đồ tốt nhưng hầu hết thời gian nó hoạt động khá tốt. Vì mã là bí mật, chúng tôi thực sự không thể biết liệu nó có hoạt động như mong đợi hay không. Bạn có thể xem xét việc tạo một bài đăng ở đây nếu bạn cảm thấy rằng vấn đề này cần được giải quyết bởi MS: connect.microsoft.com/QueryServer/Feedback
Joe Obbish 22/03/2017
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.