Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Làm việc xung quanh các tối ưu hóa bị bỏ lỡ

Trong bài đăng cuối cùng của tôi, chúng ta đã thấy cách trình tối ưu hóa có thể chuyển đổi một truy vấn có tổng hợp vô hướng sang một dạng hiệu quả hơn. Xin nhắc lại, đây là giản đồ một lần nữa:

CREATE TABLE dbo.T1 (pk integer PRIMARY KEY, c1 integer NOT NULL);
CREATE TABLE dbo.T2 (pk integer PRIMARY KEY, c1 integer NOT NULL);
CREATE TABLE dbo.T3 (pk integer PRIMARY KEY, c1 integer NOT NULL);
GO
INSERT dbo.T1 (pk, c1)
SELECT n, n
FROM dbo.Numbers AS N
WHERE n BETWEEN 1 AND 50000;
GO 
INSERT dbo.T2 (pk, c1)
SELECT pk, c1 FROM dbo.T1;
GO
INSERT dbo.T3 (pk, c1)
SELECT pk, c1 FROM dbo.T1;
GO
CREATE INDEX nc1 ON dbo.T1 (c1);
CREATE INDEX nc1 ON dbo.T2 (c1);
CREATE INDEX nc1 ON dbo.T3 (c1);
GO
CREATE VIEW dbo.V1
AS
    SELECT c1 FROM dbo.T1
    UNION ALL
    SELECT c1 FROM dbo.T2
    UNION ALL
    SELECT c1 FROM dbo.T3;
GO
-- The test query
SELECT MAX(c1)
FROM dbo.V1;

Lựa chọn kế hoạch

Với 10.000 hàng trong mỗi bảng cơ sở, trình tối ưu hóa đưa ra một kế hoạch đơn giản để tính toán giá trị tối đa bằng cách đọc tất cả 30.000 hàng thành một tổng thể:

Với 50.000 hàng trong mỗi bảng, trình tối ưu hóa dành nhiều thời gian hơn cho vấn đề và tìm ra phương án thông minh hơn. Nó chỉ đọc hàng trên cùng (theo thứ tự giảm dần) từ mỗi chỉ mục và sau đó tính giá trị tối đa chỉ từ 3 hàng đó :

Lỗi trình tối ưu hóa

Bạn có thể nhận thấy điều gì đó hơi kỳ lạ về ước tính đó kế hoạch. Toán tử Nối đọc một hàng từ ba bảng và bằng cách nào đó tạo ra mười hai hàng! Đây là lỗi do một lỗi trong ước tính số lượng mà tôi đã báo cáo vào tháng 5 năm 2011. Nó vẫn chưa được khắc phục đối với SQL Server 2014 CTP 1 (ngay cả khi công cụ ước tính số lượng mới được sử dụng) nhưng tôi hy vọng nó sẽ được giải quyết cho bản phát hành cuối cùng.

Để xem lỗi phát sinh như thế nào, hãy nhớ lại rằng một trong những phương án thay thế kế hoạch được trình tối ưu hóa xem xét cho trường hợp hàng 50.000 có tổng một phần bên dưới toán tử Kết hợp:

Đây là ước tính bản số cho MAX từng phần này tổng hợp bị lỗi. Họ ước tính bốn hàng trong đó kết quả được đảm bảo là một hàng. Bạn có thể thấy một số không phải là bốn - nó phụ thuộc vào số lượng bộ xử lý logic có sẵn cho trình tối ưu hóa tại thời điểm kế hoạch được biên dịch (xem liên kết lỗi ở trên để biết thêm chi tiết).

Sau đó, trình tối ưu hóa thay thế các tổng hợp từng phần bằng các toán tử Top (1), tính toán lại ước tính số lượng một cách chính xác. Đáng buồn thay, toán tử Kết hợp vẫn phản ánh các ước tính cho các tổng hợp từng phần được thay thế (3 * 4 =12). Kết quả là, chúng tôi kết thúc với một Ghép nối đọc 3 hàng và tạo ra 12.

Sử dụng TOP thay vì MAX

Nhìn lại kế hoạch 50.000 hàng, có vẻ như cải tiến lớn nhất mà trình tối ưu hóa tìm thấy là sử dụng các toán tử Top (1) thay vì đọc tất cả các hàng và tính toán giá trị tối đa bằng cách sử dụng brute force. Điều gì xảy ra nếu chúng tôi thử một cái gì đó tương tự và viết lại truy vấn bằng cách sử dụng Top một cách rõ ràng?

SELECT TOP (1) c1
FROM dbo.V1
ORDER BY c1 DESC;

Kế hoạch thực thi cho truy vấn mới là:

