Rõ ràng, chức năng lắp ráp CLR của tôi đang gây ra bế tắc?


9

Ứng dụng của chúng tôi cần hoạt động tốt như nhau với cơ sở dữ liệu Oracle hoặc cơ sở dữ liệu Microsoft SQL Server. Để tạo điều kiện cho điều này, chúng tôi đã tạo ra một số UDF để đồng nhất hóa cú pháp truy vấn của chúng tôi. Ví dụ: SQL Server có GETDATE () và Oracle có SYSDATE. Chúng thực hiện cùng chức năng nhưng chúng là những từ khác nhau. Chúng tôi đã viết một UDF trình bao bọc có tên NOW () cho cả hai nền tảng bao gồm cú pháp cụ thể của nền tảng có liên quan trong một tên hàm phổ biến. Chúng tôi có các chức năng khác như vậy, một số trong đó về cơ bản không làm gì ngoài việc tồn tại chỉ vì mục đích đồng nhất hóa. Thật không may, điều này có chi phí cho SQL Server. Các UDF vô hướng nội tuyến tàn phá hiệu năng và vô hiệu hóa hoàn toàn song song. Thay vào đó, chúng tôi đã viết các hàm lắp ráp CLR để thực hiện các mục tiêu tương tự. Khi chúng tôi triển khai nó cho khách hàng, họ bắt đầu gặp bế tắc thường xuyên. Khách hàng cụ thể này đang sử dụng các kỹ thuật nhân rộng và tính sẵn sàng cao và tôi tự hỏi liệu có một số loại tương tác đang diễn ra ở đây không. Tôi chỉ không hiểu làm thế nào để giới thiệu một chức năng CLR sẽ gây ra vấn đề như thế này. Để tham khảo, tôi đã bao gồm định nghĩa UDF vô hướng ban đầu cũng như định nghĩa CLR thay thế trong C # và khai báo SQL cho nó. Tôi cũng có XML bế tắc mà tôi có thể cung cấp nếu điều đó có ích.

UDF gốc

CREATE FUNCTION [fn].[APAD]
(
    @Value VARCHAR(4000)
    , @tablename VARCHAR(4000) = NULL
    , @columnname VARCHAR(4000) = NULL
)

RETURNS VARCHAR(4000)
WITH SCHEMABINDING
AS

BEGIN
    RETURN LTRIM(RTRIM(@Value))
END
GO

Chức năng hội CLR

[SqlFunction(IsDeterministic = true)]
public static string APAD(string value, string tableName, string columnName)
{
    return value?.Trim();
}

Khai báo SQL Server cho chức năng CLR

CREATE FUNCTION [fn].[APAD]
(
    @Value NVARCHAR(4000),
    @TableName NVARCHAR(4000),
    @ColumnName NVARCHAR(4000)
) RETURNS NVARCHAR(4000)
AS
EXTERNAL NAME ASI.fn.APAD
GO

9
Các hàm CLR vô hướng xác định sẽ không đóng góp vào các khóa chết. Tất nhiên các hàm CLR đọc cơ sở dữ liệu có thể. Bạn nên bao gồm XML bế tắc trong câu hỏi của bạn.
David Browne - Microsoft

Câu trả lời:


7

Phiên bản SQL Server nào bạn đang sử dụng?

Tôi nhớ lại đã thấy một sự thay đổi nhỏ trong hành vi trong SQL Server 2017 cách đây không lâu. Tôi sẽ phải quay lại và xem liệu tôi có thể tìm thấy nơi tôi đã ghi chú về nó không, nhưng tôi nghĩ nó phải làm với khóa lược đồ được bắt đầu khi một đối tượng SQLCLR được truy cập.

