Điều này dường như là một bản sao của câu hỏi này:
Thiết lập thư viện lưu trữ thủ tục / hàm lưu trữ CLR trung tâm cho các procs được lưu trữ nội bộ trong các cơ sở dữ liệu khác để sử dụng?
Tuy nhiên, tôi không cảm thấy rằng một trong hai câu trả lời là đầy đủ vì chúng không đề cập đến một số khía cạnh quan trọng hơn của câu hỏi này.
Ở đây không có lựa chọn rõ ràng nào về vị trí nào tốt hơn cho các đối tượng SQLCLR nói chung vì có thể có các ràng buộc áp đặt bởi những gì đang được thực hiện với mã SQLCLR. Có một số cách sử dụng sẽ yêu cầu Hội phải ở trong mỗi cơ sở dữ liệu riêng lẻ và một lần sử dụng sẽ yêu cầu Hội phải ở trong cơ sở dữ liệu tập trung. Tất cả phụ thuộc vào một vài khía cạnh khác nhau của những gì mã đang làm. Do đó, chúng ta cần xem xét những khía cạnh đó là gì để xác định xem thậm chí có lựa chọn nào để bắt đầu hay không, và nếu vậy, những ưu và nhược điểm sẽ là gì.
Các khía cạnh chức năng dành riêng cho SQLCLR
Các loại do người dùng xác định (UDT): UDT không thể được tham chiếu trên các cơ sở dữ liệu; chúng không thể được khai báo bằng tên 3 phần (ví dụ: DatabaseName.SchemaName.UserDefinedTypeName). Nếu bất kỳ UDT nào đang được sử dụng thì hội sẽ cần được thêm vào mỗi cơ sở dữ liệu trong đó UDT sẽ được sử dụng. Tuy nhiên, nếu các đối tượng SQLCLR khác đang được sử dụng, thì giả sử rằng có lựa chọn đặt các đối tượng đó vào DB tập trung hoặc trong mỗi DB khách hàng / ứng dụng, thì bạn luôn có thể đặt UDT vào một hội được đặt trong mỗi khách hàng / ứng dụng DB và một hội khác có chứa Hàm / Thủ tục lưu trữ / Tập hợp do người dùng xác định / Kích hoạt.
Bảo vệ:
Có bao nhiêu cơ sở dữ liệu bị ảnh hưởng: Mã CLR có làm bất cứ điều gì đòi hỏi Hội phải được đánh dấu bằng một PERMISSION_SET
trong hai EXTERNAL_ACCESS
hoặc UNSAFE
không? Nếu vậy, bạn có đang nhập bất kỳ DLL nào được ký ngoài tầm kiểm soát của bạn và không thể từ chức không? Thông thường, đây sẽ là các thư viện .NET Framework không được hỗ trợ hoặc các DLL của bên thứ 3. Khi bạn không có quyền kiểm soát việc ký kết Assemblies rằng cần phải được đánh dấu là một trong hai EXTERNAL_ACCESS
hoặc UNSAFE
, sau đó bạn có thể bị buộc phải thiết lập cơ sở dữ liệu chứa các hội (ies) để TRUSTWORTHY ON
. Kể từ khi thiết lập cơ sở dữ liệu thànhTRUSTWORTHY ON
là một rủi ro bảo mật, tốt nhất là giảm thiểu số lượng cơ sở dữ liệu bạn cần làm điều này, trong trường hợp đó, việc đặt mã vào cơ sở dữ liệu tập trung có vẻ như là một cách tiếp cận tốt hơn. Và nếu bạn đã có DB trung tâm cho mã khác và muốn thực sự giảm thiểu loại rủi ro bảo mật này, bạn có thể có DB tập trung thứ hai cho mã này.
Nếu bạn có quyền kiểm soát việc ký (các) DLL, thì bạn chắc chắn nên tạo Chứng chỉ hoặc Khóa bất đối xứng trong master
cơ sở dữ liệu dựa trên DLL, sau đó tạo Đăng nhập dựa trên Chứng chỉ hoặc Khóa bất đối xứng đó, sau đó gán một trong hai EXTERNAL ACCESS ASSEMBLY
hoặc UNSAFE ASSEMBLY
phép nhập đó. Một vài bước ngay tại đó (và những thứ duy nhất được tạo là Chứng chỉ hoặc Khóa và Đăng nhập) sẽ cho phép bất kỳ Hội đồng nào được ký với cùng một khóa riêng được đặt thành EXTERNAL_ACCESS
hoặc UNSAFE
(tùy thuộc vào quyền nào được cấp cho Đăng nhập), không vấn đề gì cơ sở dữ liệu nó được tải vào. Và nếu bạn có thể làm điều này, thì bạn có thể đặt Tập hợp thành EXTERNAL_ACCESS
hoặcUNSAFE
trong tất cả các cơ sở dữ liệu khách hàng / ứng dụng mà không có bất kỳ rủi ro bảo mật nào nhiều hơn bạn sẽ đặt cùng mã đó vào cơ sở dữ liệu tập trung ** .
Các quyền khác nhau cho các ứng dụng khách / ứng dụng khác nhau cần: Nếu, vì bất kỳ lý do nào, một số ứng dụng khách / ứng dụng có thể cần phải có sự khác biệt PERMISSION_SET
so với các ứng dụng khác, thì điều đó sẽ yêu cầu tải các hội đồng vào cơ sở dữ liệu khách hàng / ứng dụng. Điều này sẽ cho phép bạn có một số cơ sở dữ liệu sử dụng SAFE
trong khi những người khác đang sử dụng EXTERNAL_ACCESS
. Điều này vượt xa những gì có thể được thực hiện với quyền cấp đối tượng. Bằng cách đặt một hội có mã để thực hiện các chức năng của hệ thống tệp SAFE
, bạn đảm bảo rằng mã không thể hoạt động, ngay cả khi ai đó tìm cách bỏ qua bảo mật thông thường của bạn và vẫn có thể EXECUTE
Thủ tục lưu trữ SQLCLR.
AppDomains: Khía cạnh này liên quan đến việc sử dụng và phân tách bộ nhớ / tài nguyên. Đây có lẽ là lĩnh vực có ảnh hưởng nhất về mặt cân nhắc, nhưng nó cũng có thể là ít được hiểu nhất. Vì vậy, hãy bắt đầu bằng cách xem các đối tượng T-SQL sẽ xử lý cùng một DB trung tâm như thế nào so với từng câu hỏi DB của ứng dụng khách / ứng dụng.
Các chức năng và thủ tục lưu trữ T-SQL, khi được thực thi, lưu trữ các kế hoạch thực hiện của chúng trong bộ đệm của kế hoạch (tốt, không phải là TVF nội tuyến), trong bộ nhớ. Suy nghĩ về việc chỉ sử dụng bộ nhớ, sử dụng DB tập trung có lợi thế là lưu trữ một gói duy nhất thay vì một gói cho mỗi DB khách hàng / ứng dụng, đặc biệt nếu có từ 100 DB trở lên. Tuy nhiên, có một kế hoạch lưu trữ sẽ mời câu hỏi liệu nó có phải là một kế hoạch tối ưu cho các lần thực hiện tiếp theo hay không. Có thể, với một phạm vi biến đổi tiềm năng rộng lớn trong cách nó được thực thi trên rất nhiều DB khách hàng / ứng dụng, một kế hoạch duy nhất là tuyệt vời đối với một số người nhưng cũng khá kinh khủng đối với những người khác. Nếu bạn không muốn hiệu suất của việc chỉ địnhWITH RECOMPILE
, sau đó triển khai điều đó đến từng DB khách hàng / ứng dụng sẽ cho phép tối ưu hóa cá nhân hơn. Tóm lại: DB trung tâm là bộ nhớ ít hơn được sử dụng cho bộ đệm kế hoạch, nhưng có khả năng hiệu năng kém hơn; DB riêng biệt là bộ nhớ nhiều hơn cho bộ nhớ cache kế hoạch, nhưng ít vấn đề hiệu năng tiềm năng hơn.
Khi nói đến các đối tượng SQLCLR, các ưu và nhược điểm của bộ nhớ đệm kế hoạch tương tự tồn tại cho mỗi cách tiếp cận. Nhưng bây giờ chúng tôi đang xử lý các Miền ứng dụng, có nhiều phân nhánh bổ sung để xem xét. Miền ứng dụng là không gian bộ nhớ / hộp cát mà .NET sử dụng để chạy mã. Mỗi miền ứng dụng là hộp cát riêng. Trong SQL Server, Miền ứng dụng được tạo cho mỗi kết hợp Cơ sở dữ liệu và Chủ sở hữu hội. Vì vậy, nhiều Hội đồng trong cùng một DB được sở hữu bởi cùng một Người dùng sẽ chia sẻ một Miền ứng dụng, nhưng các Hội đồng trong cùng một DB đó do một Người dùng khác sở hữu sẽ có một Miền ứng dụng khác và các Hội đồng trong các DB khác sẽ ở trong Miền ứng dụng của riêng họ. Với ý nghĩ đó:
Tiêu thụ bộ nhớ tăng với tốc độ nhanh hơn khi triển khai tới các DB khách hàng / ứng dụng riêng lẻ do các Bộ lắp ráp đang được sử dụng được tải vào Miền ứng dụng (nhưng chúng không được tải cho đến khi chúng được sử dụng lần đầu tiên). Miền ứng dụng cũng chứa tất cả các biến, xử lý tài nguyên, v.v. (cho đến khi những thứ đó được đánh dấu cho Bộ sưu tập rác vàGC quyết định rằng mặt trăng và các ngôi sao được căn chỉnh hoàn hảo và lấy đó làm dấu hiệu để chạy). Vì vậy, một hội 2 MB trong một cơ sở dữ liệu sử dụng một AppDomain có một lượng bộ nhớ nhất định dành cho các biến, v.v. có thể khác hoàn toàn so với việc tải cùng một hội đó vào 100 DB trong đó hiện tại là 200 MB (về mặt kỹ thuật có một phần của DLL được chia sẻ trong bộ nhớ qua nhiều phiên bản của nó, nhưng tôi không chắc cách đo đó) cộng với 100 lần dung lượng dành cho các biến, v.v.
Một vấn đề liên quan là nếu bạn đang sử dụng Biểu thức chính quy và sử dụng tùy chọn RegEx để Compiled
biên dịch biểu thức thành Ngôn ngữ trung gian (MSIL). Điều này giúp tăng tốc mọi thứ cho các biểu thức được sử dụng nhiều lần, nhưng một khi một biểu thức được biên dịch, nó không thể là rác được thu thập và sẽ ở trong AppDomain cho đến khi nó được khởi động lại. Nếu có một hàm RegEx thường được sử dụng đang sử dụng Compiled
tùy chọn, bộ nhớ được sử dụng để lưu trữ nó sẽ được lặp lại trên mỗi DB nếu hội được tải vào mỗi DB. Trong trường hợp này, có thể có ý nghĩa khi đặt mã này vào DB tập trung.
Hạn chế về tài nguyên có thể là một vấn đề khi sử dụng cơ sở dữ liệu tập trung. Tùy thuộc vào những lớp bạn đang sử dụng, bạn có thể vô tình tạo ra một nút cổ chai tài nguyên. Ví dụ:
Khi sử dụng các phương thức RegEx tĩnh thay vì các phương thức cá thể, Biểu thức chính quy mà bạn sử dụng được lưu trữ. Nhưng kích thước bộ đệm mặc định chỉ có 15 biểu thức. Nếu một loạt các biểu thức tuyệt vời được gửi từ một số lượng lớn ứng dụng khách hoặc ứng dụng, thì các biểu thức sẽ không ở trong bộ đệm trong thời gian dài. Vì vậy, nếu đây là lý do duy nhất để xem xét việc tải hội vào mỗi Cơ sở dữ liệu, thì bạn chỉ cần tăng kích thước bộ đệm. Vui lòng xem trang MSDN cho RegEx.CacheSize để biết chi tiết.
Tương tự, nếu thực hiện WebRequests
thì có một số lượng kết nối hoạt động tối đa mặc định có thể được thực hiện cho một URI cụ thể. Và mặc định đó chỉ là 2. Nếu bạn thực hiện nhiều yêu cầu hơn cho cùng một URI đó (rất dễ thực hiện nếu đó là một vị trí tĩnh và bạn đang sử dụng DB tập trung cho mã này) thì mọi yêu cầu trên mức tối đa đó sẽ chỉ chờ trong dòng một kết nối hiện tại để đóng (tức là chặn). Vì vậy, bạn sẽ phải tải Hội vào mỗi DB khách hàng / ứng dụng hoặc tăng giới hạn kết nối trên mỗi URI. Bạn có thể đặt mức tối đa mặc định cho tất cả các URI trong Miền ứng dụng hiện tại bằng cách đặt ServicePointManager.DefaultConnectionLimit(điều này có thể được đặt một lần mỗi lần khởi động Miền ứng dụng, chẳng hạn như trong trình tạo lớp tĩnh) hoặc có thể được đặt trên cơ sở mỗi URI bằng cách tạo một httpWebRequest và sau đó đặt thuộc tính .ServicePoint.ConnectionLimit của nó (điều này cần được thực hiện mỗi khi WebRequest được khởi tạo do đối tượng có thời gian sống tối đa và một khi rác được thu thập, ConnectionLimit sẽ trở lại ServicePointManager.DefaultConnectionLimit
giá trị, như đã lưu ý ở trên, khi một phiên bản mới được tạo).
Nếu bạn đang sử dụng một biến tĩnh để lưu trữ các giá trị nhất định (bộ nhớ dùng chung - hiếm nhưng vẫn có khả năng), thì bạn cần quyết định phạm vi chia sẻ các giá trị đó sẽ là gì. Nếu bạn muốn chia sẻ được chứa trong mỗi DB khách hàng / ứng dụng, sau đó tải Hội đồng vào từng DB khách hàng / ứng dụng. Nhưng nếu bạn muốn chia sẻ các giá trị đó trên tất cả các DB, thì hãy đặt Hội vào một DB tập trung, chia sẻ.
Khía cạnh chức năng chung
Truy cập cơ sở dữ liệu: Mã có tham chiếu bất kỳ đối tượng cụ thể cơ sở dữ liệu nào không? Hãy nhớ rằng việc thực thi SQL bằng cách sử dụng trong quá trình / Context Connection = true;
kết nối sẽ thực thi ban đầu với cơ sở dữ liệu "hiện tại" được đặt thành cơ sở dữ liệu nơi đối tượng tồn tại, không nhất thiết phải gọi đối tượng từ đâu. Do đó mã chạy trong cơ sở dữ liệu khách hàng / ứng dụng và gọi một đối tượng trong cơ sở dữ liệu tập trung sẽ không thể tham chiếu các đối tượng bằng cách chỉ sử dụng tên 2 phần. Tuy nhiên, bạn vẫn có thể sử dụng cơ sở dữ liệu tập trung cho mã đó miễn là bạn có tham số đầu vào cho @DatabaseName
(use [SqlFacet(MaxSize = 128)] SqlString DatabaseName
:) và sau đó chuyển DB_NAME()
vào nó. Sau đó, bạn có thể sử dụng DatabaseName.Value
mã SQLCLR cho mộtUSE
câu lệnh hoặc nối vào SQL động để tạo các tên đối tượng đủ điều kiện thích hợp (nghĩa là tên 3 phần).
Đây có lẽ không phải là yếu tố quyết định nếu bạn chỉ tham chiếu các đối tượng dựa trên hệ thống (tức là sys.databases
) trả về cùng một hàng cho dù bạn đang ở cơ sở dữ liệu nào. Bạn cũng không nên gặp vấn đề nếu bạn đang thực hiện kết nối bên ngoài vì bạn đã sẵn sàng nhập tên cơ sở dữ liệu cho chuỗi kết nối hoặc bạn sẽ đăng nhập vào cơ sở dữ liệu mặc định để đăng nhập thực hiện kết nối.
Sự khác biệt đối chiếu: Nếu Collations giữa DB tập trung và DB khách / ứng dụng giống nhau, thì đây không phải là yếu tố quyết định khi quyết định giữa hai mô hình này. Nhưng, nếu hệ thống của bạn sẽ hỗ trợ các bộ sưu tập khác nhau, thì bạn cần lưu ý về những gì mã của bạn đang làm vì nó có thể bị ảnh hưởng bởi Collation Precedence. Nếu bạn đang gửi các chuỗi sẽ được so sánh với các chuỗi khác, thì hành vi có thể không như mong đợi, ngay cả khi không có lỗi được tạo ra. Đối chiếu được sử dụng để so sánh các biến cục bộ và chuỗi ký tự sẽ là Đối chiếu mặc định nơi đối tượng (tức là Thủ tục lưu trữ hoặc Hàm) tồn tại. Nếu Đối chiếu này khác với đối chiếu cơ sở dữ liệu "hiện tại" khi đối tượng đó được gọi (nếu truyền theo nghĩa đen hoặc biến) hoặc khác với trường được truyền vào, thì có thể có nhiều sự khác biệt trong cách so sánh đó được thực hiện. Do đó, nếu hỗ trợ nhiều Bộ sưu tập khác nhau, thì các hoạt động dựa trên chuỗi có thể ổn định / nhất quán hơn khi mã được triển khai cho từng DB khách / ứng dụng.
Phiền nhiễu
Sau đây là những lý do được đưa ra trong Câu hỏi này và trong câu hỏi trùng lặp được liên kết ở đầu câu trả lời này, vì thích phương pháp này hay phương pháp khác, nhưng tôi cảm thấy không thực sự phù hợp trong việc quyết định phương pháp nào phù hợp hơn:
- DB tập trung dễ hỗ trợ hơn: Làm sao vậy? Mã chỉ nên tồn tại một lần trong kiểm soát nguồn. Và giả sử rằng mã được tải vào từng cơ sở dữ liệu khách hàng / ứng dụng là như nhau, thì việc khắc phục sự cố sẽ gần như giống nhau trong cả hai trường hợp. Nếu bất cứ điều gì, việc đặt mã SQLCLR trong cơ sở dữ liệu chung, tách biệt với DB khách hàng / ứng dụng sẽ thêm một lớp phức tạp với các cuộc gọi cơ sở dữ liệu chéo có thể được xem là lý do để không tập trung mã này (hoặc khác).
- DB tập trung dễ dàng hơn trong việc triển khai: Nếu đây là trường hợp, thì tôi sẽ nói rằng người ta cần sửa quy trình triển khai của họ. Nếu bạn đang làm việc với một mô hình trong đó bạn sao chép cơ sở dữ liệu khách hàng / ứng dụng để có lược đồ giống hệt nhau và chỉ là dữ liệu khác nhau, thì bạn đã phải đối mặt với vấn đề này khi triển khai thay đổi lược đồ. Triển khai SQLCLR cũng chỉ là các câu lệnh DDL. Có thể khó triển khai mã SQLCLR hơn nếu cố tải hội từ DLL, nhưng không có lý do gì để làm điều đó. Chỉ cần đảm bảo có một tập lệnh SQL độc lập thực hiện
CREATE ASSEMBLY
hoặc ALTER ASSEMBLY
sử dụng các byte hex (tức là FROM 0x4D5F000002C...
).
- DB khách hàng / ứng dụng cá nhân tốt hơn để sao lưu / khôi phục: Đối số ở đây là nếu có vấn đề và bạn cần khôi phục, thì việc khôi phục mọi thứ trong DB khách / ứng dụng là dễ nhất. Tôi sẽ nói rằng nếu bạn cần thực hiện khôi phục, thì việc khôi phục 2 DB (DB khách hàng / DB chung và DB chung) hoạt động khá giống nhau và khôi phục 101 DB (100 DB khách / ứng dụng và 1 DB chung) vẫn còn khá nhiều như nhau Và từ quan điểm độ tin cậy, quá trình sao lưu của bạn là đáng tin cậy và có thể được tin cậy để sao lưu đúng cả DB khách hàng / ứng dụng và DB chung hoặc bạn cần sửa quy trình sao lưu của mình ;-).
- DB khách hàng / ứng dụng cá nhân dễ dàng hơn để kiểm tra các biến thể: Ở một mức độ nào đó là đúng nếu bạn cần kiểm tra một phiên bản mã mà không thay đổi tất cả các tham chiếu. Mặc dù thông thường, điều này nên được thực hiện bằng cách thử nghiệm trong một môi trường hoàn toàn khác để bắt đầu. Nhưng nếu có một khách hàng đang thử nghiệm beta một số thay đổi mã, chẳng hạn, thì tình huống này có thể được giảm thiểu đủ dễ dàng bằng cách sử dụng mô hình được đề xuất bởi @AaronBertrand trong câu trả lời của anh ta cho câu hỏi này rất giống nhau (chủ yếu liên quan Các đối tượng T-SQL và không phải mã SQLCLR cụ thể): Tạo chức năng trong cơ sở dữ liệu trung tâm hoặc lặp lại trong mỗi cơ sở dữ liệu? . Mô hình được thảo luận trong câu trả lời đó là sử dụng DB tập trung cho hầu hết / tất cả các trường hợp vàđể tạo Từ đồng nghĩa trong mỗi DB khách / ứng dụng để mã cục bộ có thể tham chiếu tên cục bộ. Sau đó, nếu cần một biến thể, mã có thể được đặt trong DB khách hàng / ứng dụng cụ thể và từ đồng nghĩa sẽ bị xóa. Trong trường hợp đó, mã T-SQL cục bộ vẫn sẽ tham chiếu cùng tên cục bộ và không biết sự khác biệt. Cá nhân, tôi thực sự thích cách tiếp cận này và không thể nghĩ ra bất kỳ nhược điểm cụ thể nào ngoài việc cần phải tạo / xóa Từ đồng nghĩa, có vẻ như không phải là một cái giá cao để trả cho sự linh hoạt có được.
Ergo: đối với tình huống cụ thể được mô tả trong Câu hỏi này (tức là hàm vô hướng, không có tài nguyên bên ngoài, không truy cập đối tượng cơ sở dữ liệu, không giới hạn tài nguyên), có vẻ như sử dụng cơ sở dữ liệu cấu hình duy nhất của bạn sẽ ổn.
** Nếu bạn thấy mình suy nghĩ "nhưng các hội đồng được đặt thành EXTERNAL_ACCESS hoặc UNSAFE là rủi ro bảo mật vì những gì họ cho phép bạn làm": Tôi không nói rằng không có rủi ro nào với các hội đồng được đặt thành EXTERNAL_ACCESS hoặc UNSAFE nếu bạn sử dụng Chứng chỉ / Phương pháp dựa trên khóa bất đối xứng. Điều tôi đang nói là, trong cấu hình đó, bất kỳ rủi ro nào tồn tại đều không khác nhau giữa việc đặt Hội trong cơ sở dữ liệu tập trung so với trong mỗi cơ sở dữ liệu khách hàng / ứng dụng. Điều này là do bất kỳ vấn đề bảo mật tiềm năng có thể là kết quả của hội được thiết lập để EXTERNAL_ACCESS hoặc không an toàn không cục bộ cơ sở dữ liệu trong đó các hội đồng tồn tại (không giống như thiết TRUSTWORTHY
để ON
). Bất kỳ vấn đề bảo mật là hệ thống rộng. Nhưng, khi thiết lập cơ sở dữ liệu thànhTRUSTWORTHY ON
, sau đó bạn có thêm các vấn đề bảo mật trên mỗi cơ sở dữ liệu.