Ich habe eine Frage zur Validierung der XML
Verwendung von XSD schema
inside gestellt SQL Server 2012
(siehe Link ). Ich verstehe (wie ich vermutet habe), dass ich verwenden muss CLR Function
. Die Funktion wird erhalten XSD schema text
und XML text
und Validierung machen.
Ich werde 1 Konfigurationsdatenbank und viele Installationsdatenbanken haben. Unter diesem Gesichtspunkt frage ich mich, wo diese Funktion erstellt werden soll - in der Konfigurationsdatenbank oder in jeder Installationsdatenbank?
Aus Sicht der Unterstützung wäre es besser, nur eine CLR-Funktion zu haben.
Antworten:
Dies scheint ein Duplikat dieser Frage zu sein:
Einrichten einer zentralen CLR-Bibliothek für gespeicherte Prozeduren / Funktionen für interne gespeicherte Prozesse in anderen Datenbanken?
Ich bin jedoch nicht der Meinung, dass eine der beiden Antworten angemessen ist, da sie einige der wichtigeren Aspekte dieser Frage nicht erwähnen.
Hier gibt es keine offensichtliche Wahl, welcher Ort für SQLCLR-Objekte im Allgemeinen besser ist, da durch die Ausführung des SQLCLR-Codes Einschränkungen auferlegt werden können. Es gibt einige Verwendungszwecke, bei denen die Assembly in jeder einzelnen Datenbank vorhanden sein muss, und eine Verwendung, bei der sich die Assembly in einer zentralisierten Datenbank befinden muss. Es hängt alles von einigen verschiedenen Aspekten ab, was der Code tut. Daher müssen wir uns ansehen, was diese Aspekte sind, um festzustellen, ob es überhaupt eine Wahl gibt und wenn ja, welche Vor- und Nachteile dies hätte.
SQLCLR-spezifische funktionale Aspekte
Benutzerdefinierte Typen (UDTs): UDTs können nicht datenbankübergreifend referenziert werden. Sie können nicht mit dreiteiligen Namen deklariert werden (z. B. DatabaseName.SchemaName.UserDefinedTypeName). Wenn UDTs verwendet werden, muss die Assembly zu jeder Datenbank hinzugefügt werden, in der der UDT verwendet wird. Wenn jedoch andere SQLCLR-Objekte verwendet werden und davon ausgegangen wird, dass diese Objekte entweder in einer zentralen Datenbank oder in jeder Kunden- / Anwendungs-Datenbank platziert werden können, können Sie die UDTs jederzeit in einer Assembly platzieren, die in jedem Kunden platziert wird / application DB und eine andere Assembly mit Funktionen / gespeicherten Prozeduren / benutzerdefinierten Aggregaten / Triggern.
Sicherheit:
Wie viele Datenbanken sind betroffen: Tut der CLR-Code etwas, bei dem die Assembly mit einem
PERMISSION_SET
von entwederEXTERNAL_ACCESS
oder gekennzeichnet werden mussUNSAFE
? Wenn ja, importieren Sie DLLs, die außerhalb Ihrer Kontrolle signiert wurden und nicht zurückgetreten werden können? In der Regel handelt es sich dabei um nicht unterstützte .NET Framework-Bibliotheken oder DLLs von Drittanbietern. Wenn Sie keine Kontrolle über das Signieren von Baugruppen haben, die entwederEXTERNAL_ACCESS
oder markiert werden müssen, müssenUNSAFE
Sie möglicherweise die Datenbank mit den Baugruppen auf setzenTRUSTWORTHY ON
. Seit dem Einstellen einer Datenbank aufTRUSTWORTHY ON
Da dies ein Sicherheitsrisiko darstellt, ist es vorzuziehen, die Anzahl der Datenbanken zu minimieren, für die Sie dies tun müssten. In diesem Fall scheint das Einfügen des Codes in eine zentralisierte Datenbank ein besserer Ansatz zu sein. Und wenn Sie bereits eine zentrale Datenbank für anderen Code haben und diese Art von Sicherheitsrisiko wirklich minimieren möchten, können Sie eine zweite zentralisierte Datenbank nur für diesen Code haben.Wenn Sie die Kontrolle über das Signieren der DLL (s) haben, sollten Sie auf jeden Fall ein Zertifikat oder einen asymmetrischen Schlüssel in der
master
Datenbank basierend auf der DLL erstellen, dann eine Anmeldung basierend auf diesem Zertifikat oder diesem asymmetrischen Schlüssel erstellen und dann zuweisen entweder dieEXTERNAL ACCESS ASSEMBLY
oder dieUNSAFE ASSEMBLY
Erlaubnis zu diesem Login. Diese wenigen Schritte genau dort (und die einzigen Dinge, die erstellt werden, sind das Zertifikat oder der Schlüssel und die Anmeldung) ermöglichen es, dass jede Assembly, die mit demselben privaten Schlüssel signiert ist, entwederEXTERNAL_ACCESS
oderUNSAFE
(abhängig davon, welche Berechtigung für die Anmeldung erteilt wurde), Nr egal in welche Datenbank (en) es geladen wird. Und wenn Sie dazu in der Lage sind, können Sie Baugruppen entweder aufEXTERNAL_ACCESS
oder setzenUNSAFE
in allen Kunden- / Anwendungsdatenbanken ohne größeres Sicherheitsrisiko, als wenn Sie denselben Code in eine zentralisierte Datenbank gestellt hätten ** .Unterschiedliche Berechtigungen für unterschiedliche Clients / Apps sind erforderlich: Wenn einige Clients / Apps aus irgendeinem Grund andere Berechtigungen
PERMISSION_SET
als andere benötigen, müssen die Assemblys in jede Client- / App-Datenbank geladen werden. Auf diese Weise können Sie einige Datenbanken verwenden,SAFE
während andere verwendet werdenEXTERNAL_ACCESS
. Dies geht über das hinaus, was mit Berechtigungen auf Objektebene möglich ist. Indem Sie eine Assembly festlegen, die Code für Dateisystemfunktionen enthältSAFE
, stellen Sie sicher, dass der Code nicht funktioniert, auch wenn jemand einen Weg findet, Ihre reguläre Sicherheit zu umgehen, undEXECUTE
die gespeicherte SQLCLR-Prozedur weiterhin ausführen kann .AppDomains: Dieser Aspekt betrifft die Speicher- / Ressourcennutzung und -trennung. Dies ist wahrscheinlich der Bereich, der in Bezug auf Überlegungen am stärksten beeinflusst, aber wahrscheinlich auch am wenigsten verstanden wird. Lassen Sie uns zunächst untersuchen, wie T-SQL-Objekte dieselbe zentrale Datenbank für jede Client- / App-Datenbankfrage behandeln würden.
T-SQL-Funktionen und gespeicherte Prozeduren speichern nach ihrer Ausführung ihre Ausführungspläne im Plan-Cache (also nicht in Inline-TVFs), der sich im Speicher befindet. Wenn Sie nur an die Speichernutzung denken, hat die Verwendung einer zentralisierten Datenbank den Vorteil, dass ein einzelner Plan anstelle eines Plans pro Client / App-Datenbank gespeichert wird, insbesondere wenn 100 oder mehr DBs vorhanden sind. Ein zwischengespeicherter Plan wirft jedoch die Frage auf, ob er ein optimaler Plan für nachfolgende Ausführungen ist oder nicht. Es ist möglich, dass ein einziger Plan für einige großartig, für andere aber auch fürchterlich ist, da die Ausführung in so vielen Client- / App-DBs möglicherweise sehr unterschiedlich ist. Wenn Sie nicht möchten, dass die Leistung beeinträchtigt wird
WITH RECOMPILE
Die Bereitstellung auf jeder Client- / App-Datenbank würde eine individuellere Optimierung ermöglichen. Kurz gesagt: Die zentrale Datenbank verwendet weniger Speicher für den Plan-Cache, aber möglicherweise eine schlechtere Leistung. Separate DBs bieten mehr Speicher für den Plan-Cache, aber weniger potenzielle Leistungsprobleme.Wenn es um SQLCLR-Objekte geht, gibt es für jeden Ansatz die gleichen Vor- und Nachteile beim Plan-Caching. Aber jetzt, da wir uns mit App-Domänen befassen, sind zusätzliche Konsequenzen zu berücksichtigen. App-Domänen sind Speicherbereiche / Sandboxen, in denen .NET Code ausführt. Jede App-Domäne ist eine eigene Sandbox. In SQL Server werden App-Domänen für jede Kombination aus Datenbank und Assembly-Eigentümer erstellt. Mehrere Assemblies in derselben Datenbank, die demselben Benutzer gehören, teilen sich eine App-Domäne, aber Assemblies in derselben DB, die einem anderen Benutzer gehören, haben eine andere App-Domäne, und Assemblys in anderen DBs befinden sich in ihren eigenen App-Domänen. In diesem Sinne:
Der Speicherverbrauch steigt bei der Bereitstellung auf einzelnen Client- / App-DBs schneller an, da die verwendeten Assemblys in die App-Domänen geladen werden (sie werden jedoch erst geladen, wenn sie zum ersten Mal verwendet werden). Die App-Domäne enthält auch alle Variablen, Ressourcenhandles usw. (bis diese Dinge für Garbage Collection und markiert sindGC entscheidet, dass Mond und Sterne perfekt ausgerichtet sind und nimmt dies als Zeichen zum Laufen. Eine 2-MB-Assembly in einer Datenbank mit einer AppDomain, in der eine bestimmte Menge an Speicher für Variablen usw. reserviert ist, kann sich also erheblich vom Laden derselben Assembly in 100 DBs unterscheiden, in denen sie jetzt 200 MB beträgt (technisch gesehen gibt es einen Teil der DLL) Das wird im Speicher von mehreren Instanzen geteilt, aber ich bin mir nicht sicher, wie ich das messen soll.) plus 100-mal so viel Speicherplatz, der für Variablen usw. reserviert ist.
Ein verwandtes Problem ist, wenn Sie reguläre Ausdrücke verwenden und die RegEx-Option verwenden, für
Compiled
die der Ausdruck bis zur Intermediate Language (MSIL) kompiliert wird. Dies beschleunigt zwar die Verwendung wiederholt verwendeter Ausdrücke, aber sobald ein Ausdruck kompiliert wurde, kann er nicht mehr gesammelt werden und bleibt in der AppDomain, bis er neu gestartet wird. Wenn es eine häufig verwendete RegEx-Funktion gibt, die dieseCompiled
Option verwendet, wird der zum Speichern verwendete Speicher für jede Datenbank wiederholt, wenn die Assembly in jede Datenbank geladen wird. In diesem Fall kann es sinnvoll sein, diesen Code in einer zentralen Datenbank abzulegen.Ressourcenbeschränkungen können bei der Verwendung einer zentralisierten Datenbank ein Problem sein. Abhängig davon, welche Klassen Sie verwenden, können Sie unwissentlich einen Ressourcenengpass verursachen. Zum Beispiel:
Wenn Sie die statischen RegEx-Methoden anstelle der Instanzmethoden verwenden, werden die von Ihnen verwendeten regulären Ausdrücke zwischengespeichert. Die Standard-Cache-Größe beträgt jedoch nur 15 Ausdrücke. Wenn eine große Anzahl von Ausdrücken von einer großen Anzahl von Clients oder Apps gesendet wird, bleiben Ausdrücke nicht sehr lange im Cache. Wenn dies der einzige Grund ist, die Assembly in jede Datenbank zu laden, können Sie einfach die Cache-Größe erhöhen. Weitere Informationen finden Sie auf der MSDN-Seite für RegEx.CacheSize .
In ähnlicher Weise gibt es beim Herstellen
WebRequests
einer Standardmaximalanzahl aktiver Verbindungen, die zu einem bestimmten URI hergestellt werden können. Und dieser Standardwert ist nur 2. Wenn Sie mehr Anforderungen an denselben URI stellen (sehr einfach, wenn es sich um einen statischen Speicherort handelt und Sie eine zentralisierte Datenbank für diesen Code verwenden), warten alle Anforderungen über diesem Maximum einfach in der Schlange eine aktuelle Verbindung zum Schließen (dh Blockieren). Sie müssten also entweder die Assembly in jede Client- / App-Datenbank laden oder das Limit für Verbindungen pro URI erhöhen. Sie können das Standardmaximum für alle URIs in der aktuellen App-Domäne festlegen, indem Sie ServicePointManager.DefaultConnectionLimit festlegen(Dies kann einmal pro Start der App-Domäne festgelegt werden, z. B. in einem statischen Klassenkonstruktor.) Sie kann auch auf URI-Basis festgelegt werden, indem Sie eine HttpWebRequest erstellen und dann die Eigenschaft .ServicePoint.ConnectionLimit festlegen (dies muss erforderlich sein) Dies erfolgt jedes Mal, wenn die WebRequest instanziiert wird, da das Objekt eine maximale Lebensdauer hat. Sobald der Müll gesammelt wurde, wird das ConnectionLimit auf denServicePointManager.DefaultConnectionLimit
Wert zurückgesetzt, wie oben angegeben, wenn eine neue Instanz erstellt wird.Wenn Sie eine statische Variable zum Zwischenspeichern bestimmter Werte verwenden (gemeinsam genutzter Speicher - selten, aber immer noch möglich), müssen Sie entscheiden, in welchem Umfang diese Werte gemeinsam genutzt werden sollen. Wenn die Freigabe in jeder Client- / App-Datenbank enthalten sein soll, laden Sie die Assembly in jede Client- / App-Datenbank. Wenn Sie diese Werte jedoch für alle DBs freigeben möchten, fügen Sie die Assembly in eine gemeinsam genutzte, zentralisierte DB ein.
Allgemeine funktionale Aspekte
Datenbankzugriff: Verweist der Code auf datenbankspezifische Objekte? Beachten Sie, dass die Ausführung von SQL mithilfe des In-Process / der
Context Connection = true;
Verbindung zunächst ausgeführt wird, wobei die "aktuelle" Datenbank auf die Datenbank festgelegt wird, in der das Objekt vorhanden ist, und nicht unbedingt auf die Datenbank, von der aus das Objekt aufgerufen wird. Daher kann Code, der in einer Kunden- / Anwendungsdatenbank ausgeführt wird und ein Objekt in einer zentralen Datenbank aufruft, Objekte nicht mit nur zweiteiligen Namen referenzieren. Sie können jedoch weiterhin eine zentralisierte Datenbank für diesen Code verwenden, solange Sie einen Eingabeparameter für@DatabaseName
(use :) haben[SqlFacet(MaxSize = 128)] SqlString DatabaseName
und diesen dann übergebenDB_NAME()
. Dann können SieDatabaseName.Value
im SQLCLR-Code entweder aUSE
Anweisung oder zum Verketten in Dynamic SQL, um die entsprechenden vollständig qualifizierten Objektnamen (dh dreiteilige Namen) zu erstellen.Dies ist wahrscheinlich kein entscheidender Faktor, wenn Sie nur auf systembasierte Objekte verweisen (dh
sys.databases
), die unabhängig von der Datenbank, in der Sie sich befinden, dieselben Zeilen zurückgeben. Es sollte auch kein Problem sein, wenn Sie eine externe Verbindung herstellen, da dies bereits der Fall wäre Wenn Sie den Datenbanknamen für die Verbindungszeichenfolge übergeben, melden Sie sich einfach bei der Standarddatenbank für die Anmeldung an, die die Verbindung herstellt.Sortierunterschiede: Wenn die Sortierungen zwischen der zentralisierten Datenbank und der Client / App-Datenbank identisch sind, ist dies kein entscheidender Faktor bei der Entscheidung zwischen diesen beiden Modellen. Wenn Ihr System jedoch unterschiedliche Kollatierungen unterstützt, müssen Sie wissen, was Ihr Code tut, da dies möglicherweise durch die Kollatierungspräzision beeinflusst wird. Wenn Sie Zeichenfolgen senden, die mit anderen Zeichenfolgen verglichen werden, entspricht das Verhalten möglicherweise nicht den Erwartungen, auch wenn kein Fehler auftritt. Die zum Vergleichen lokaler Variablen und Zeichenfolgenliterale verwendete Kollatierung ist die Standardkollatierung, in der das Objekt (dh gespeicherte Prozedur oder Funktion) vorhanden ist. Wenn sich diese Sortierung von der Sortierung der "aktuellen" Datenbank unterscheidet, wenn dieses Objekt aufgerufen wird (wenn ein Literal oder eine Variable übergeben wird), oder sich von dem Feld unterscheidet, das übergeben wird, kann es verschiedene Unterschiede bei der Art und Weise dieses Vergleichs geben erledigt. Wenn eine Vielzahl von Kollatierungen unterstützt wird, sind stringbasierte Vorgänge möglicherweise stabiler / konsistenter, wenn der Code für jede Client- / App-Datenbank bereitgestellt wird.
Ablenkungen
Die folgenden Gründe werden in dieser Frage und in der oben in dieser Antwort verlinkten doppelten Frage angegeben, um die eine oder andere Methode zu bevorzugen, aber ich bin der Meinung, dass sie für die Entscheidung, welche Methode besser passt, nicht wirklich relevant sind:
CREATE ASSEMBLY
oderALTER ASSEMBLY
die Hex-Bytes (dhFROM 0x4D5F000002C...
) verwendet.Ergo: Für die in dieser Frage beschriebene besondere Situation (dh Skalarfunktion, keine externen Ressourcen, kein Zugriff auf Datenbankobjekte, keine Ressourcenbeschränkungen) scheint die Verwendung Ihrer einzelnen Konfigurationsdatenbank in Ordnung zu sein.
** Wenn Sie denken, "aber Assemblys, die auf EXTERNAL_ACCESS oder UNSAFE gesetzt sind, stellen Sicherheitsrisiken dar, weil sie dies zulassen": Ich habe nicht gesagt, dass bei Assemblys, die auf EXTERNAL_ACCESS oder UNSAFE gesetzt sind, überhaupt kein Risiko besteht, wenn Sie die verwenden Zertifikat- / asymmetrische schlüsselbasierte Methode. Was ich damit sagen möchte, ist, dass in dieser Konfiguration das Risiko nicht zwischen dem Platzieren der Assembly in einer zentralisierten Datenbank und dem Platzieren in jeder Client- / Anwendungsdatenbank besteht. Dies liegt daran, dass potenzielle Sicherheitsprobleme, die sich aus Assemblys ergeben können, die auf EXTERNAL_ACCESS oder UNSAFE festgelegt sind, nicht in der Datenbank lokalisiert sind, in der diese Assemblys vorhanden sind (im Gegensatz zu Einstellung
TRUSTWORTHY
aufON
). Alle Sicherheitsprobleme sind systemweit. Aber beim Einstellen von Datenbanken aufTRUSTWORTHY ON
Dann haben Sie zusätzliche Sicherheitsprobleme pro Datenbank.quelle