Trong khi tôi đang tìm kiếm điều đó, tôi sẽ nói như sau về cách tiếp cận của bạn:

  1. Vui lòng sử dụng các Sql*loại cho các tham số đầu vào, các loại trả lại. Bạn nên sử dụng SqlStringthay vì string. SqlStringrất giống với một chuỗi nullable (của bạn value?, nhưng nó có chức năng khác được xây dựng trong đó là dành riêng cho SQL Server. Tất cả các Sql*loại đều có thuộc Valuetính trả về loại .NET dự kiến ​​(ví dụ: SqlString.Valuetrả về string, SqlInt32trả về int, SqlDateTimetrả về DateTime, v.v.).
  2. Tôi sẽ đề nghị chống lại toàn bộ cách tiếp cận này để bắt đầu, cho dù các bế tắc có liên quan hay không. Tôi nói điều này bởi vì:

    1. Ngay cả với các UDF SQLCLR xác định có thể tham gia vào các kế hoạch song song, rất có thể bạn sẽ nhận được các lần truy cập hiệu năng để mô phỏng các hàm dựng sẵn đơn giản.
    2. API SQLCLR không cho phép VARCHAR. Bạn có ổn với việc chuyển đổi hoàn toàn mọi thứ sang NVARCHARvà sau đó quay lại VARCHARhoạt động đơn giản không?
    3. API SQLCLR không cho phép quá tải, do đó bạn có thể cần nhiều phiên bản hàm cho phép các chữ ký khác nhau trong T-SQL và / hoặc PL / SQL.
    4. Tương tự như không cho phép quá tải, có một sự khác biệt lớn giữa NVARCHAR(4000)NVARCHAR(MAX): MAXloại (thậm chí chỉ có một trong số chúng trong chữ ký) làm cho lệnh gọi SQLCLR mất gấp đôi thời gian không có bất kỳ MAXloại nào trong chữ ký (tôi tin rằng điều này giữ đúng cho VARBINARY(MAX)vs VARBINARY(4000)là tốt). Vì vậy, bạn cần phải quyết định giữa:
      • chỉ sử dụng NVARCHAR(MAX)để có API đơn giản hóa, nhưng thực hiện cú đánh hiệu suất khi bạn đang sử dụng 8000 byte hoặc ít hơn dữ liệu chuỗi hoặc
      • tạo hai biến thể cho tất cả / hầu hết / nhiều hàm chuỗi: một có MAXloại và một không có (khi bạn được đảm bảo không bao giờ vượt quá 8000 byte dữ liệu chuỗi vào hoặc ra). Đây là cách tiếp cận tôi chọn để thực hiện cho hầu hết các hàm trong thư viện SQL # của mình : có một Trim()hàm có thể có một hoặc nhiều MAXloại và một Trim4k()phiên bản không bao giờ có MAXloại nào trong lược đồ tập hợp chữ ký hoặc kết quả. Các phiên bản "4k" hoàn toàn hiệu quả hơn.
    5. Bạn không cẩn thận để mô phỏng chức năng được đưa ra ví dụ trong câu hỏi. LTRIMRTRIMchỉ cắt các khoảng trắng, trong khi .NET cắt bớt khoảng String.Trim()trắng (ít nhất là khoảng trắng, tab và dòng mới). Ví dụ:

        PRINT LTRIM(RTRIM(N'      a       '));
    6. Ngoài ra, tôi chỉ nhận thấy rằng hàm của bạn, cả trong T-SQL và C #, chỉ sử dụng 1 trong 3 tham số đầu vào. Đây chỉ là một bằng chứng về khái niệm, hoặc mã được tái cấu trúc?

1. Cảm ơn về mẹo sử dụng các loại Sql. Tôi sẽ thực hiện thay đổi đó ngay bây giờ. 2. Có những lực lượng bên ngoài làm việc ở đây đòi hỏi phải sử dụng chúng. Tôi không vui mừng về điều đó nhưng tin tưởng tôi, nó tốt hơn so với giải pháp thay thế. Câu hỏi ban đầu của tôi chứa một chút giải thích về lý do tại sao một chức năng dường như asinine tồn tại và đang được sử dụng.
Russ Suter

@RussSuter Hiểu lại: ngoại lực. Tôi chỉ chỉ ra một số cạm bẫy có thể chưa được biết khi quyết định đó được đưa ra. Dù bằng cách nào, tôi không thể tìm thấy ghi chú của mình hoặc tái tạo kịch bản từ một vài chi tiết tôi nhớ về nó. Tôi chỉ nhớ một cái gì đó chắc chắn thay đổi trong năm 2017 liên quan đến các giao dịch và gọi mã từ một hội đồng, và thực sự khó chịu với nó vì nó dường như là một thay đổi không cần thiết cho điều tồi tệ hơn, và tôi đã phải làm việc xung quanh nó để thử nghiệm những gì tôi đã thử nghiệm tốt trong các phiên bản trước. Vì vậy, xin vui lòng gửi một liên kết trong câu hỏi đến XML bế tắc.
Solomon Rutzky

Cảm ơn thông tin bổ sung đó. Đây là một liên kết đến XML: dropbox.com/s/n9w8nsdojqdypqm/deadlock17.xml?dl=0
Russ Suter

@RussSuter Bạn đã thử điều này với nội tuyến T-SQL chưa? Nhìn vào XML bế tắc (không dễ vì nó là một dòng duy nhất - tất cả các dòng mới đã bị xóa bằng cách nào đó) dường như là một loạt các khóa TRANG giữa các phiên 60 và 78. Có 8 trang bị khóa giữa cả hai phiên: 3 cho một SPID và 5 cho cái khác. Mỗi người có một ID tiến trình khác nhau, vì vậy đây là vấn đề song song. Nếu điều này có liên quan đến SQLCLR, thì có thể trớ trêu thay thực tế là SQLCLR không ngăn chặn sự song song. Đây là lý do tại sao tôi hỏi nếu bạn đã thử đặt chức năng đơn giản nội tuyến vì điều đó cũng có thể hiển thị bế tắc.
Solomon Rutzky
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.