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

Cẩn thận với dữ liệu sai lệch từ SET STATISTICS IO

Đồng nghiệp của tôi, Steve Wright (blog | @SQL_Steve), gần đây đã thúc đẩy tôi một câu hỏi về một kết quả kỳ lạ mà anh ấy đã nhìn thấy. Để kiểm tra một số chức năng trong công cụ mới nhất của chúng tôi, SQL Sentry Plan Explorer PRO, anh ấy đã tạo ra một bảng rộng và lớn, đồng thời đang chạy nhiều truy vấn đối với nó. Trong một trường hợp, anh ta trả về rất nhiều dữ liệu, nhưng STATISTICS IO cho thấy rằng rất ít lần đọc diễn ra. Tôi đã ping một số người trên #sqlhelp và vì có vẻ như không ai thấy vấn đề này, tôi nghĩ mình sẽ viết blog về nó.

TL; Phiên bản DR

Tóm lại, hãy lưu ý rằng có một số trường hợp mà bạn không thể dựa vào STATISTICS IO để nói với bạn sự thật. Trong một số trường hợp (trường hợp này liên quan đến TOP và song song), nó sẽ báo cáo rất nhiều lần đọc lôgic. Điều này có thể khiến bạn tin rằng bạn có một truy vấn rất thân thiện với I / O khi bạn không có. Có những trường hợp khác rõ ràng hơn - chẳng hạn như khi bạn có một loạt I / O bị ẩn đi bằng cách sử dụng các hàm vô hướng do người dùng xác định. Chúng tôi nghĩ rằng Plan Explorer làm cho những trường hợp đó rõ ràng hơn; Tuy nhiên, cái này phức tạp hơn một chút.

Truy vấn vấn đề

Bảng có 37 triệu hàng, tối đa 250 byte mỗi hàng, khoảng 1 triệu trang và độ phân mảnh rất thấp (0,42% ở cấp 0, 15% ở cấp 1 và hơn thế nữa là 0). Không có cột được tính toán, không có UDF nào đang phát và không có chỉ mục nào ngoại trừ một khóa chính được phân cụm trên INT hàng đầu cột. Một truy vấn đơn giản trả về 500.000 hàng, tất cả các cột, sử dụng TOPSELECT * :

SET STATISTICS IO ON;
 
SELECT TOP 500000 * FROM dbo.OrderHistory 
WHERE OrderDate < (SELECT '19961029');

(Và có, tôi nhận ra rằng tôi đang vi phạm các quy tắc của riêng mình và sử dụng SELECT *TOP không có ORDER BY , nhưng vì mục đích đơn giản, tôi đang cố gắng hết sức để giảm thiểu ảnh hưởng của mình đến trình tối ưu hóa.)

Kết quả:

(500000 (các) hàng bị ảnh hưởng)
Bảng 'OrderHistory'. Quét đếm 1, đọc lôgic 23, đọc vật lý 0, đọc trước đọc 0, lôgic đọc 0, lôgic đọc 0, đọc trước lôgic đọc 0.

Chúng tôi đang trả lại 500.000 hàng và mất khoảng 10 giây. Tôi ngay lập tức biết rằng có điều gì đó không ổn với số lần đọc hợp lý. Ngay cả khi tôi chưa biết về dữ liệu cơ bản, tôi có thể cho biết từ kết quả lưới trong Management Studio rằng điều này đang kéo hơn 23 trang dữ liệu, cho dù chúng từ bộ nhớ hay bộ đệm và điều này sẽ được phản ánh ở đâu đó trong STATISTICS IO . Xem xét kế hoạch…

… Chúng tôi thấy sự song song ở đó và chúng tôi đã quét toàn bộ bảng. Vậy làm thế nào mà chỉ có 23 lần đọc logic?

Một truy vấn "giống hệt" khác

Một trong những câu hỏi đầu tiên của tôi đối với Steve là:"Điều gì sẽ xảy ra nếu bạn loại bỏ tính song song?" Vì vậy, tôi đã thử nó ra. Tôi đã lấy phiên bản truy vấn con ban đầu và thêm MAXDOP 1 :

SET STATISTICS IO ON;
 
SELECT TOP 500000 * FROM dbo.OrderHistory 
WHERE OrderDate < (SELECT '19961029') OPTION (MAXDOP 1);

Kết quả và kế hoạch:

(500000 (các) hàng bị ảnh hưởng)
Bảng 'OrderHistory'. Quét đếm 1, đọc lôgic 149589, đọc vật lý 0, đọc trước đọc 0, lôgic đọc 0, đọc lôgic vật lý 0, đọc trước lôgic đọc 0.

Chúng tôi có một kế hoạch ít phức tạp hơn một chút và không có sự song song (vì những lý do rõ ràng), STATISTICS IO đang cho chúng ta thấy những con số đáng tin cậy hơn đối với số lần đọc hợp lý.

Sự thật là gì?

Không khó để thấy rằng một trong những truy vấn này không nói lên toàn bộ sự thật. Trong khi STATISTICS IO có thể không cho chúng ta biết toàn bộ câu chuyện, có thể sẽ theo dõi. Nếu chúng tôi truy xuất các chỉ số thời gian chạy bằng cách tạo một kế hoạch thực thi thực tế trong Plan Explorer, chúng tôi thấy rằng trên thực tế, truy vấn đọc thấp kỳ diệu là lấy dữ liệu từ bộ nhớ hoặc đĩa chứ không phải từ một đám mây bụi ma thuật. Trên thực tế, nó có * nhiều hơn * lần đọc so với phiên bản khác:

Vì vậy, rõ ràng là các lần đọc đang diễn ra, chúng không xuất hiện chính xác trong STATISTICS IO đầu ra.

Vấn đề là gì?

Vâng, tôi sẽ thành thật mà nói:Tôi không biết, ngoài thực tế là sự song song chắc chắn đóng một vai trò nào đó, và nó dường như là một dạng điều kiện chủng tộc nào đó. STATISTICS IO (và, vì đó là nơi chúng tôi lấy dữ liệu, tab Bảng I / O của chúng tôi) hiển thị một số lần đọc rất sai lệch. Rõ ràng là truy vấn trả về tất cả dữ liệu mà chúng tôi đang tìm kiếm, và rõ ràng từ kết quả theo dõi rằng nó sử dụng phép đọc chứ không phải thẩm thấu để làm như vậy. Tôi đã hỏi Paul White (blog | @SQL_Kiwi) về điều đó và anh ấy gợi ý rằng chỉ một số I / O tiền chuỗi mới được đưa vào tổng số (và đồng ý rằng đây là một lỗi).

Nếu bạn muốn thử điều này tại nhà, tất cả những gì bạn cần là AdventureWorks (điều này sẽ chạy lại với các phiên bản 2008, 2008 R2 và 2012) và truy vấn sau:

SET STATISTICS IO ON;
DBCC SETCPUWEIGHT(1000) WITH NO_INFOMSGS;
GO
 
SELECT TOP (15000) * 
FROM Sales.SalesOrderHeader 
WHERE OrderDate < (SELECT '20080101');
 
SELECT TOP (15000) * 
FROM Sales.SalesOrderHeader 
WHERE OrderDate < (SELECT '20080101') 
OPTION (MAXDOP 1);
 
DBCC SETCPUWEIGHT(1) WITH NO_INFOMSGS;

(Lưu ý rằng SETCPUWEIGHT chỉ được sử dụng để đồng trục song song. Để biết thêm thông tin, hãy xem bài đăng trên blog của Paul White về Kế hoạch chi phí.)

Kết quả:

Bảng 'SalesOrderHeader'. Quét đếm 1, đọc lôgic 4, đọc vật lý 0, đọc trước đọc 0, đọc lôgic 0, thu đọc vật lý 0, đọc trước lob đọc 0.
Bảng 'SalesOrderHeader'. Quét đếm 1, đọc lôgic 333, đọc vật lý 0, đọc trước đọc 0, lôgic đọc 0, lôgic đọc 0, đọc trước lôgic đọc 0.

Paul đã chỉ ra một repro thậm chí còn đơn giản hơn:

SET STATISTICS IO ON;
GO
 
SELECT TOP (15000) *
FROM Production.TransactionHistory
WHERE TransactionDate < (SELECT '20080101')
OPTION (QUERYTRACEON 8649, MAXDOP 4);
 
SELECT TOP (15000) *
FROM Production.TransactionHistory AS th
WHERE TransactionDate < (SELECT '20080101');

Kết quả:

Bảng 'Lịch sử giao dịch'. Quét đếm 1, đọc lôgic 5, đọc vật lý 0, đọc trước đọc 0, lôgic đọc 0, tham chiếu vật lý 0, đọc trước lôgic đọc 0.
Bảng 'TransactionHistory'. Quét đếm 1, đọc lôgic 110, đọc vật lý 0, đọc trước đọc 0, lôgic đọc 0, ghi lôgic vật lý 0, đọc trước lôgic đọc 0.

Vì vậy, có vẻ như chúng ta có thể dễ dàng sao chép điều này theo ý muốn với TOP toán tử và DOP đủ thấp. Tôi đã gửi một lỗi:

  • THỐNG KÊ IO báo cáo dưới các lần đọc lôgic cho các kế hoạch song song

Và Paul đã đưa ra hai lỗi khác có liên quan đến tính song song, lỗi đầu tiên là kết quả của cuộc trò chuyện giữa chúng tôi:

  • Lỗi ước tính cấp số lượng với dự đoán được đẩy trên bản tra cứu [bài đăng trên blog có liên quan]
  • Hiệu suất kém với Song song và [bài đăng trên blog có liên quan] hàng đầu

(Đối với những người hoài cổ, đây là sáu lỗi song song khác mà tôi đã chỉ ra cách đây vài năm.)

Bài học là gì?

Hãy cẩn thận về việc tin tưởng một nguồn duy nhất. Nếu bạn chỉ nhìn vào STATISTICS IO sau khi thay đổi một truy vấn như thế này, bạn có thể bị cám dỗ tập trung vào sự sụt giảm kỳ diệu về lượt đọc thay vì sự gia tăng thời lượng. Tại thời điểm đó, bạn có thể tự vỗ về mình, rời khỏi công việc sớm và tận hưởng ngày cuối tuần của mình, nghĩ rằng bạn vừa tạo ra tác động to lớn đến hiệu suất cho truy vấn của mình. Tất nhiên, khi không thể có gì khác hơn sự thật.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thêm các tính năng nâng cao khác như quản lý danh mục và bỏ phiếu cho chủ đề và bài đăng

  2. API REST Python với Flask, Connexion và SQLAlchemy - Phần 3

  3. Cách tăng tốc truy vấn SQL

  4. Thiết kế cơ sở dữ liệu 101

  5. Thiết kế Cơ sở dữ liệu cho Cổng Thông tin Việc làm Trực tuyến