Gói này hoàn toàn khác với gói được trình tối ưu hóa chọn cho MAX truy vấn. Nó có ba tính năng quét chỉ mục có thứ tự, hai liên kết hợp nhất chạy ở chế độ Nối và một toán tử Top duy nhất. Kế hoạch truy vấn mới này có một số tính năng thú vị đáng để xem xét chi tiết một chút.

Phân tích kế hoạch

Hàng đầu tiên (theo thứ tự chỉ mục giảm dần) được đọc từ chỉ mục không gộp của mỗi bảng và một Phép kết hợp hợp nhất hoạt động trong chế độ Kết hợp được sử dụng. Mặc dù toán tử Kết hợp liên kết không thực hiện phép nối theo nghĩa thông thường, nhưng thuật toán xử lý của toán tử này có thể dễ dàng điều chỉnh để nối các đầu vào của nó thay vì áp dụng tiêu chí kết hợp.

Lợi ích của việc sử dụng toán tử này trong kế hoạch mới là Kết hợp Hợp nhất bảo toàn thứ tự sắp xếp trên các đầu vào của nó. Ngược lại, một toán tử Kết nối thông thường đọc từ các đầu vào của nó theo trình tự. Sơ đồ dưới đây minh họa sự khác biệt (nhấp để mở rộng):

Hành vi bảo toàn thứ tự của Kết hợp Hợp nhất có nghĩa là hàng đầu tiên do toán tử Kết hợp ngoài cùng bên trái tạo ra trong kế hoạch mới được đảm bảo là hàng có giá trị cao nhất trong cột c1 trên cả ba bảng. Cụ thể hơn, kế hoạch hoạt động như sau:

  • Một hàng được đọc từ mỗi bảng (theo thứ tự giảm dần chỉ số); và
  • Mỗi hợp nhất thực hiện một lần kiểm tra để xem hàng đầu vào nào của nó có giá trị cao hơn

Đây có vẻ là một chiến lược rất hiệu quả, vì vậy có vẻ kỳ lạ khi MAX của trình tối ưu hóa kế hoạch có chi phí ước tính thấp hơn một nửa kế hoạch mới. Ở một mức độ lớn, lý do là Liên kết Hợp nhất bảo toàn trật tự được cho là đắt hơn một Liên kết đơn giản. Trình tối ưu hóa không nhận ra rằng mỗi Hợp nhất chỉ có thể nhìn thấy tối đa một hàng và kết quả là ước tính quá mức chi phí của nó.

Các vấn đề về chi phí khác

Nói một cách chính xác, chúng tôi không so sánh táo với táo ở đây, bởi vì hai kế hoạch dành cho các truy vấn khác nhau. So sánh chi phí như vậy thường không phải là điều hợp lệ để làm, mặc dù SSMS thực hiện chính xác điều đó bằng cách hiển thị tỷ lệ phần trăm chi phí cho các báo cáo khác nhau trong một lô. Nhưng, tôi lạc đề.

Nếu bạn nhìn vào kế hoạch mới trong SSMS thay vì SQL Sentry Plan Explorer, bạn sẽ thấy một cái gì đó như sau:

Một trong các toán tử Kết hợp Kết hợp Kết hợp có chi phí ước tính là 73% trong khi toán tử thứ hai (hoạt động trên cùng một số hàng) được hiển thị là không tốn kém gì cả. Một dấu hiệu khác cho thấy có điều gì đó không ổn ở đây là tỷ lệ phần trăm chi phí điều hành trong kế hoạch này không tổng bằng 100%.

Trình tối ưu hóa so với Công cụ Thực thi

Vấn đề nằm ở chỗ không tương thích giữa trình tối ưu hóa và công cụ thực thi. Trong trình tối ưu hóa, Union và Union Tất cả có thể có 2 đầu vào trở lên. Trong công cụ thực thi, chỉ toán tử Kết hợp mới có thể chấp nhận 2 hoặc nhiều hơn các yếu tố đầu vào; Hợp nhất Tham gia yêu cầu chính xác hai đầu vào, ngay cả khi được định cấu hình để thực hiện nối thay vì nối.

Để giải quyết sự không tương thích này, một bản ghi lại sau tối ưu hóa được áp dụng để dịch cây đầu ra của trình tối ưu hóa thành một dạng mà công cụ thực thi có thể xử lý. Khi một Liên minh hoặc Liên minh Tất cả có nhiều hơn hai đầu vào được thực hiện bằng cách sử dụng Hợp nhất, một chuỗi các nhà khai thác là cần thiết. Với ba đầu vào cho Liên minh Tất cả trong trường hợp hiện tại, cần có hai Liên minh Hợp nhất:

Chúng tôi có thể thấy cây đầu ra của trình tối ưu hóa (với ba đầu vào cho một liên hợp hợp nhất vật lý) bằng cách sử dụng cờ theo dõi 8607:

Bản sửa lỗi không hoàn chỉnh

Rất tiếc, quá trình viết lại sau tối ưu hóa không được triển khai hoàn hảo. Nó làm cho một chút lộn xộn của các con số chi phí. Làm tròn các vấn đề sang một bên, chi phí kế hoạch cộng thêm tới 114% với 14% bổ sung đến từ đầu vào cho Kết hợp kết hợp bổ sung Kết nối kết hợp được tạo ra bởi việc viết lại:

Hợp nhất ngoài cùng bên phải trong kế hoạch này là toán tử ban đầu trong cây đầu ra của trình tối ưu hóa. Nó được ấn định toàn bộ chi phí hoạt động của Liên minh. Sự hợp nhất khác được thêm vào bằng cách viết lại và nhận được chi phí bằng không.

Cho dù chúng ta chọn cách nào để xem xét nó (và có những vấn đề khác nhau ảnh hưởng đến Kết nối thông thường) thì các con số trông có vẻ kỳ lạ. Plan Explorer cố gắng hết sức để giải quyết thông tin bị hỏng trong kế hoạch XML bằng cách ít nhất đảm bảo các con số cộng lại lên đến 100%:

Vấn đề chi phí cụ thể này đã được khắc phục trong SQL Server 2014 CTP 1:

Chi phí của Kết hợp Hợp nhất hiện được chia đều giữa hai nhà khai thác và tỷ lệ phần trăm cộng lại lên đến 100%. Bởi vì XML cơ bản đã được sửa, SSMS cũng quản lý để hiển thị các số tương tự.

Gói nào Tốt hơn?

Nếu chúng tôi viết truy vấn bằng MAX , chúng tôi phải dựa vào việc lựa chọn trình tối ưu hóa để thực hiện thêm công việc cần thiết để tìm ra một kế hoạch hiệu quả. Nếu trình tối ưu hóa sớm tìm thấy một kế hoạch đủ tốt, nó có thể tạo ra một kế hoạch tương đối kém hiệu quả đọc mọi hàng từ mỗi bảng cơ sở:

Nếu bạn đang chạy SQL Server 2008 hoặc SQL Server 2008 R2, trình tối ưu hóa sẽ vẫn chọn một kế hoạch không hiệu quả bất kể số hàng trong bảng cơ sở. Kế hoạch sau được tạo trên SQL Server 2008 R2 với 50.000 hàng:

Ngay cả với 50 triệu hàng trong mỗi bảng, trình tối ưu hóa 2008 và 2008 R2 chỉ bổ sung tính song song, nó không giới thiệu các toán tử Hàng đầu:

Như đã đề cập trong bài viết trước của tôi, cờ theo dõi 4199 được yêu cầu để SQL Server 2008 và 2008 R2 tạo ra kế hoạch với các toán tử hàng đầu. SQL Server 2005 và 2012 trở đi không yêu cầu cờ theo dõi:

TOP với ORDER BY

Khi chúng tôi hiểu những gì đang diễn ra trong các kế hoạch thực thi trước đó, chúng tôi có thể đưa ra lựa chọn có ý thức (và có hiểu biết) để viết lại truy vấn bằng cách sử dụng TOP rõ ràng với ORDER BY:

SELECT TOP (1) c1
FROM dbo.V1
ORDER BY c1 DESC;

Kế hoạch thực thi kết quả có thể có tỷ lệ phần trăm chi phí trông kỳ lạ trong một số phiên bản của SQL Server, nhưng kế hoạch cơ bản là đúng đắn. Việc viết lại sau tối ưu hóa khiến các con số trông kỳ lạ được áp dụng sau khi tối ưu hóa truy vấn hoàn tất, vì vậy chúng tôi có thể chắc chắn rằng việc lựa chọn kế hoạch của trình tối ưu hóa không bị ảnh hưởng bởi vấn đề này.

Kế hoạch này không thay đổi tùy thuộc vào số lượng hàng trong bảng cơ sở và không yêu cầu bất kỳ cờ theo dõi nào để tạo. Một lợi thế nhỏ bổ sung là kế hoạch này được trình tối ưu hóa tìm thấy trong giai đoạn đầu tiên của tối ưu hóa dựa trên chi phí (tìm kiếm 0):

Gói tốt nhất được trình tối ưu hóa chọn cho MAX yêu cầu truy vấn chạy hai giai đoạn tối ưu hóa dựa trên chi phí (tìm kiếm 0 tìm kiếm 1).

