Làm chậm rất nhiều truy vấn SQL Server khi thêm ký tự đại diện (hoặc trên cùng)


52

Tôi đã có một sở thú gồm 20 triệu động vật mà tôi theo dõi trên cơ sở dữ liệu SQL Server 2005 của mình. Khoảng 1% trong số chúng là người da đen và khoảng 1% trong số chúng là thiên nga. Tôi muốn nhận thông tin chi tiết về tất cả những con thiên nga đen và vì vậy, không muốn tràn vào trang kết quả mà tôi đã làm:

select top 10 * 
from animal 
where colour like 'black'  
and species like 'swan'

(Vâng, vô tình những trường đó là freetext nhưng cả hai đều được lập chỉ mục). Hóa ra chúng ta không có những con vật như vậy khi truy vấn trả về một tập hợp trống trong khoảng 300 mili giây. Nó sẽ nhanh hơn gấp đôi nếu tôi sử dụng '=' thay vì 'thích' nhưng tôi có linh cảm là điều sau này sẽ giúp tôi tiết kiệm được một số cách gõ.

Hóa ra người quản lý vườn thú nghĩ rằng anh ta có thể đã nhập một số con thiên nga là 'đen' nên tôi sửa đổi truy vấn cho phù hợp:

select top 10 * 
from animal  
where colour like 'black%' 
and species like 'swan'

Hóa ra không có ai trong số họ (và trên thực tế không có động vật '% đen' nào ngoại trừ 'đen') nhưng truy vấn hiện mất khoảng 30 giây để trả về sản phẩm nào.

Dường như đó chỉ là sự kết hợp giữa 'top' và 'like%' gây rắc rối bởi vì

select count(*) 
from animal  
where colour like 'black%' 
and species like 'swan'

trả về 0 rất nhanh và thậm chí

select * 
from animal 
where colour like 'black%' 
and species like 'swan'

trả về sản phẩm nào trong một phần của giây.

Có ai có ý tưởng tại sao 'top' và '%' nên âm mưu gây ra sự mất hiệu suất đáng kể như vậy, đặc biệt là trong một tập kết quả trống không?

EDIT: Để làm rõ, tôi không sử dụng bất kỳ chỉ mục FreeText nào, tôi chỉ có nghĩa là các trường là freetext tại điểm nhập, tức là không được chuẩn hóa trong cơ sở dữ liệu. Xin lỗi vì sự nhầm lẫn, từ ngữ kém về phía tôi.

Câu trả lời:


76

Đây là một quyết định của tối ưu hóa dựa trên chi phí.

Các chi phí ước tính được sử dụng trong lựa chọn này là không chính xác vì nó giả định tính độc lập thống kê giữa các giá trị trong các cột khác nhau.

Nó tương tự như vấn đề được mô tả trong Row Goals Gone Rogue trong đó số chẵn và số lẻ có mối tương quan nghịch.

Nó rất dễ dàng để sinh sản.

CREATE TABLE dbo.animal(
    id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
    colour varchar(50) NOT NULL,
    species varchar(50) NOT NULL,
    Filler char(10) NULL
);

/*Insert 20 million rows with 1% black and 1% swan but no black swans*/
WITH T
     AS (SELECT TOP 20000000 ROW_NUMBER() OVER (ORDER BY @@SPID) AS RN
         FROM   master..spt_values v1,
                master..spt_values v2,
                master..spt_values v3)
INSERT INTO dbo.animal
            (colour,
             species)
SELECT CASE
         WHEN RN % 100 = 1 THEN 'black'
         ELSE CAST(RN % 100 AS VARCHAR(3))
       END,
       CASE
         WHEN RN % 100 = 2 THEN 'swan'
         ELSE CAST(RN % 100 AS VARCHAR(3))
       END
FROM   T 

/*Create some indexes*/
CREATE NONCLUSTERED INDEX ix_species ON  dbo.animal(species);
CREATE NONCLUSTERED INDEX ix_colour ON  dbo.animal(colour);

