Tôi có Cơ sở dữ liệu Azure SQL cung cấp ứng dụng .NET Core API. Duyệt các báo cáo tổng quan về hiệu suất trong Cổng thông tin Azure cho thấy phần lớn tải (sử dụng DTU) trên máy chủ cơ sở dữ liệu của tôi đến từ CPU và một truy vấn cụ thể:
Như chúng ta có thể thấy, truy vấn 3780 chịu trách nhiệm cho gần như tất cả việc sử dụng CPU trên máy chủ.
Điều này phần nào có ý nghĩa, vì truy vấn 3780 (xem bên dưới) về cơ bản là toàn bộ mấu chốt của ứng dụng và được người dùng gọi khá thường xuyên. Đây cũng là một truy vấn khá phức tạp với nhiều tham gia cần thiết để có được bộ dữ liệu phù hợp cần thiết. Truy vấn đến từ một sproc cuối cùng trông như thế này:
-- @UserId UNIQUEIDENTIFIER
SELECT
C.[Id],
C.[UserId],
C.[OrganizationId],
C.[Type],
C.[Data],
C.[Attachments],
C.[CreationDate],
C.[RevisionDate],
CASE
WHEN
@UserId IS NULL
OR C.[Favorites] IS NULL
OR JSON_VALUE(C.[Favorites], CONCAT('$."', @UserId, '"')) IS NULL
THEN 0
ELSE 1
END [Favorite],
CASE
WHEN
@UserId IS NULL
OR C.[Folders] IS NULL
THEN NULL
ELSE TRY_CONVERT(UNIQUEIDENTIFIER, JSON_VALUE(C.[Folders], CONCAT('$."', @UserId, '"')))
END [FolderId],
CASE
WHEN C.[UserId] IS NOT NULL OR OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 OR G.[AccessAll] = 1 OR CG.[ReadOnly] = 0 THEN 1
ELSE 0
END [Edit],
CASE
WHEN C.[UserId] IS NULL AND O.[UseTotp] = 1 THEN 1
ELSE 0
END [OrganizationUseTotp]
FROM
[dbo].[Cipher] C
LEFT JOIN
[dbo].[Organization] O ON C.[UserId] IS NULL AND O.[Id] = C.[OrganizationId]
LEFT JOIN
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId
LEFT JOIN
[dbo].[CollectionCipher] CC ON C.[UserId] IS NULL AND OU.[AccessAll] = 0 AND CC.[CipherId] = C.[Id]
LEFT JOIN
[dbo].[CollectionUser] CU ON CU.[CollectionId] = CC.[CollectionId] AND CU.[OrganizationUserId] = OU.[Id]
LEFT JOIN
[dbo].[GroupUser] GU ON C.[UserId] IS NULL AND CU.[CollectionId] IS NULL AND OU.[AccessAll] = 0 AND GU.[OrganizationUserId] = OU.[Id]
LEFT JOIN
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
LEFT JOIN
[dbo].[CollectionGroup] CG ON G.[AccessAll] = 0 AND CG.[CollectionId] = CC.[CollectionId] AND CG.[GroupId] = GU.[GroupId]
WHERE
C.[UserId] = @UserId
OR (
C.[UserId] IS NULL
AND OU.[Status] = 2
AND O.[Enabled] = 1
AND (
OU.[AccessAll] = 1
OR CU.[CollectionId] IS NOT NULL
OR G.[AccessAll] = 1
OR CG.[CollectionId] IS NOT NULL
)
)
Nếu bạn quan tâm, nguồn đầy đủ cho cơ sở dữ liệu này có thể được tìm thấy trên GitHub tại đây . Nguồn từ truy vấn trên:
- https://github.com/bitwarden/core/blob/master/src/Sql/dbo/Stored%20Procedures/CodesDetails_ReadByUserId.sql
- https://github.com/bitwarden/core/blob/master/src/Sql/dbo/Fiances/UserCodesDetails.sql
- https://github.com/bitwarden/core/blob/master/src/Sql/dbo/Fifts/CodesDetails.sql
Tôi đã dành một chút thời gian cho truy vấn này trong nhiều tháng để điều chỉnh kế hoạch thực hiện theo cách tốt nhất mà tôi biết, kết thúc với trạng thái hiện tại. Các truy vấn với kế hoạch thực hiện này nhanh chóng trên hàng triệu hàng (<1 giây), nhưng như đã lưu ý ở trên, đang ăn CPU của máy chủ ngày càng nhiều khi ứng dụng tăng kích thước.
Tôi đã đính kèm kế hoạch truy vấn thực tế bên dưới (không chắc chắn về bất kỳ cách nào khác để chia sẻ rằng ở đây trên trao đổi ngăn xếp), trong đó cho thấy một thực thi của sproc trong sản xuất đối với dữ liệu được trả về ~ 400 kết quả.
Một số điểm tôi đang tìm kiếm làm rõ về:
Chỉ số Tìm kiếm
[IX_Cipher_UserId_Type_IncludeAll]
chiếm 57% tổng chi phí của kế hoạch. Sự hiểu biết của tôi về kế hoạch là chi phí này có liên quan đến IO, do bảng Mã hóa chứa hàng triệu bản ghi. Tuy nhiên, báo cáo hiệu suất Azure SQL đang cho tôi thấy rằng các vấn đề của tôi xuất phát từ CPU trên truy vấn này, không phải IO, vì vậy tôi không chắc liệu đây có thực sự là vấn đề hay không. Thêm vào đó, nó đã thực hiện một chỉ mục tìm kiếm ở đây, vì vậy tôi không thực sự chắc chắn có bất kỳ chỗ nào để cải thiện.Các hoạt động Hash Match từ tất cả các phép nối dường như là những gì đang cho thấy việc sử dụng CPU đáng kể trong kế hoạch (tôi nghĩ vậy?), Nhưng tôi không thực sự chắc chắn làm thế nào điều này có thể được làm tốt hơn. Bản chất phức tạp của cách tôi cần để có được dữ liệu cần rất nhiều phép nối trên một số bảng. Tôi đã ngắn mạch nhiều trong số các phép nối này nếu có thể (dựa trên kết quả từ lần tham gia trước) trong các
ON
mệnh đề của chúng .
Tải xuống gói thực hiện đầy đủ tại đây: https://www.dropbox.com/s/lua1awsc0uz1lo9/CodesDetails_ReadByUserId.sqlplan?dl=0
Tôi cảm thấy mình có thể đạt được hiệu năng CPU tốt hơn từ truy vấn này, nhưng tôi đang ở giai đoạn mà tôi không chắc chắn làm thế nào để tiếp tục điều chỉnh kế hoạch thực hiện nữa. Những tối ưu hóa nào khác có thể phải giảm tải CPU? Những hoạt động nào trong kế hoạch thực hiện là những người phạm tội tồi tệ nhất trong việc sử dụng CPU?
UNION ALL
(một choC.[UserId] = @UserId
và một choC.[UserId] IS NULL AND ...
). Điều này làm giảm các tập kết quả nối và loại bỏ hoàn toàn nhu cầu băm khớp (hiện đang thực hiện các vòng lặp lồng nhau trên các tập hợp nhỏ). Các truy vấn bây giờ tốt hơn nhiều trên CPU. Cảm ơn bạn!