Có một sự khác biệt nhỏ về ngữ nghĩa giữa TOP truy vấn và MAX ban đầu hình thức mà tôi nên đề cập đến. Nếu không có bảng nào chứa một hàng, truy vấn ban đầu sẽ tạo ra một NULL duy nhất kết quả. Thay thế TOP (1) truy vấn không tạo ra kết quả nào trong các trường hợp tương tự. Sự khác biệt này thường không quan trọng trong các truy vấn trong thế giới thực, nhưng nó là điều cần lưu ý. Chúng tôi có thể sao chép hành vi của TOP sử dụng MAX trong SQL Server 2008 trở đi bằng cách thêm một tập hợp trống GROUP BY :

SELECT MAX(c1)
FROM dbo.V1
GROUP BY ();

Thay đổi này không ảnh hưởng đến các kế hoạch thực thi được tạo cho MAX truy vấn theo cách hiển thị cho người dùng cuối.

MAX với Kết hợp Hợp nhất

Với sự thành công của Merge Join Concatenation trong TOP (1) kế hoạch thực thi, điều tự nhiên là tự hỏi liệu có thể tạo cùng một kế hoạch tối ưu cho MAX ban đầu hay không truy vấn nếu chúng tôi buộc trình tối ưu hóa sử dụng Kết hợp Hợp nhất thay vì Kết nối thông thường cho UNION ALL hoạt động.

Có một gợi ý truy vấn cho mục đích này - MERGE UNION - nhưng đáng buồn là nó chỉ hoạt động chính xác trong SQL Server 2012 trở đi. Trong các phiên bản trước, UNION gợi ý chỉ ảnh hưởng đến UNION truy vấn, không phải UNION ALL . Trong SQL Server 2012 trở đi, chúng tôi có thể thử điều này:

SELECT MAX(c1) 
FROM dbo.V1
OPTION (MERGE UNION)

Chúng tôi được thưởng bằng một kế hoạch có tính năng Kết hợp Hợp nhất. Thật không may, đó không phải là tất cả những gì chúng ta có thể mong đợi:

Các toán tử thú vị trong kế hoạch này là các loại. Lưu ý ước lượng số lượng đầu vào 1 hàng và ước tính 4 hàng trên đầu ra. Nguyên nhân đến giờ chắc hẳn đã quen thuộc với bạn:đó là cùng một lỗi ước lượng tổng hợp từng phần mà chúng ta đã thảo luận trước đó.

Sự hiện diện của các loại cho thấy một vấn đề nữa với các tổng hợp một phần. Chúng không chỉ tạo ra một ước tính số lượng không chính xác, chúng còn không bảo toàn được thứ tự chỉ mục khiến việc sắp xếp không cần thiết (Kết hợp Hợp nhất yêu cầu các đầu vào được sắp xếp). Các tổng hợp một phần là vô hướng MAX tổng hợp, được đảm bảo sản xuất một hàng, vì vậy vấn đề đặt hàng dù sao cũng phải được tranh luận (chỉ có một cách để sắp xếp một hàng!)

Đây là một điều đáng tiếc, bởi vì nếu không có các loại này thì đây sẽ là một kế hoạch thực hiện tốt. Nếu các tổng hợp một phần được triển khai đúng cách và MAX được viết bằng GROUP BY () , chúng tôi thậm chí có thể hy vọng rằng trình tối ưu hóa có thể phát hiện ra rằng ba Tops và Tổng số luồng cuối cùng có thể được thay thế bằng một toán tử Top cuối cùng duy nhất, đưa ra cùng một kế hoạch với TOP (1) rõ ràng truy vấn. Trình tối ưu hóa không chứa chuyển đổi đó ngày hôm nay và tôi không cho rằng nó sẽ hữu ích đủ thường xuyên để khiến việc đưa vào trở nên đáng giá trong tương lai.

Lời cuối cùng

Sử dụng TOP không phải lúc nào cũng thích hợp với MIN hoặc MAX . Trong một số trường hợp, nó sẽ tạo ra một kế hoạch kém tối ưu hơn đáng kể. Điểm mấu chốt của bài đăng này là việc hiểu các phép chuyển đổi được áp dụng bởi trình tối ưu hóa có thể đề xuất các cách viết lại truy vấn ban đầu có thể hữu ích.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tìm hiểu về Concatenate trong SQL với các ví dụ

  2. Khôi phục bản sao lưu cơ sở dữ liệu trong OpenCart 1.5

  3. Câu lệnh SQL SELECT

  4. Xóa tập tin theo dõi bằng ADRCI

  5. Huawei GaussDB