Bây giờ cố gắng

SELECT TOP 10 *
FROM   animal
WHERE  colour LIKE 'black'
       AND species LIKE 'swan' 

Điều này đưa ra kế hoạch dưới đây được chi phí tại 0.0563167.

nhập mô tả hình ảnh ở đây

Kế hoạch có thể thực hiện liên kết hợp nhất giữa kết quả của hai chỉ mục trên idcột. ( Thêm chi tiết về thuật toán hợp nhất ở đây ).

Hợp nhất tham gia yêu cầu cả hai đầu vào được sắp xếp theo khóa tham gia.

Các chỉ mục không bao gồm được sắp xếp theo thứ tự (species, id)(colour, id)tương ứng (các chỉ mục không phân cụm không luôn luôn có bộ định vị hàng được thêm vào cuối khóa nếu không được thêm một cách rõ ràng). Truy vấn không có bất kỳ ký tự đại diện nào đang thực hiện tìm kiếm đẳng thức vào species = 'swan'colour ='black'. Vì mỗi tìm kiếm chỉ lấy một giá trị chính xác từ cột hàng đầu, các hàng khớp sẽ được sắp xếp theo thứ tự do idđó kế hoạch này là có thể.

Truy vấn các nhà khai thác kế hoạch thực hiện từ trái sang phải . Với toán tử bên trái yêu cầu các hàng từ con của nó, lần lượt yêu cầu các hàng từ con của chúng (và cứ thế cho đến khi đạt được các nút lá). Trình TOPlặp sẽ dừng yêu cầu thêm bất kỳ hàng nào từ con của nó sau khi nhận được 10 hàng.

SQL Server có số liệu thống kê về các chỉ mục cho biết rằng 1% số hàng khớp với từng vị từ. Nó giả định rằng các thống kê này là độc lập (nghĩa là không tương quan tích cực hoặc tiêu cực) để trung bình một khi nó đã xử lý 1.000 hàng khớp với vị từ đầu tiên, nó sẽ tìm thấy 10 khớp với thứ hai và có thể thoát. (kế hoạch trên thực tế cho thấy 987 chứ không phải 1.000 nhưng đủ gần).

Trong thực tế vì các vị từ có tương quan nghịch, kế hoạch thực tế cho thấy rằng tất cả 200.000 hàng phù hợp cần được xử lý từ mỗi chỉ mục nhưng điều này được giảm nhẹ ở một mức độ nào đó bởi vì các hàng không tham gia cũng có nghĩa là thực sự cần tìm kiếm.

So sánh với

SELECT TOP 10 *
FROM   animal
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan' 

Cung cấp cho các kế hoạch dưới đây được chi phí tại 0.567943

nhập mô tả hình ảnh ở đây

Việc thêm ký tự đại diện hiện đã gây ra quét chỉ mục. Chi phí của kế hoạch vẫn còn khá thấp mặc dù để quét trên bảng hàng 20 triệu.

Thêm querytraceon 9130một số thông tin

SELECT TOP 10 *
FROM   animal
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan'       
OPTION (QUERYTRACEON 9130)  

nhập mô tả hình ảnh ở đây

Có thể thấy rằng SQL Server cho rằng nó sẽ chỉ cần quét khoảng 100.000 hàng trước khi tìm thấy 10 khớp với vị từ và TOPcó thể dừng yêu cầu các hàng.

Một lần nữa điều này có ý nghĩa với giả định độc lập như 10 * 100 * 100 = 100,000

Cuối cùng, hãy thử và buộc một kế hoạch giao cắt chỉ số

SELECT TOP 10 *
FROM   animal WITH (INDEX(ix_species), INDEX(ix_colour))
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan' 

Điều này mang lại cho tôi một kế hoạch song song với chi phí ước tính là 3,4625

nhập mô tả hình ảnh ở đây

