Tối ưu hóa tham gia trên bàn lớn


10

Tôi đang cố gắng dỗ một số hiệu suất cao hơn từ một truy vấn đang truy cập vào một bảng có ~ 250 triệu bản ghi. Từ việc tôi đọc kế hoạch thực hiện (không ước tính) thực tế, nút cổ chai đầu tiên là một truy vấn trông như thế này:

select
    b.stuff,
    a.added,
    a.value
from
    dbo.hugetable a
    inner join
    #smalltable b on a.fk = b.pk
where
    a.added between @start and @end;

Xem thêm về các định nghĩa của các bảng và chỉ mục liên quan.

Kế hoạch thực hiện chỉ ra rằng một vòng lặp lồng nhau đang được sử dụng trên #smalltable và việc quét chỉ mục trên hugetable đang được thực hiện 480 lần (cho mỗi hàng trong #smalltable). Điều này dường như ngược với tôi, vì vậy tôi đã cố gắng buộc một phép nối hợp nhất được sử dụng thay thế:

select
    b.stuff,
    a.added,
    a.value
from
    dbo.hugetable a with(index = ix_hugetable)
    inner merge join
    #smalltable b with(index(1)) on a.fk = b.pk
where
    a.added between @start and @end;

Chỉ mục trong câu hỏi (xem bên dưới để định nghĩa đầy đủ) bao gồm các cột fk (vị từ nối), được thêm vào (được sử dụng trong mệnh đề where) & id (vô dụng) theo thứ tự tăng dần và bao gồm giá trị .

Tuy nhiên, khi tôi thực hiện việc này, truy vấn sẽ chuyển từ 2 1/2 phút sang hơn 9. Tôi đã hy vọng rằng các gợi ý sẽ buộc một phép nối hiệu quả hơn chỉ thực hiện một lần vượt qua mỗi bảng, nhưng rõ ràng là không.

Bất kỳ hướng dẫn đều được chào đón. Thông tin bổ sung được cung cấp nếu được yêu cầu.

Cập nhật (2011/06/02)

Sau khi sắp xếp lại việc lập chỉ mục trên bàn, tôi đã thực hiện các bước hiệu suất đáng kể, tuy nhiên tôi đã gặp một trở ngại mới khi nói đến việc tóm tắt dữ liệu trong bảng lớn. Kết quả là một cuộc so sánh theo tháng, hiện tại trông như sau:

select
    b.stuff,
    datediff(month, 0, a.added),
    count(a.value),
    sum(case when a.value > 0 else 1 end) -- this triples the running time!
from
    dbo.hugetable a
    inner join
    #smalltable b on a.fk = b.pk
group by
    b.stuff,
    datediff(month, 0, a.added);

Hiện tại, hugetable có một chỉ mục được nhóm pk_hugetable (added, fk)(khóa chính) và một chỉ mục không được phân cụm theo cách khác ix_hugetable (fk, added).

Không có cột thứ 4 ở trên, trình tối ưu hóa sử dụng phép nối vòng lặp lồng nhau như trước, sử dụng #smalltable làm đầu vào bên ngoài và chỉ mục không phân cụm tìm kiếm như vòng lặp bên trong (thực hiện lại 480 lần). Điều tôi quan tâm là sự chênh lệch giữa các hàng ước tính (12.958,4) và các hàng thực tế (74,668,468). Chi phí tương đối của những lần tìm kiếm này là 45%. Thời gian chạy tuy nhiên dưới một phút.

Với cột thứ 4, thời gian chạy tăng đột biến lên 4 phút. Nó tìm kiếm trên chỉ mục được nhóm lần này (2 lần thực hiện) với cùng một chi phí tương đối (45%), tổng hợp thông qua khớp băm (30%), sau đó thực hiện băm tham gia trên #smalltable (0%).

Tôi không chắc chắn về hành động tiếp theo của mình. Mối quan tâm của tôi là không tìm kiếm phạm vi ngày cũng như vị từ tham gia được đảm bảo hoặc thậm chí tất cả những gì có khả năng làm giảm đáng kể tập kết quả. Phạm vi ngày trong hầu hết các trường hợp sẽ chỉ cắt có thể 10 - 15% hồ sơ và tham gia bên trong trên fk có thể lọc ra có thể 20-30%.


Theo yêu cầu của Will A, kết quả của sp_spaceused:

name      | rows      | reserved    | data        | index_size  | unused
hugetable | 261774373 | 93552920 KB | 18373816 KB | 75167432 KB | 11672 KB

#smalltable được định nghĩa là:

create table #endpoints (
    pk uniqueidentifier primary key clustered,
    stuff varchar(6) null
);

Trong khi dbo.hugetable được định nghĩa là:

create table dbo.hugetable (
    id uniqueidentifier not null,
    fk uniqueidentifier not null,
    added datetime not null,
    value decimal(13, 3) not null,

    constraint pk_hugetable primary key clustered (
        fk asc,
        added asc,
        id asc
    )
    with (
        pad_index = off, statistics_norecompute = off,
        ignore_dup_key = off, allow_row_locks = on,
        allow_page_locks = on
    )
    on [primary]
)
on [primary];

