Gần đây, tôi đã nhận được câu hỏi qua email từ một người nào đó trong cộng đồng về CLR_MANUAL_EVENT loại chờ đợi; cụ thể là cách khắc phục sự cố với sự chờ đợi này đột nhiên trở nên phổ biến đối với khối lượng công việc hiện có chủ yếu dựa vào các kiểu dữ liệu không gian và truy vấn bằng cách sử dụng các phương thức không gian trong SQL Server.
Là một nhà tư vấn, câu hỏi đầu tiên của tôi hầu như luôn luôn là, "Điều gì đã thay đổi?" Nhưng trong trường hợp này, cũng như rất nhiều trường hợp, tôi được đảm bảo rằng không có gì thay đổi với mã của ứng dụng hoặc các mẫu khối lượng công việc. Vì vậy, điểm dừng đầu tiên của tôi là tăng CLR_MANUAL_EVENT hãy đợi trong Thư viện kiểu chờ của SQLskills.com để xem những thông tin khác mà chúng tôi đã thu thập về kiểu chờ này, vì tôi thấy vấn đề trong SQL Server không phải là trường hợp chờ đợi. Điều tôi thấy thực sự thú vị là biểu đồ / bản đồ nhiệt về các lần xuất hiện cho kiểu chờ đợi này do SentryOne cung cấp ở đầu trang:
Thực tế là không có dữ liệu nào được thu thập cho loại hình này trong suốt một phần khách hàng tốt của họ đã thực sự xác nhận với tôi rằng đây không phải là vấn đề thường gặp, vì vậy tôi bị thu hút bởi thực tế là khối lượng công việc cụ thể này hiện đang được trưng bày vấn đề với sự chờ đợi này. Tôi không biết phải đi đâu để điều tra thêm vấn đề vì vậy tôi đã trả lời email nói rằng tôi rất tiếc vì tôi không thể giúp gì thêm vì tôi không biết điều gì sẽ gây ra hàng chục chuỗi thực hiện các truy vấn không gian theo đúng nghĩa đen. đột ngột bắt đầu phải đợi 2-4 giây cùng một lúc với kiểu chờ này.
Một ngày sau, tôi nhận được một email theo dõi tử tế từ người đặt câu hỏi thông báo cho tôi rằng họ đã giải quyết được vấn đề. Thật vậy, không có gì trong khối lượng công việc ứng dụng thực tế đã thay đổi, nhưng có một sự thay đổi đối với môi trường đã xảy ra. Một gói phần mềm của bên thứ ba đã được nhóm bảo mật của họ cài đặt trên tất cả các máy chủ trong cơ sở hạ tầng của họ và phần mềm này đang thu thập dữ liệu trong khoảng thời gian 5 phút và khiến cho quá trình xử lý thu gom rác .NET chạy cực kỳ mạnh mẽ và “dở chứng” như họ nói rằng. Được trang bị thông tin này và một số kiến thức trước đây của tôi về phát triển .NET, tôi quyết định muốn tìm hiểu một số điều này và xem liệu tôi có thể tái tạo hành vi và cách chúng ta có thể khắc phục sự cố thêm các nguyên nhân hay không.
Thông tin cơ sở
Trong nhiều năm, tôi đã luôn theo dõi Blog PSSQL trên MSDN và đó thường là một trong những địa điểm tôi đến khi tôi nhớ lại rằng tôi đã đọc về một vấn đề liên quan đến SQL Server tại một thời điểm nào đó nhưng tôi có thể ' t nhớ tất cả các chi tiết cụ thể.
Có một bài đăng trên blog có tiêu đề Sự chờ đợi cao trên CLR_MANUAL_EVENT và CLR_AUTO_EVENT của Jack Li từ năm 2008 giải thích lý do tại sao những sự chờ đợi này có thể được bỏ qua một cách an toàn trong sys.dm_os_wait_stats tổng hợp DMV vì các lần chờ xảy ra trong điều kiện bình thường, nhưng nó không giải quyết việc phải làm nếu thời gian chờ quá lâu hoặc điều gì có thể khiến chúng được nhìn thấy trên nhiều chuỗi trong sys.dm_os_waiting_tasks tích cực.
Có một bài đăng trên blog khác của Jack Li từ năm 2013 có tiêu đề Một vấn đề về hiệu suất liên quan đến việc thu gom rác CLR và cài đặt mối quan hệ CPU SQL mà tôi tham khảo trong lớp điều chỉnh hiệu suất IEPTO2 của chúng tôi khi tôi nói về các cân nhắc về nhiều phiên bản và cách Trình thu gom rác .NET (GC) được kích hoạt bởi một phiên bản có thể tác động đến các phiên bản khác trên cùng một máy chủ.
GC trong .NET tồn tại để giảm mức sử dụng bộ nhớ của các ứng dụng sử dụng CLR bằng cách cho phép tự động dọn dẹp bộ nhớ được phân bổ cho các đối tượng, do đó loại bỏ sự cần thiết của các nhà phát triển phải xử lý thủ công cấp phát bộ nhớ và phân bổ bộ nhớ ở mức độ yêu cầu của mã không được quản lý . Chức năng GC được ghi lại trong Sách trực tuyến nếu bạn muốn biết thêm về cách hoạt động của nó, nhưng các chi tiết cụ thể ngoài thực tế là các bộ sưu tập có thể bị chặn không quan trọng đối với việc khắc phục sự cố chờ hoạt động trên CLR_MANUAL_EVENT trong SQL Server hơn nữa.
Tìm hiểu gốc rễ của vấn đề
Với sự hiểu biết rằng thu thập rác bằng .NET là nguyên nhân gây ra sự cố, tôi quyết định thực hiện một số thử nghiệm bằng cách sử dụng một truy vấn không gian duy nhất chống lại AdventureWorks2016 và một tập lệnh PowerShell rất đơn giản để gọi trình thu gom rác theo cách thủ công trong một vòng lặp để theo dõi những gì xảy ra trong sys.dm_os_waiting_tasks bên trong SQL Server cho truy vấn:
USE AdventureWorks2016; GO SELECT a.SpatialLocation.ToString(), a.City, b.SpatialLocation.ToString(), b.City FROM Person.Address AS a INNER JOIN Person.Address AS b ON a.SpatialLocation.STDistance(b.SpatialLocation) <= 100 ORDER BY a.SpatialLocation.STDistance(b.SpatialLocation);
Truy vấn này so sánh tất cả các địa chỉ trong Person.Address bàn đối diện nhau để tìm bất kỳ địa chỉ nào cách địa chỉ khác trong bảng 100 mét. Điều này tạo ra một tác vụ song song chạy dài bên trong SQL Server cũng tạo ra một kết quả Descartes lớn. Nếu bạn quyết định tự tái tạo hành vi này, đừng mong đợi quá trình này hoàn tất hoặc trả lại kết quả. Khi truy vấn đang chạy, chuỗi mẹ cho tác vụ bắt đầu đợi trên CXPACKET đợi và truy vấn tiếp tục xử lý trong vài phút. Tuy nhiên, điều tôi quan tâm là điều gì sẽ xảy ra khi thu thập rác xảy ra trong thời gian chạy CLR hoặc nếu GC được gọi, vì vậy tôi đã sử dụng một tập lệnh PowerShell đơn giản sẽ lặp lại và buộc GC chạy theo cách thủ công.
LƯU Ý:ĐÂY KHÔNG PHẢI LÀ THỰC HÀNH ĐƯỢC KHUYẾN CÁO TRONG MÃ SẢN XUẤT VÌ NHIỀU LÝ DO!
while (1 -eq 1) {[System.GC]::Collect() }
Khi cửa sổ PowerShell đang chạy, tôi gần như ngay lập tức bắt đầu thấy CLR_MANUAL_EVENT chờ xảy ra trên các chuỗi nhiệm vụ con song song (được hiển thị bên dưới, trong đó tệp executive_context_id lớn hơn 0) trong sys.dm_os_waiting_tasks :
Bây giờ tôi có thể kích hoạt hành vi này và bắt đầu trở nên rõ ràng rằng SQL Server không nhất thiết phải là vấn đề ở đây và có thể chỉ là nạn nhân của hoạt động khác, tôi muốn biết cách tìm hiểu sâu hơn và xác định nguyên nhân gốc rễ của vấn đề . Đây là lúc PerfMon có ích để theo dõi nhóm bộ đếm .NET CLR Bộ nhớ cho tất cả các tác vụ trên máy chủ.
Ảnh chụp màn hình này đã được giảm bớt để hiển thị các bộ sưu tập cho sqlservr và powershell như các ứng dụng so với _Global_ bộ sưu tập theo thời gian chạy .NET. Bằng cách buộc GC.Collect () để chạy liên tục, chúng ta có thể thấy rằng powershell ví dụ đang điều khiển các bộ sưu tập GC trên máy chủ. Sử dụng nhóm bộ đếm PerfMon này, chúng tôi có thể theo dõi những ứng dụng nào đang thực hiện nhiều bộ sưu tập nhất và từ đó tiếp tục điều tra thêm về vấn đề. Đối với trường hợp này, chỉ cần dừng tập lệnh PowerShell sẽ loại bỏ CLR_MANUAL_EVENT đợi bên trong SQL Server và truy vấn tiếp tục xử lý cho đến khi chúng tôi dừng nó hoặc cho phép nó trả về hàng tỷ hàng kết quả sẽ được xuất ra bởi nó.
Kết luận
Nếu bạn có lượt chờ đang hoạt động cho CLR_MANUAL_EVENT gây chậm ứng dụng, không tự động cho rằng sự cố tồn tại bên trong SQL Server. SQL Server sử dụng tính năng thu gom rác cấp máy chủ (ít nhất là trước SQL Server 2017 CU4, nơi các máy chủ nhỏ có RAM dưới 2GB có thể sử dụng thu gom rác cấp máy khách để giảm mức sử dụng tài nguyên). Nếu bạn thấy sự cố này xảy ra trong SQL Server, hãy sử dụng nhóm bộ đếm .NET CLR Bộ nhớ trong PerfMon và kiểm tra xem liệu ứng dụng khác có đang thúc đẩy thu gom rác trong CLR và kết quả là chặn các tác vụ CLR nội bộ trong SQL Server hay không.