“Nhưng nó chạy tốt trên máy chủ phát triển của chúng tôi!”
Tôi đã nghe thấy nó bao nhiêu lần khi sự cố hiệu suất truy vấn SQL xảy ra ở đây và ở đó? Tôi đã tự nói điều đó vào ngày hôm đó. Tôi đã giả định rằng một truy vấn chạy trong vòng chưa đầy một giây sẽ chạy tốt trong máy chủ sản xuất. Nhưng tôi đã nhầm.
Bạn có thể liên hệ với kinh nghiệm này? Nếu bạn vẫn ở trên con thuyền này ngày hôm nay vì bất cứ lý do gì, thì bài đăng này là dành cho bạn. Nó sẽ cung cấp cho bạn một số liệu tốt hơn về việc tinh chỉnh hiệu suất truy vấn SQL của bạn. Chúng ta sẽ nói về ba trong số những nhân vật quan trọng nhất trong IO THỐNG KÊ.
Ví dụ, chúng tôi sẽ sử dụng cơ sở dữ liệu mẫu AdventureWorks.
Trước khi bạn bắt đầu chạy các truy vấn bên dưới, hãy bật STATISTICS IO. Dưới đây là cách thực hiện trong cửa sổ truy vấn:
USE AdventureWorks
GO
SET STATISTICS IO ON
Khi bạn chạy một truy vấn với STATISTICS IO ON, các thông báo khác nhau sẽ xuất hiện. Bạn có thể thấy những điều này trong tab Thông báo của cửa sổ truy vấn trong SQL Server Management Studio (xem Hình 1):
Bây giờ chúng ta đã hoàn tất phần giới thiệu ngắn, hãy cùng tìm hiểu sâu hơn.
1. Đọc logic cao
Điểm đầu tiên trong danh sách của chúng tôi là thủ phạm phổ biến nhất - số lần đọc logic cao.
Số lần đọc logic là số trang được đọc từ bộ nhớ đệm dữ liệu. Một trang có kích thước 8KB. Mặt khác, bộ nhớ cache dữ liệu đề cập đến RAM được sử dụng bởi SQL Server.
Đọc logic là rất quan trọng để điều chỉnh hiệu suất. Yếu tố này xác định lượng SQL Server cần để tạo ra tập kết quả cần thiết. Do đó, điều duy nhất cần nhớ là:các lần đọc logic càng cao thì SQL Server cần hoạt động càng lâu. Nó có nghĩa là truy vấn của bạn sẽ chậm hơn. Giảm số lần đọc logic và bạn sẽ tăng hiệu suất truy vấn của mình.
Nhưng tại sao lại sử dụng số lần đọc logic thay vì thời gian đã trôi qua?
- Thời gian đã trôi qua phụ thuộc vào những việc khác do máy chủ thực hiện, không chỉ riêng truy vấn của bạn.
- Thời gian đã trôi qua có thể thay đổi từ máy chủ phát triển sang máy chủ sản xuất. Điều này xảy ra khi cả hai máy chủ có dung lượng và cấu hình phần cứng và phần mềm khác nhau.
Dựa vào thời gian đã trôi qua sẽ khiến bạn nói, "Nhưng nó chạy tốt trong máy chủ phát triển của chúng tôi!" sớm hay muộn.
Tại sao lại sử dụng các phép đọc logic thay vì các phép đọc vật lý?
- Số lần đọc vật lý là số trang được đọc từ đĩa vào bộ đệm dữ liệu (trong bộ nhớ). Khi các trang cần thiết trong một truy vấn đã ở trong bộ nhớ đệm dữ liệu, bạn không cần phải đọc lại chúng từ đĩa.
- Khi chạy lại cùng một truy vấn, số lần đọc vật lý sẽ bằng không.
Đọc logic là lựa chọn hợp lý để tinh chỉnh hiệu suất truy vấn SQL.
Để thấy điều này trong thực tế, hãy tiếp tục với một ví dụ.
Ví dụ về số đọc logic
Giả sử bạn cần lấy danh sách các khách hàng có đơn đặt hàng được giao vào ngày 11 tháng 7 năm 2011. Bạn đưa ra truy vấn khá đơn giản dưới đây:
SELECT
d.FirstName
,d.MiddleName
,d.LastName
,d.Suffix
,a.OrderDate
,a.ShipDate
,a.Status
,b.ProductID
,b.OrderQty
,b.UnitPrice
FROM Sales.SalesOrderHeader a
INNER JOIN Sales.SalesOrderDetail b ON a.SalesOrderID = b.SalesOrderID
INNER JOIN Sales.Customer c ON a.CustomerID = c.CustomerID
INNER JOIN Person.Person d ON c.PersonID = D.BusinessEntityID
WHERE a.ShipDate = '07/11/2011'
Thật đơn giản. Truy vấn này sẽ có kết quả sau:
Sau đó, bạn kiểm tra kết quả IO THỐNG KÊ của truy vấn này:
Đầu ra hiển thị các lần đọc logic của từng bảng trong số bốn bảng được sử dụng trong truy vấn. Tổng cộng, tổng số lần đọc logic là 729. Bạn cũng có thể thấy các lần đọc vật lý với tổng số tiền là 21. Tuy nhiên, hãy thử chạy lại truy vấn và nó sẽ bằng không.
Hãy xem xét kỹ hơn các bài đọc hợp lý của SalesOrderHeader . Bạn có thắc mắc tại sao nó có 689 lần đọc logic không? Có lẽ, bạn đã nghĩ đến việc kiểm tra kế hoạch thực thi của truy vấn dưới đây:
Đối với một điều, có một quá trình quét chỉ mục đã xảy ra trong SalesOrderHeader với chi phí 93%. Điều gì có thể xảy ra? Giả sử bạn đã kiểm tra các thuộc tính của nó:
Ái chà! 31.465 hàng được đọc chỉ có 5 hàng được trả về? Thật vô lý!
Giảm số lần đọc logic
Không quá khó để giảm bớt 31.465 hàng được đọc. SQL Server đã cho chúng tôi một manh mối. Tiếp tục như sau:
BƯỚC 1:Thực hiện theo Khuyến nghị của Máy chủ SQL và Thêm Chỉ mục bị Thiếu
Bạn có nhận thấy khuyến nghị chỉ mục bị thiếu trong kế hoạch thực thi (Hình 4) không? Điều đó có khắc phục được sự cố không?
Có một cách để tìm hiểu:
CREATE NONCLUSTERED INDEX [IX_SalesOrderHeader_ShipDate]
ON [Sales].[SalesOrderHeader] ([ShipDate])
Chạy lại truy vấn và xem các thay đổi trong các lần đọc logic IO THỐNG KÊ.
Như bạn có thể thấy trong IO THỐNG KÊ (Hình 6), số lần đọc logic từ 689 xuống 17. Số lần đọc logic tổng thể mới là 57, đây là một cải tiến đáng kể so với 729 lần đọc logic. Nhưng để chắc chắn, hãy kiểm tra lại kế hoạch thực hiện.
Có vẻ như có một sự cải tiến trong kế hoạch dẫn đến giảm số lần đọc logic. Quét chỉ mục bây giờ là tìm kiếm chỉ mục. SQL Server sẽ không cần phải kiểm tra từng hàng để lấy các bản ghi với Shipdate =’07 / 11/2011 ′ . Nhưng có điều gì đó vẫn đang rình rập trong kế hoạch đó và nó không đúng.
Bạn cần bước 2.
BƯỚC 2:Thay đổi chỉ mục và thêm vào các cột được bao gồm:Ngày đặt hàng, Trạng thái và ID khách hàng
Bạn có thấy toán tử Tra cứu Khóa đó trong kế hoạch thực thi (Hình 7) không? Điều đó có nghĩa là chỉ mục không phân cụm đã tạo là không đủ - bộ xử lý truy vấn cần sử dụng lại chỉ mục được phân nhóm.
Hãy kiểm tra các thuộc tính của nó.
Lưu ý hộp kèm theo trong Danh sách đầu ra . Điều xảy ra là chúng tôi cần Ngày đặt hàng , Trạng thái và ID khách hàng trong tập kết quả. Để có được những giá trị đó, bộ xử lý truy vấn đã sử dụng chỉ mục được phân nhóm (Xem phần Tìm kiếm dự đoán ) để đến bàn.
Chúng ta cần loại bỏ Key Lookup đó. Giải pháp là bao gồm Ngày đặt hàng , Trạng thái và ID khách hàng vào chỉ mục đã tạo trước đó.
- Nhấp chuột phải vào IX_SalesOrderHeader_ShipDate trong SSMS.
- Chọn Thuộc tính .
- Nhấp vào Các cột được bao gồm tab.
- Thêm Ngày đặt hàng , Trạng thái và ID khách hàng .
- Nhấp vào OK .
Sau khi tạo lại chỉ mục, hãy chạy lại truy vấn. Điều này có xóa Tra cứu khóa không và giảm số lần đọc logic?
Nó đã làm việc! Từ 17 lần đọc logic xuống 2 (Hình 9).
Và Tra cứu khóa ?
Nó đi rồi! Tìm kiếm chỉ mục theo cụm đã thay thế Key Lookup.
Takeaway
Vậy, chúng ta đã học được gì?
Một trong những cách chính để giảm số lần đọc logic và cải thiện hiệu suất truy vấn SQL là tạo một chỉ mục thích hợp. Nhưng có một cơ hội. Trong ví dụ của chúng tôi, nó làm giảm số lần đọc logic. Đôi khi, điều ngược lại sẽ đúng. Nó cũng có thể ảnh hưởng đến hiệu suất của các truy vấn liên quan khác.
Do đó, hãy luôn kiểm tra IO THỐNG KÊ và kế hoạch thực thi sau khi tạo chỉ mục.
2. Số lần đọc logic cao
Nó cũng giống như điểm số 1, nhưng nó sẽ xử lý các loại dữ liệu văn bản , ntext , hình ảnh , varchar ( tối đa ), nvarchar ( tối đa ), varbinary ( tối đa ) hoặc columnstore các trang chỉ mục.
Hãy tham khảo một ví dụ:tạo các lần đọc logic lob.
Ví dụ về Số đọc lôgic của Lob
Giả sử bạn muốn hiển thị một sản phẩm với giá cả, màu sắc, hình ảnh thu nhỏ và hình ảnh lớn hơn trên trang web. Do đó, bạn đưa ra một truy vấn ban đầu như hình dưới đây:
SELECT
a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,b.Name AS ProductSubcategory
,d.ThumbNailPhoto
,d.LargePhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
ORDER BY ProductSubcategory, ProductName, a.Color
Sau đó, bạn chạy nó và thấy kết quả như bên dưới:
Vì bạn là một chàng trai (hoặc cô gái) có đầu óc làm việc hiệu quả cao, bạn ngay lập tức kiểm tra IO THỐNG KÊ. Đây rồi:
Cảm giác như có bụi bẩn trong mắt. 665 lần đọc logic? Bạn không thể chấp nhận điều này. Chưa kể 194 lần đọc logic mỗi từ ProductPhoto và ProductProductPhoto những cái bàn. Bạn thực sự nghĩ rằng truy vấn này cần một số thay đổi.
Giảm số lần đọc logic của Lob
Truy vấn trước đó có 97 hàng được trả về. Tất cả 97 xe đạp. Bạn có nghĩ rằng điều này là tốt để hiển thị trên một trang web?
Một chỉ mục có thể hữu ích, nhưng tại sao không đơn giản hóa truy vấn trước? Bằng cách này, bạn có thể lựa chọn những gì SQL Server sẽ trả về. Bạn có thể giảm số lần đọc lôgic.
- Thêm bộ lọc cho Danh mục phụ sản phẩm và để khách hàng chọn. Sau đó, bao gồm điều này trong mệnh đề WHERE.
- Xóa Danh mục sản phẩm vì bạn sẽ thêm bộ lọc cho Danh mục phụ sản phẩm.
- Xóa Ảnh chụp lớn cột. Truy vấn điều này khi người dùng chọn một sản phẩm cụ thể.
- Sử dụng phân trang. Khách hàng sẽ không thể xem tất cả 97 chiếc xe đạp cùng một lúc.
Dựa trên các thao tác được mô tả ở trên, chúng tôi thay đổi truy vấn như sau:
- Xóa ProductSubcategory và Ảnh lớn các cột từ tập kết quả.
- Sử dụng OFFSET và FETCH để điều chỉnh phân trang trong truy vấn. Chỉ truy vấn 10 sản phẩm cùng một lúc.
- Thêm ProductSubcategoryID trong mệnh đề WHERE dựa trên lựa chọn của khách hàng.
- Xóa Danh mục sản phẩm trong mệnh đề ORDER BY.
Truy vấn bây giờ sẽ tương tự như sau:
DECLARE @pageNumber TINYINT
DECLARE @noOfRows TINYINT = 10 -- each page will display 10 products at a time
SELECT
a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,d.ThumbNailPhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
AND a.ProductSubcategoryID = 2 -- Road Bikes
ORDER BY ProductName, a.Color
OFFSET (@pageNumber-1)*@noOfRows ROWS FETCH NEXT @noOfRows ROWS ONLY
-- change the OFFSET and FETCH values based on what page the user is.
Với những thay đổi được thực hiện, việc đọc logic của lob có được cải thiện không? THỐNG KÊ IO hiện báo cáo:
Ảnh chụp sản phẩm bảng hiện có 0 lần đọc lôgic - từ 665 lần đọc lôgic xuống không có. Đó là một số cải tiến.
Takeaway
Một trong những cách để giảm số lần đọc logic của lob là viết lại truy vấn để đơn giản hóa nó.
Loại bỏ các cột không cần thiết và giảm các hàng được trả về thành ít bắt buộc nhất. Khi cần thiết, hãy sử dụng OFFSET và FETCH để phân trang.
Để đảm bảo rằng các thay đổi truy vấn đã cải thiện lượt đọc logic lob và hiệu suất truy vấn SQL, hãy luôn kiểm tra THỐNG KÊ IO.
3. Số lần đọc logic cao của Workfile / Workfile
Cuối cùng, đó là cách đọc hợp lý của Bàn làm việc và Hồ sơ công việc . Nhưng những bảng này là gì? Tại sao chúng xuất hiện khi bạn không sử dụng chúng trong truy vấn của mình?
Có Bàn làm việc và Hồ sơ công việc xuất hiện trong IO THỐNG KÊ có nghĩa là SQL Server cần nhiều công việc hơn để có được kết quả mong muốn. Nó sử dụng các bảng tạm thời trong tempdb , cụ thể là Bàn làm việc và Hồ sơ công việc . Việc có chúng trong đầu ra IO THỐNG KÊ không nhất thiết là có hại, miễn là số lần đọc logic bằng 0 và nó không gây ra sự cố cho máy chủ.
Các bảng này có thể xuất hiện khi có ORDER BY, GROUP BY, CROSS JOIN hoặc DISTINCT, trong số những bảng khác.
Ví dụ về Worktable / Workfile Logical Reads
Giả sử rằng bạn cần truy vấn tất cả các cửa hàng không bán một số sản phẩm nhất định.
Ban đầu bạn nghĩ ra những điều sau:
SELECT DISTINCT
a.SalesPersonID
,b.ProductID
,ISNULL(c.OrderTotal,0) AS OrderTotal
FROM Sales.Store a
CROSS JOIN Production.Product b
LEFT JOIN (SELECT
b.SalesPersonID
,a.ProductID
,SUM(a.LineTotal) AS OrderTotal
FROM Sales.SalesOrderDetail a
INNER JOIN Sales.SalesOrderHeader b ON a.SalesOrderID = b.SalesOrderID
WHERE b.SalesPersonID IS NOT NULL
GROUP BY b.SalesPersonID, a.ProductID, b.OrderDate) c ON a.SalesPersonID
= c.SalesPersonID
AND b.ProductID = c.ProductID
WHERE c.OrderTotal IS NULL
ORDER BY a.SalesPersonID, b.ProductID
Truy vấn này trả về 3649 hàng:
Hãy kiểm tra xem IO THỐNG KÊ nói gì:
Cần lưu ý rằng Bàn làm việc số lần đọc logic là 7128. Số lần đọc logic tổng thể là 8853. Nếu bạn kiểm tra kế hoạch thực thi, bạn sẽ thấy rất nhiều phép song song, khớp băm, cuộn chỉ và quét chỉ mục.
Giảm số đọc logic của Workfile / Workfile
Tôi không thể tạo một câu lệnh SELECT duy nhất với kết quả thỏa mãn. Do đó, sự lựa chọn duy nhất là chia câu lệnh SELECT thành nhiều truy vấn. Xem bên dưới:
SELECT DISTINCT
a.SalesPersonID
,b.ProductID
INTO #tmpStoreProducts
FROM Sales.Store a
CROSS JOIN Production.Product b
SELECT
b.SalesPersonID
,a.ProductID
,SUM(a.LineTotal) AS OrderTotal
INTO #tmpProductOrdersPerSalesPerson
FROM Sales.SalesOrderDetail a
INNER JOIN Sales.SalesOrderHeader b ON a.SalesOrderID = b.SalesOrderID
WHERE b.SalesPersonID IS NOT NULL
GROUP BY b.SalesPersonID, a.ProductID
SELECT
a.SalesPersonID
,a.ProductID
FROM #tmpStoreProducts a
LEFT JOIN #tmpProductOrdersPerSalesPerson b ON a.SalesPersonID = b.SalesPersonID AND
a.ProductID = b.ProductID
WHERE b.OrderTotal IS NULL
ORDER BY a.SalesPersonID, a.ProductID
DROP TABLE #tmpProductOrdersPerSalesPerson
DROP TABLE #tmpStoreProducts
Nó dài hơn vài dòng và sử dụng các bảng tạm thời. Bây giờ, hãy xem IO THỐNG KÊ tiết lộ điều gì:
Cố gắng không tập trung vào độ dài báo cáo thống kê này - điều đó chỉ gây khó chịu. Thay vào đó, hãy thêm các lần đọc logic từ mỗi bảng.
Đối với tổng số 1279, đó là một sự giảm đáng kể, vì nó là 8853 lần đọc logic từ một câu lệnh SELECT.
Chúng tôi chưa thêm bất kỳ chỉ mục nào vào các bảng tạm thời. Bạn có thể cần một bản ghi nếu thêm nhiều bản ghi vào SalesOrderHeader và SalesOrderDetail . Nhưng bạn hiểu rõ.
Takeaway
Đôi khi 1 câu lệnh SELECT xuất hiện tốt. Tuy nhiên, đằng sau hậu trường, điều ngược lại mới là sự thật. Bàn làm việc và Hồ sơ công việc với số lần đọc logic cao làm tụt hậu hiệu suất truy vấn SQL của bạn.
Nếu bạn không thể nghĩ ra cách khác để tạo lại truy vấn và các chỉ mục là vô dụng, hãy thử phương pháp "chia và chinh phục". Bảng làm việc và Hồ sơ công việc vẫn có thể xuất hiện trong tab Thông báo của SSMS, nhưng số lần đọc logic sẽ bằng không. Do đó, kết quả tổng thể sẽ là những lần đọc ít logic hơn.
Điểm mấu chốt trong Hiệu suất Truy vấn SQL và THỐNG KÊ IO
Vấn đề lớn là gì với 3 thống kê I / O khó chịu này?
Sự khác biệt về hiệu suất truy vấn SQL sẽ giống như đêm và ngày nếu bạn chú ý đến những con số này và hạ thấp chúng. Chúng tôi chỉ trình bày một số cách để giảm số lần đọc hợp lý như:
- tạo các chỉ mục thích hợp;
- đơn giản hóa các truy vấn - loại bỏ các cột không cần thiết và giảm thiểu tập hợp kết quả;
- chia nhỏ một truy vấn thành nhiều truy vấn.
Có nhiều thứ khác như cập nhật thống kê, chống phân mảnh chỉ mục và đặt FILLFACTOR phù hợp. Bạn có thể bổ sung thêm về vấn đề này trong phần bình luận không?
Nếu bạn thích bài đăng này, hãy chia sẻ nó với phương tiện truyền thông xã hội yêu thích của bạn.