因此的问题是,与扩展的 SP 方法相比,这样做是否安全(更安全、足够安全等)?
一般是的。我的意思是,如果您正在向操作系统进程发起攻击,那么您正在向操作系统进程发起攻击。我看不出使用扩展存储过程 API 来做到这一点必然比 SQLCLR API 更安全,尤其是当可能崩溃的是位于数据库之外的操作系统进程时。
当然,我不确定 XP API,因为我没有使用过它,但我确实知道以下内容:
- XP API 已被弃用,建议所有可以使用其中任何一种技术完成的新项目都应在 SQLCLR 中完成。
- SQLCLR 确实允许比其他两个更细化的权限,包括进行模拟的能力(如果执行 SQLCLR 对象的登录名是 Windows 登录名)。
- SQLCLR API 由数据库和程序集所有者(即
AUTHORIZATION
子句指定的用户)在进程/内存方面分开。因此,您可以在一个数据库中的程序集出现问题,而不会影响其他数据库中的 SQLCLR 对象(如果另一个用户拥有程序集,甚至在同一个数据库中,尽管实际上这可能很少发生,因为大多数人只是使用默认值dbo
)。
我不确定它是否回答了我的问题,因为我不是在追求“稳健性和可扩展性”,而是在追求稳定性和保持事物正常运行。
当然,当 Assembly 设置为时,您可以在 SQLCLR 中执行以下操作UNSAFE
:
- 可能写入注册表(取决于授予运行 SQL Server 进程的登录身份帐户的访问权限,或者如果启用了模拟并且它是 Windows 登录,则执行 SQLCLR 功能的登录)。
- 可能写入文件系统
- 可能与系统上运行的进程交互
- 与执行来自同一程序集的其他 SQL Server SPID(即会话)共享内存(意味着该数据库中由该用户拥有的特定程序集)。这可能是人们最难以理解的,因为当您习惯于控制台应用程序和 Windows 应用程序拥有自己的单独内存空间时,这是出乎意料的,但是在这里,因为它是每个所有者每个数据库每个程序集的单个 AppDomain,执行该代码的所有会话都共享所有
static
变量。很多代码都是假设 AppDomain 是私有的,因此在静态变量中存储值是有效的,因为它会缓存值,但是在 SQLCLR 中,如果两个进程正在覆盖彼此的值并读取另一个进程,则可能会出现意外行为会话的价值。
- 潜在的内存泄漏。主机保护属性试图阻止您使用可以执行此操作的内置 .NET 功能,例如
TimeZoneInfo
用于在 TimeZoneID 之间转换时间,但主机保护属性不会在程序集上强制执行UNSAFE
。
- 在执行 UNSAFE / FullTrust 代码(协作多任务与抢占)时,运行 SQLCLR 方法的线程可能会以不同方式处理。我以为我读过 UNSAFE 线程的管理方式不同,但不确定我在哪里读到它并正在寻找源代码。
但是以上都说了,如果你调用的是外部EXE,它有自己的AppDomain。
所以,你可以做的是:
继续使用 SQLCLR 包装器调用 EXE 以Process.Start()
. 这为您提供了进程/内存分离以及更轻松地控制对单个存储过程的权限的能力,该存储过程只会调用此 EXE 并且没有人可以更改它(至少在不更改 SQLCLR 代码并重新安装程序集的情况下不能更改)。
在同一台机器上安装 SQL Server Express 的实例,在那里加载 SQLCLR 对象,并在两个方向(从当前 SQL Server 实例到新的 SQL Server Express 实例)创建链接服务器,以便您可以在它们之间轻松通信。这将允许您隔离 SQLCLR 执行并使其远离主 SQL Server 进程。
Of course, that all being said, how much of a concern is this really? Meaning, how likely is it that a process fully crashes and takes down everything with it? Sure, it's not impossible, but usually a crash would take down just the AppDomain and not the CLR host itself. I would think it far more likely that code that doesn't crash but is written poorly and consumes too much memory and/or CPU would be the problem people run into.