SQL Server CLR集成生命周期是什么?

8
CLR (.NET)对象在SQL Server中是如何管理的?
从SQL Server到任何CLR代码的入口点都是静态方法。通常,您只会创建存在于该方法范围内的对象。但是,您可以将对象引用存储在静态成员中,让它们逃离方法调用范围。如果SQL Server在多个存储过程/函数调用之间保留这些对象,则它们可能对缓存应用程序有用-尽管它们也更加危险。
SQL Server如何处理这个问题?它是否允许(非方法)静态成员?如果是,它会在内存中保留它们多长时间?它会在每次CLR调用后回收所有内容吗?它如何处理并发性?
5个回答

6
在罗宾·杜森(Robin Dewson)和朱利安·斯金纳(Julian Skinner)所著的《Pro SQL Server 2005 Assemblies》一书中,提到“与其他数据库对象一样,加载到数据库中的程序集归属于数据库用户。同一数据库中由同一用户拥有的所有程序集将在同一AppDomain中运行。不同用户拥有的程序集将在单独的AppDomain中运行。”
这告诉我们,如果您正在使用单个数据库,并且所有使用CREATE ASSEMBLY语句加载的程序集具有相同的所有者,则这些程序集都将在同一个应用程序域中运行。然而,在同一个AppDomain中并不意味着使用相同的代码库,因此即使是同一个dll也可以被加载到同一个应用程序域中多次,它们的类型也不会匹配,尽管它们具有相同的名称。当来自不同代码库的同名类型时,它们的静态变量也将是不同的实例。
我认为在SQL Server CLR环境中使用多个程序集中的静态变量的唯一安全方法是实际上只使用一个程序集。您可以使用ILMerge实用程序和"UnionMerge"选项将所有程序集打包到一个程序集中,并合并具有相同名称的类。这应该保证对于给定数据库,在您唯一的程序集中,您的静态变量将像在独立应用程序中一样工作。我认为可以安全地假设应用程序域不会在每个请求时卸载和重新加载,但不能保证它永远不会被卸载,因为每当发生未处理的错误时(至少如果它正在使用不安全模式),它都会发生。

4

如果程序集以Unsafe权限级别部署,SQL Server允许使用静态只读成员。

实际上,对象在内存中保留直到SQL服务停止/重新启动。

关于并发性,您的对象和方法应该像其他地方一样是线程安全的。

例如:

public static class MyCLRClass
{
    private static readonly ReaderWriterLock rwlock = new ReaderWriterLock();
    private static readonly ArrayList list = new ArrayList();

    private static void AddToList(object obj)
    {
        rwlock.AcquireWriterLock(1000);
        try
        {
            list.Add(obj);
        }
        finally
        {
            rwlock.ReleaseLock();
        }
    }

    [SqlProcedure(Name="MyCLRProc")]
    public static void MyCLRProc()
    {
        rwlock.AcquireReaderLock(1000);
        try
        {
            SqlContext.Pipe.Send(string.Format("items in list: {0}", list.Count));
        }
        finally
        {
            rwlock.ReleaseLock();
        }
    }
}

我在 SQL CLR 中使用这些东西,它可以正常工作。

3
不难想象,您可以创建静态成员。但是,对于具有SAFEEXTERNAL_ACCESSPERMISSION_SET的程序集,它们需要标记为readonly。只有被标记为UNSAFE的程序集才能拥有可写静态成员。这种限制是由于静态成员的本质:它们在线程和会话之间共享。
当方法首次访问程序集时,程序集将被加载。它被所有会话共享使用,这就是为什么只有静态方法可用的原因。想法是编写函数而不是应用程序,所以保持状态没有太大用处。如果各个会话彼此覆盖,则很容易(虽然肯定不总是)导致不可预测的行为。因此,除非你自己编写并处理并发,否则不会处理并发。
应该期望一旦加载,类(及其所在的应用程序域)将保留在内存中,直到SQL Server服务重新启动或更改了PERMISSION_SET值。但这并不是保证。根据这个页面Memory Usage in SQL CLR
当服务器上存在内存压力时,SQL CLR将尝试通过显式运行垃圾收集并在必要时卸载appdomains来释放内存。
因此,在静态成员方面您提出了两个正确的观点:
  • 它们可用于缓存(非常酷)
  • 它们可能更加危险:
    • 他们可能会导致意外的行为
    • 它们可以占用内存,因为没有固有机制或自然事件来清理它们,因为类保持活动状态。
此外,CLR例程可用的内存量因SQL Server是32位还是64位以及是否使用SQL Server 2005/2008/2008 R2或使用SQL Server 2012/2014而有很大差异。有关SQLCLR可使用多少内存的更多信息,请查看SQL Server 2012 MemoryMemory Usage in SQL CLR(与上面引用相同)。

1

0

来自C#规范3.0(5.1.1)

静态变量在其所属类型的静态构造函数(§10.12)执行之前就已经存在,并且在相关应用程序域停止存在时停止存在。

显然,它不会在每次调用后关闭整个应用程序域,因为这会有点低效。因此,是的,只要数据库没有停止或重新启动,这些静态对象就会一直存在。


我希望不是这样,但是我想看到一个明确的“是”或“否”,特别是针对SQL Server(它肯定比典型的.NET运行时更受限制)。 - Craig Walker
@CraigWalker和Tamas:关于“是的,只要数据库不停止或重新启动,这些静态对象就会一直存在”的说法,这是不正确的。AppDomains 可以因各种原因而卸载,包括:**1)由于内存压力自动卸载,2)当程序集或数据库的安全设置发生更改时,3)如果有人执行 DBCC DROPSYSTEMBUFFERS('ALL'),以及4)**可能还有其他1或2种方式。 - Solomon Rutzky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接