Với chỉ số sau được xác định:

create nonclustered index ix_hugetable on dbo.hugetable (
    fk asc, added asc, id asc
) include(value) with (
    pad_index = off, statistics_norecompute = off,
    sort_in_tempdb = off, ignore_dup_key = off,
    drop_existing = off, online = off,
    allow_row_locks = on, allow_page_locks = on
)
on [primary];

Trường id là dự phòng, một vật phẩm từ một DBA trước đó đã nhấn mạnh rằng tất cả các bảng ở mọi nơi nên có GUID, không có ngoại lệ.


Bạn có thể bao gồm kết quả của sp_spaceuse 'dbo.hugetable' không?
Sẽ có

Xong, thêm ngay phía trên đầu của định nghĩa bảng.
Nhanh Joe Smith

Chắc chắn rồi. Kích thước lố bịch của nó là lý do tôi đang xem xét điều này.
Nhanh Joe Smith

Câu trả lời:


5

Ngoại hình của bạn ix_hugetablekhá vô dụng vì:

  • chỉ số cụm (PK)
  • INCLUDE không có sự khác biệt bởi vì một chỉ mục được nhóm INCLUDEs tất cả các cột không khóa (giá trị không khóa ở lá thấp nhất = INCLUDEd = chỉ số được nhóm là gì)

Ngoài ra: - nên thêm hoặc fk trước - ID là đầu tiên = không sử dụng nhiều

Hãy thử thay đổi khóa cụm (added, fk, id)và thả ix_hugetable. Bạn đã thử (fk, added, id). Nếu không có gì khác, bạn sẽ tiết kiệm rất nhiều dung lượng đĩa và bảo trì chỉ mục

Một lựa chọn khác có thể là thử gợi ý FORCE ORDER với các cách đặt hàng theo thứ tự bảng và không có gợi ý THAM GIA / INDEX. Tôi cố gắng không sử dụng gợi ý THAM GIA / INDEX cá nhân vì bạn xóa các tùy chọn cho trình tối ưu hóa. Nhiều năm trước tôi đã được thông báo (hội thảo với Chuyên gia SQL) rằng gợi ý FORCE ORDER có thể giúp ích khi bạn có một bảng lớn THAM GIA bảng nhỏ: YMMV 7 năm sau ...

Ồ, và cho chúng tôi biết DBA sống ở đâu để chúng tôi có thể sắp xếp điều chỉnh bộ gõ

Chỉnh sửa, sau ngày 02 tháng 6 cập nhật

Cột thứ 4 không phải là một phần của chỉ mục không được phân cụm nên nó sử dụng chỉ mục được phân cụm.

Hãy thử thay đổi chỉ mục NC thành INCLUDE cột giá trị để nó không phải truy cập vào cột giá trị cho chỉ mục được nhóm

create nonclustered index ix_hugetable on dbo.hugetable (
    fk asc, added asc
) include(value)

Lưu ý: Nếu giá trị không thể rỗng thì nó giống như COUNT(*)về mặt ngữ nghĩa. Nhưng đối với SUM nó cần giá trị thực tế chứ không phải sự tồn tại .

Ví dụ, nếu bạn thay đổi COUNT(value)thành COUNT(DISTINCT value) mà không thay đổi chỉ mục, nó sẽ phá lại truy vấn vì nó phải xử lý giá trị dưới dạng giá trị, không phải là tồn tại.

Truy vấn cần 3 cột: thêm, fk, giá trị. 2 cái đầu tiên được lọc / nối nên các cột chính. giá trị chỉ được sử dụng để có thể được bao gồm. Sử dụng cổ điển của một chỉ số bao gồm.


Hah, tôi đã có trong đầu rằng các chỉ mục phân cụm và không phân cụm có fk & được thêm vào theo thứ tự khác nhau. Tôi không thể tin rằng tôi đã không nhận thấy điều đó, gần như tôi không thể tin rằng nó đã được thiết lập theo cách này ngay từ đầu. Tôi sẽ thay đổi chỉ số cụm vào ngày mai, sau đó xuống phố để uống cà phê trong khi nó được xây dựng lại.
Joe Smith nhanh

Tôi đã thay đổi việc lập chỉ mục và có một cú đánh với FORCE ORDER trong nỗ lực giảm số lượng tìm kiếm trên bàn lớn nhưng không có kết quả. Câu hỏi của tôi đã được cập nhật.
Nhanh Joe Smith

@Quick Joe Smith: đã cập nhật câu trả lời của tôi
gbn

Vâng, tôi đã thử điều đó không lâu sau đó. Bởi vì việc xây dựng lại chỉ mục mất quá nhiều thời gian, tôi đã quên nó và ban đầu tôi nghĩ rằng tôi đã tăng tốc làm những việc hoàn toàn không liên quan.
Nhanh Joe Smith

2