Sự khác biệt chính ở đây là colour like 'black%'vị ngữ có thể khớp với nhiều màu khác nhau. Điều này có nghĩa là các hàng chỉ mục phù hợp cho vị từ đó không còn được đảm bảo để được sắp xếp theo thứ tự id.

Ví dụ: chỉ mục tìm kiếm like 'black%'có thể trả về các hàng sau

+------------+----+
|   Colour   | id |
+------------+----+
| black      | 12 |
| black      | 20 |
| black      | 23 |
| black      | 25 |
| blackberry |  1 |
| blackberry | 50 |
+------------+----+

Trong mỗi màu, các id được sắp xếp nhưng các id trên các màu khác nhau có thể không được.

Kết quả là SQL Server không còn có thể thực hiện giao cắt chỉ mục kết hợp hợp nhất (mà không cần thêm toán tử sắp xếp chặn) và thay vào đó, nó sẽ thực hiện phép nối băm. Hash Join đang chặn đầu vào bản dựng, vì vậy bây giờ chi phí phản ánh thực tế là tất cả các hàng khớp sẽ cần được xử lý từ đầu vào bản dựng thay vì giả sử nó sẽ chỉ phải quét 1.000 như trong kế hoạch đầu tiên.

Tuy nhiên, đầu vào thăm dò không chặn và nó vẫn ước tính không chính xác rằng nó sẽ có thể dừng việc thăm dò sau khi xử lý 987 hàng từ đó.

(Thông tin thêm về Không chặn so với chặn lặp ở đây)

Do chi phí gia tăng của các hàng ước tính thêm và hàm băm tham gia quét chỉ mục một phần có vẻ rẻ hơn.

Trong thực tế, quét chỉ mục cụm "một phần" hoàn toàn không phải là một phần và nó cần phải chug qua toàn bộ 20 triệu hàng thay vì 100 nghìn giả định khi so sánh các kế hoạch.

Việc tăng giá trị của TOP(hoặc loại bỏ hoàn toàn) cuối cùng sẽ gặp một điểm bùng phát trong đó số lượng hàng mà nó ước tính quét CI sẽ cần phải làm cho kế hoạch đó trông đắt hơn và nó trở lại kế hoạch giao cắt chỉ mục. Đối với tôi cắt ra điểm giữa hai phương án là TOP (89)vs TOP (90).

Đối với bạn nó cũng có thể khác nhau vì nó phụ thuộc vào độ rộng của chỉ số cụm.

Loại bỏ TOPvà buộc quét CI

SELECT *
FROM   animal WITH (INDEX = 1)
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan' 

Được chi phí 88.0586trên máy của tôi cho bảng ví dụ của tôi.

Nếu SQL Server nhận thức được rằng sở thú không có thiên nga đen và nó sẽ cần phải quét toàn bộ thay vì chỉ đọc 100.000 hàng thì kế hoạch này sẽ không được chọn.

Tôi đã thử stats cột đa trên animal(species,colour)animal(colour,species)và lọc số liệu thống kê trên animal (colour) where species = 'swan'nhưng không ai trong số những sự giúp đỡ thuyết phục nó rằng thiên nga đen không tồn tại và TOP 10quét sẽ cần phải xử lý hơn 100.000 hàng.

Điều này là do "giả định bao gồm" trong đó SQL Server về cơ bản giả định rằng nếu bạn đang tìm kiếm thứ gì đó thì nó có thể tồn tại.

Trên 2008+ có cờ theo dõi tài liệu 4138 tắt mục tiêu hàng. Hiệu quả của việc này là kế hoạch được tính chi phí mà không có giả định rằng TOPsẽ cho phép các nhà khai thác con chấm dứt sớm mà không cần đọc tất cả các hàng khớp. Với cờ theo dõi này, tôi tự nhiên có được kế hoạch giao chỉ số tối ưu hơn.

SELECT TOP 10 *
FROM   animal
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan'
OPTION (QUERYTRACEON 4138)       

