Bog*_*nov 7 xml t-sql sql-server-2012 functions sql-clr
我问了一个关于XML
使用XSD schema
inside验证的问题SQL Server 2012
(见链接)。我明白(正如我怀疑的那样)我需要使用CLR Function
. 该函数将获取XSD schema text
并XML text
进行验证。
我将有 1 个配置数据库和许多安装数据库。从那个角度来看,我想知道在哪里创建该功能 - 在配置数据库中还是在每个安装数据库中?
从支持的角度来看,最好只有一个 CLR 功能。
这似乎是这个问题的重复:
为其他数据库中的内部存储过程设置中央 CLR 存储过程/函数存储库库以使用?
但是,我不认为这两个答案中的任何一个都足够,因为它们没有提到这个问题的一些更重要的方面。
对于一般情况下哪个位置更适合 SQLCLR 对象,这里没有明显的选择,因为SQLCLR 代码所做的事情可能会施加约束。有些用途需要将程序集放在每个单独的数据库中,还有一种用途需要将程序集放在集中式数据库中。这完全取决于代码正在做什么的几个不同方面。因此,我们需要查看这些方面是什么,以确定是否可以选择开始,如果可以,利弊是什么。
用户定义类型 (UDT): UDT 不能跨数据库引用;它们不能用 3 部分名称(即 DatabaseName.SchemaName.UserDefinedTypeName)声明。如果正在使用任何 UDT,则需要将程序集添加到将使用 UDT 的每个数据库中。但是,如果正在使用其他 SQLCLR 对象,则假设可以选择将这些对象放置在集中式 DB 中或每个客户/应用程序 DB 中,那么您始终可以将 UDT 放置在放置在每个客户中的程序集中/application DB 和另一个包含函数/存储过程/用户定义聚合/触发器的程序集。
安全:
多少个数据库受影响:是对CLR代码做任何事情,要求大会与被标记PERMISSION_SET
的两种EXTERNAL_ACCESS
或UNSAFE
?如果是这样,您是否正在导入任何在您的控制之外签名且无法重新签名的 DLL?通常,这些是不受支持的 .NET Framework 库或 3rd 方 DLL。当您无法控制需要标记为EXTERNAL_ACCESS
或 的程序UNSAFE
集的签名时,您可能被迫将包含程序集的数据库设置为TRUSTWORTHY ON
。由于将数据库设置为TRUSTWORTHY ON
是一种安全风险,最好尽量减少您需要执行此操作的数据库数量,在这种情况下,将代码放入集中式数据库似乎是一种更好的方法。如果您已经有一个用于其他代码的中央数据库,并希望真正将这种类型的安全风险降到最低,那么您可以拥有一个仅用于此代码的第二个中央数据库。
如果您确实可以控制 DLL 的签名,那么您绝对应该master
基于 DLL在数据库中创建证书或非对称密钥,然后基于该证书或非对称密钥创建登录,然后分配该登录的EXTERNAL ACCESS ASSEMBLY
或UNSAFE ASSEMBLY
权限。就在那里的那几个步骤(并且唯一创建的是证书或密钥和登录名)将允许将使用相同私钥签名的任何程序集设置为EXTERNAL_ACCESS
或UNSAFE
(取决于授予登录名的权限),没有不管它加载到什么数据库中。如果您能够做到这一点,那么您可以将程序集设置为EXTERNAL_ACCESS
或UNSAFE
在所有客户/应用程序数据库中,与将相同的代码放入中央数据库**相比,没有任何更多的安全风险。
不同客户端/应用程序需要不同的权限:如果出于任何原因,某些客户端/应用程序可能需要与其他客户端/应用程序不同PERMISSION_SET
,则需要将程序集加载到每个客户端/应用程序数据库中。这将允许您使用一些数据库,SAFE
而其他数据库则使用EXTERNAL_ACCESS
. 这超出了使用对象级权限所能完成的范围。通过将具有执行文件系统功能的代码的程序集设置为SAFE
,您可以保证代码无法工作,即使有人确实找到了绕过常规安全性的方法并且仍然可以EXECUTE
执行 SQLCLR 存储过程。
AppDomains:这方面涉及内存/资源的利用和分离。这可能是考虑因素影响最大的领域,但也可能是最不了解的领域。因此,让我们首先看看 T-SQL 对象如何处理相同的中央数据库与每个客户端/应用程序数据库问题。
T-SQL 函数和存储过程在执行时,将它们的执行计划存储在内存中的计划缓存(好吧,不是内联 TVF)中。仅从内存利用率的角度考虑,使用集中式 DB 的优势在于可以为每个客户端/应用程序 DB 存储单个计划而不是一个计划,尤其是在有 100 个或更多 DB 的情况下。但是,拥有缓存计划会引发一个问题,即它是否是后续执行的最佳计划。有可能,由于它在如此多的客户端/应用程序 DB 中的执行方式可能存在很大差异,因此单个计划对某些人来说很好,但对其他人来说也很糟糕。如果您不希望指定的性能受到影响WITH RECOMPILE
,然后将其部署到每个客户端/应用程序数据库将允许更个性化的优化。简而言之:中央数据库用于计划缓存的内存较少,但性能可能更差;单独的 DB 为计划缓存提供更多内存,但潜在的性能问题更少。
对于 SQLCLR 对象,每种方法都存在相同的计划缓存优缺点。但是现在我们正在处理应用程序域,还有其他后果需要考虑。应用程序域是 .NET 用来运行代码的内存空间/沙箱。每个应用程序域都是它自己独立的沙箱。在 SQL Server 中,应用程序域是按每个数据库和程序集所有者组合创建的。因此,同一用户拥有的同一数据库中的多个程序集将共享一个应用程序域,但另一个用户拥有的同一数据库中的程序集将具有不同的应用程序域,而其他数据库中的程序集将位于自己的应用程序域中。考虑到这一点:
部署到单个客户端/应用程序数据库时,内存消耗会以更快的速度增加,因为正在使用的程序集已加载到应用程序域中(但在首次使用之前不会加载它们)。应用程序域还包含所有变量、资源句柄等(直到这些东西被标记为垃圾收集和GC 决定月亮和星星完美对齐,并将其作为运行的标志)。因此,使用一个 AppDomain 的一个数据库中的 2 MB 程序集为变量等保留了一定数量的内存,这与将同一个程序集加载到 100 个 DB 中(现在它现在是 200 MB)完全不同(从技术上讲,存在 DLL 的某些部分)它在多个实例的内存中共享,但我不知道如何衡量)加上为变量等保留的空间量的 100 倍。
一个相关的问题是,如果您使用正则表达式并使用 RegEx 选项Compiled
将表达式编译为中间语言 (MSIL)。这确实加快了重复使用表达式的速度,但是一旦表达式被编译,它就不能被垃圾收集,并且会一直留在 AppDomain 中,直到它重新启动。如果有一个常用的 RegEx 函数正在使用该Compiled
选项,则如果将程序集加载到每个 DB 中,用于存储它的内存将按每个 DB 重复。在这种情况下,将此代码放在集中式数据库中可能是有意义的。
使用集中式数据库时,资源限制可能是一个问题。根据您使用的类,您可能会在不知不觉中造成资源瓶颈。例如:
当使用静态 RegEx 方法而不是实例方法时,您使用的正则表达式会被缓存。但是默认缓存大小只有 15 个表达式。如果从大量客户端或应用程序发送各种各样的表达式,则表达式不会在缓存中停留很长时间。因此,如果这是考虑将程序集加载到每个数据库的唯一原因,那么您可以增加缓存大小。有关详细信息,请参阅RegEx.CacheSize的 MSDN 页面。
类似地,如果进行WebRequests
,则可以对特定 URI 进行默认的最大活动连接数。并且该默认值仅为 2。如果您向同一个 URI 发出更多请求(如果它是静态位置,并且您为此代码使用集中式数据库,则很容易做到),那么任何超过该最大值的请求都将简单地排队等待要关闭(即阻塞)的当前连接。因此,您必须将程序集加载到每个客户端/应用程序数据库中,或者增加每个 URI 的连接数限制。您可以通过设置ServicePointManager.DefaultConnectionLimit为当前应用程序域中的所有URI设置默认最大值(这可以在 App Domain 每次启动时设置一次,例如在静态类构造函数中),或者可以通过创建 HttpWebRequest 然后设置其.ServicePoint.ConnectionLimit属性(这需要在每个 URI 的基础上设置)每次实例化 WebRequest 时都会执行此操作,因为对象具有最长的生存时间,并且一旦垃圾收集,ConnectionLimit 将恢复为该ServicePointManager.DefaultConnectionLimit
值,如上所述,当创建新实例时)。
如果您使用静态变量来缓存某些值(共享内存——很少见,但仍有可能),那么您需要决定共享这些值的范围应该是什么。如果您希望共享包含在每个客户端/应用程序 DB 中,则将程序集加载到每个客户端/应用程序 DB 中。但是,如果您想在所有数据库之间共享这些值,则将程序集放入共享的集中式数据库中。
数据库访问:代码是否引用了任何特定于数据库的对象?请记住,使用进程内/Context Connection = true;
连接执行 SQL最初将在“当前”数据库设置为对象所在的数据库的情况下执行,不一定是从哪里调用对象。因此,在客户/应用程序数据库中运行并调用集中式数据库中的对象的代码将无法仅使用由两部分组成的名称来引用对象。但是,您仍然可以为此类代码使用集中式数据库,只要您有@DatabaseName
(use: [SqlFacet(MaxSize = 128)] SqlString DatabaseName
)的输入参数,然后将其DB_NAME()
传入即可。然后您可以DatabaseName.Value
在 SQLCLR 代码中使用USE
语句或连接到动态 SQL 以创建适当的完全限定对象名称(即 3 部分名称)。
如果您只引用基于系统的对象(即sys.databases
),无论您在哪个数据库中都返回相同的行,这可能不是决定性因素。如果您正在建立外部连接,这也不应该是一个问题,因为您已经是传递连接字符串的数据库名称,或者您将只是登录到默认数据库以进行连接的登录。
排序规则差异:如果集中式 DB 和客户端 / 应用程序 DB 之间的排序规则相同,那么在这两种模型之间做出决定时,这不是决定因素。但是,如果您的系统要支持不同的排序规则,那么您需要了解您的代码正在做什么,因为它可能会受到排序规则优先级的影响. 如果您发送的字符串将与其他字符串进行比较,那么即使没有产生错误,行为也可能不是预期的。用于比较局部变量和字符串文字的排序规则将是对象(即存储过程或函数)所在的默认排序规则。如果此排序规则与调用该对象时“当前”数据库的排序规则不同(如果传入文字或变量),或者与传入的字段不同,则该比较的方式可能存在多种差异已经完成了。因此,如果支持各种排序规则,那么当代码部署到每个客户端/应用程序数据库时,基于字符串的操作可能会更加稳定/一致。
以下是此问题以及此答案顶部链接的重复问题中给出的原因,用于首选一种方法或另一种方法,但我认为在决定哪种方法更合适时并不真正相关:
CREATE ASSEMBLY
或ALTER ASSEMBLY
使用十六进制字节(即FROM 0x4D5F000002C...
)。因此:对于这个问题中描述的特定情况(即标量函数、没有外部资源、没有数据库对象访问、没有资源限制),使用您的单个配置数据库似乎没问题。
**如果您发现自己在想“但设置为 EXTERNAL_ACCESS 或 UNSAFE 的程序集是安全风险,因为它们允许您执行的操作”:我并不是说设置为 EXTERNAL_ACCESS 或 UNSAFE 的程序集完全没有风险,如果您使用基于证书/非对称密钥的方法。我要说的是,在该配置中,无论存在何种风险,将程序集放置在集中式数据库中与放置在每个客户端/应用程序数据库中都没有区别。这是因为设置为 EXTERNAL_ACCESS 或 UNSAFE 的程序集可能导致的任何潜在安全问题都未本地化到这些程序集所在的数据库中(与设置TRUSTWORTHY
为不同ON
)。任何安全问题都是系统范围的。但是,当将数据库设置为TRUSTWORTHY ON
,那么您还有额外的、每个数据库的安全问题。
归档时间: |
|
查看次数: |
1393 次 |
最近记录: |