Xác định một chỉ mục hugetabletrên chỉ addedcột.

DB sẽ chỉ sử dụng một chỉ mục nhiều phần (nhiều cột) ở phía bên phải của danh sách cột vì nó có các giá trị đếm từ bên trái. Truy vấn của bạn không chỉ định fktrong mệnh đề where của truy vấn đầu tiên, vì vậy nó bỏ qua chỉ mục.


Kế hoạch thực hiện cho thấy chỉ mục (ix_hugetable) đang được tìm kiếm. Hay bạn đang nói rằng chỉ số này không phù hợp với truy vấn?
Nhanh Joe Smith

Chỉ số không phù hợp. Ai biết nó "sử dụng chỉ mục" như thế nào. Kinh nghiệm cho tôi biết đây là vấn đề của bạn. Hãy thử nó và cho chúng tôi biết làm thế nào nó đi.
Bohemian

@Quick Joe Smith - bạn đã thử đề xuất của @ Bohemian chưa? Kết quả ở đâu?
Lieven Keersmaekers

2
Tôi không đồng ý: mệnh đề ON được xử lý logic trước và thực sự là WHERE trong thực tế nên OP phải thử cả hai cột trước. Không lập chỉ mục trên fk tại tất cả = quét chỉ mục cụm hoặc tra cứu khóa để lấy giá trị fk cho THAM GIA. Bạn có thể thêm một số tài liệu tham khảo cho hành vi bạn đã mô tả quá xin vui lòng? Đặc biệt đối với SQL Server khi bạn có ít câu trả lời lịch sử trước đó cho RDBMS này. Trên thực tế, -1 khi nhìn lại khi tôi nhập bình luận này
gbn

2

Kế hoạch thực hiện chỉ ra rằng một vòng lặp lồng nhau đang được sử dụng trên #smalltable và việc quét chỉ mục trên hugetable đang được thực hiện 480 lần (cho mỗi hàng trong #smalltable).

Đây là thứ tự tôi mong muốn trình tối ưu hóa truy vấn sẽ sử dụng, giả sử rằng một vòng lặp tham gia vào lựa chọn đúng. Cách khác là lặp 250 lần và thực hiện tra cứu vào bảng #temp mỗi lần - việc này có thể mất hàng giờ / ngày.

Chỉ mục bạn buộc phải sử dụng trong tham gia MERGE có khá nhiều hàng 250 triệu * 'kích thước của mỗi hàng' - không nhỏ, ít nhất là vài GB. Đánh giá từ sp_spaceusedđầu ra 'một vài GB' có thể là một cách đánh giá thấp - việc tham gia MERGE yêu cầu bạn truy tìm chỉ số sẽ rất chuyên sâu vào I / O.


Tôi hiểu rằng có 3 loại thuật toán nối và phép nối hợp nhất có hiệu suất tốt nhất khi cả hai đầu vào được sắp xếp theo vị từ nối. Đúng hay sai, đây là kết quả mà tôi đang cố gắng đạt được.
Nhanh Joe Smith

2
Nhưng có nhiều hơn thế. Nếu #smalltable có số lượng hàng lớn thì phép nối hợp nhất có thể phù hợp. Nếu, như tên gọi của nó, nó có một số lượng nhỏ các hàng thì phép nối vòng có thể là lựa chọn đúng. Hãy tưởng tượng #smalltable có một hoặc hai hàng và khớp với một số hàng từ bảng khác - thật khó để biện minh cho việc hợp nhất tham gia ở đây.
Sẽ có

Tôi hình dung có nhiều hơn thế; Tôi chỉ không biết đó có thể là gì. Tối ưu hóa cơ sở dữ liệu không chính xác là phù hợp mạnh mẽ của tôi, như bạn có thể đã đoán.
Nhanh Joe Smith

@Quick Joe Smith - cảm ơn vì sp_spaceuse. 75GB chỉ mục và 18GB dữ liệu - ix_hugetable không phải là chỉ mục duy nhất trên bảng?
Sẽ có

1
Di chúc +1. Các kế hoạch hiện đang làm điều đúng đắn. Vấn đề nằm ở việc tìm kiếm đĩa ngẫu nhiên do cách các bảng của bạn được phân cụm.
Denis de Bernardy

1

Chỉ số của bạn không chính xác. Xem chỉ số dos và donts .

Như mọi thứ, chỉ số hữu ích duy nhất của bạn là trên khóa chính của bảng nhỏ. Do đó, kế hoạch hợp lý duy nhất là seq quét chiếc bàn nhỏ và lồng vòng lộn xộn với cái lớn.

Hãy thử thêm một chỉ mục cụm trên hugetable(added, fk). Điều này sẽ làm cho trình hoạch định tìm kiếm các hàng có thể áp dụng từ bảng lớn và vòng lặp lồng hoặc hợp nhất nối chúng với bảng nhỏ.


Cảm ơn liên kết đó. Tôi sẽ thử điều này khi tôi đi làm vào ngày mai.
Nhanh Joe Smith
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.