nhập mô tả hình ảnh ở đây

Kế hoạch này hiện chi phí chính xác cho việc đọc toàn bộ 200 nghìn hàng trong cả hai chỉ số tìm kiếm nhưng chi phí cao hơn cho việc tra cứu chính (ước tính 2 nghìn so với thực tế 0. Điều TOP 10này sẽ hạn chế tối đa 10 nhưng cờ theo dõi ngăn điều này được tính đến) . Tuy nhiên, kế hoạch có chi phí rẻ hơn đáng kể so với quét CI đầy đủ vì vậy được chọn.

Tất nhiên kế hoạch này có thể không được tối ưu cho các kết hợp mà phổ biến. Chẳng hạn như thiên nga trắng.

Một chỉ mục tổng hợp trên animal (colour, species)hoặc lý tưởng animal (species, colour)sẽ cho phép truy vấn hiệu quả hơn nhiều cho cả hai kịch bản.

Để sử dụng hiệu quả nhất của chỉ số tổng hợp, LIKE 'swan'cũng cần phải thay đổi thành = 'swan'.

Bảng dưới đây cho thấy các vị từ tìm kiếm và các vị từ còn lại được hiển thị trong các kế hoạch thực hiện cho tất cả bốn hoán vị.

+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
|                 WHERE clause                 |       Index       |                         Seek Predicate                         |              Residual Predicate              |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
| colour LIKE 'black%' AND species LIKE 'swan' | ix_colour_species | colour >= 'black' AND colour < 'blacL'                         | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species LIKE 'swan' | ix_species_colour | species >= 'swan' AND species <= 'swan'                        | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species = 'swan'    | ix_colour_species | (colour,species) >= ('black', 'swan')) AND colour < 'blacL'    | colour LIKE 'black%' AND species = 'swan'    |
| colour LIKE 'black%' AND species = 'swan'    | ix_species_colour | species = 'swan' AND (colour >= 'black' and colour <  'blacL') | colour like 'black%'                         |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+

15

Sáng lập ra điều hấp dẫn này, tôi đã thực hiện một số tìm kiếm và tình cờ tìm thấy Q / A này Làm thế nào (và tại sao) TOP tác động đến một kế hoạch thực hiện?

Về cơ bản, việc sử dụng TOP làm thay đổi chi phí của các nhà khai thác theo nó (theo cách không cần thiết), điều này cũng khiến kế hoạch tổng thể thay đổi (sẽ rất tuyệt nếu bạn bao gồm ExecPlans có và không có TOP 10), điều này thay đổi khá nhiều việc thực thi quá mức của các truy vấn.

Hi vọng điêu nay co ich.

Chẳng hạn, tôi đã thử nó trên cơ sở dữ liệu và: -khi không có đỉnh nào được gọi, song song được sử dụng - với TOP, song song không được sử dụng

Vì vậy, một lần nữa, hiển thị kế hoạch thực hiện của bạn sẽ cung cấp thêm thông tin.

Chúc một ngày tốt lành


-1

Tôi tin rằng điều này có thể là do bản chất cơ bản của MSSQL 2005 và cách trình tối ưu hóa truy vấn quyết định kế hoạch thực hiện nào là hiệu quả nhất.

Nếu bạn sử dụng biến SQL, nó sẽ 'lừa' trình tối ưu hóa truy vấn bằng cách sử dụng khớp băm thay vì các vòng lặp lồng nhau, điều này sẽ dẫn đến mức độ song song cao hơn nhiều.

Thử:

DECLARE @topn INT = 10
SELECT TOP (@topn) *
FROM    animal
WHERE   colour LIKE 'black%' 
AND species LIKE 'swan'

5
Làm mờ TOPgiá trị trong một biến có nghĩa là nó sẽ giả sử TOP 100chứ không phải TOP 10. Điều này có thể hoặc không thể giúp tùy thuộc vào điểm bùng phát giữa hai kế hoạch là gì.
Martin 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.