SQL Server 2005(Yukon CLR)基本架构

SQL Server 2005(Yukon CLR) 基本架构

概述

随着 SQL Server “Yukon” Beta1 (也许也有人说: SQL Server2005 )的推出 , 人们发现微软在 Yukon 中集成了非常多的新功能,其中最引人瞩目的是在数据库引擎中集成了对 Windows .NET Framework Common Language Runtime (CLR 公用语言运行环境 ) 的支持。在 2000 年 7 月的专业开发者大会( PDC )上,微软第一次向世界展示了这个新特征。

作为 SQL Server 2000 的开发人员,编写数据库的应用程序往往只局限于 T-SQL ,而宿主了 CLR 的 Yukon ,引入了许多强大的新特征,我们可以用 Visual Basic.NET, C# 等面向对象的语言完成以前 T-SQL 难于完成的任务。比如,以前我们如果要调用一些系统函数( WIN32API 或 COM 组件),我们必须写扩展存储过程,通过 ODS(Open Data Services) 开放式数据服务层和数据库引擎交互,而现在我们通过 CLR 提供的托管对象可以非常方便的调用 Windows .NET Framework Class Library (FCL).NET 框架类库。这些托管对象包括:

· 托管存储过程

· 托管函数

· 托管触发器

· 自定义复杂数据库类型

· 自定义复杂类型索引

· 自定义集合函数

首先 , 我们要了解数据库引擎集成 CLR 的优势和 CLR 可以提供的一些关键特征。

CLR 是一个托管的运行环境,所谓“托管”的意思是许多任务过去需要程序员负责的(比如内存管理)现在可以委托 CLR 来处理。作为托管代码的一部分, CLR 控制代码执行过程中的每一个部分,同时 CLR 是基于类型安全( CLR 扮演了代码验证的角色)和安全许可的环境――彻底地编译为本地代码( Native Code )。 CLR 可以“辨识”出运行的代码是否企图直接处理内存或调用非 .NET 的代码。

.NET 的可执行代码在 CLR 中被装载为程序集( assemblies )的形式。程序集中包括中间语言代码( IL 指令),描述代码的元数据( Metadata )和其它资源 ( 比如:引用其它文件列表 ) ,源代码并不是直接保存在程序集中,而是编译为一种 CLR 可以读懂的中间语言( IL ) , 在程序执行之前 CLR 通过即时编译( JIT )将代码转换为本地代码(通常是 X86 代码)。如果代码在本地运行,这个本地代码会被加载到一段拥有生命周期的缓存,这就是说 .NET 的代码是非解释型的――它在运行时最终会被编译成本地代码。

CLR 拥有自己管理内存的方式:如果一个对象或一组对象没有被其它运行的代码引用,从理论上说要回收所有不在使用的内存,但是对于 CLR ,内存只是在运行资源不足时才会被回收, CLR 在需要额外的内存资源时才调用垃圾收集器,这种内存管理机制使程序员不用担心内存泄漏。

所有的代码都运行在自己的应用程序域( Application Domain )。在 CLR 环境中,应用程序域的作用是在单个进程中宿主不同的应用程序域,用来隔离不同的执行代码 ―― 换句话说,在一个应用程序域中的运行失常的代码不会影响其它应用程序域中运行的代码。 每个进程中都存在至少一个拥有 CLR 的应用程序域, 当然, 也可以有多个――这是由寄宿进程和运行的托管代码决定的。当一个程序集被 CLR 加载时,是指被加载(即时编译( JIT ))到一个指定的应用程序域中,同时这个程序集也可以被加载到同一个进程中不同的应用程序域中。也可以加载一个中立的程序集――在这种情况下,一个程序集的单独备份可以为一个进程中所有的用户提供服务。

寄宿

宿主了 CLR 的 Yukon 数据库引擎有一些特殊需求。数据库引擎对于资源管理是非常谨慎的――比如:通过直观推断数据库使用的资源变化,在不同的缓存之间移动内存。因此,应该在数据库引擎和 CLR 的资源管理方式保持某种默契关系。

在 CLR 的 1.0 和 1.1 的版本中, CLR 提供一个 API 来加强进程的控制――比如判断 CLR 线程池的大小。当然,这个 API 并非强大到可以替代 CLR 在 Yukon 中的作用, 比如:考虑以下几种情况:

内存管理

Yukon 数据库引擎完全控制已经被分配的内存空间,在不同的缓存之间切换,比如:存储过程缓存和数据缓存。在这种情况下, Yukon 数据库引擎需要有更加灵活的方法来运行 CLR 的垃圾收集器,同时对 CLR 垃圾收集器发生的条件,分配或释放多少内存产生影响。

线程管理

CLR 通过线程技术完成异步任务和多元化的并发操作。 CLR 拥有自己的托管线程池,用来计划执行线程池中的任务。然而, SQL Server 使用更精致的纤程机制( mechanism of fibres )来应对可能的巨量并发请求,因此, SQL Server 需要接管 CLR 的线程管理。

并且,由于存在 CLR 的运行机制和 SQL Server 的需要之间的微妙关系,我们意识到这需要对 CLR 的寄宿接口进行扩展。因此随着 Whidbey (是微软 Visual Studio .NET 的下一代版本)的发布 .NET 会提供更多和更强大的寄宿 API ( Yukon 发布的 .NET 版本和 Yukon 支持的 .NET 版本将保持一致)

Whidbey 寄宿 API 的特征

在 Whidbey 中,有 7 处寄宿 API 的关键部分得到了扩展:

1. 内存管理

现在 CLR 允许宿主机( Yukon )取代 WINDOWS 和 C 运行库分配常规内存,因此现在宿主机可以控制和判断内存的分配或释放多少。同时,宿主机可以用自己的内存通知机制取代标准的内存通知机制,从而可以触发垃圾收集器。宿主机也可以不进行内存分配,这时 CLR 会警告宿主机内存分配失败的结果,这样宿主机可以采取相应的策略。

Yukon 通过这种策略施加对内存分配的影响,这样来控制缓存的大小和预防潜在地可能造成内存分页的操作。

2. 线程

现在, CLR 从线程中抽象出一个新的概念叫任务( Task )。宿主机可以控制任务的分配和存储,包括任务的开始,结束和同步等等。同时 CLR 和宿主机相互告知显著的事件(比如当任务从可执行到不可执行的状态改变,反之亦然,宿主机也要通知 CLR )。宿主机可以提供配置 CLR 线程池工具。这样的集成级别允许 Yukon 延续自己的轨迹同时允许 CLR 运行 2 个异步任务。

3. I/O 实现

寄宿 API 现在可以为了 CLR 的利益配置宿主机 I/O 同步,在 I/O 操作完成后通知 CLR 。这样 Yukon 可以完全了解进程内所有 I/O 的操作。

4. 同步

如果 CLR 放弃对任务的控制,就必须运许宿主机提供同步不同任务的手段。因此,宿主机必须提供一个方法来建立:临界区( critical section ),互斥体( mutexes ),事件( events ),读写锁( reader/writer locks )和监控器( monitors )。 宿主机通过这些控制方式,就完全可以检测死锁操作。这样 Yukon 就可以正确的处理 CLR 请求的计划任务从而提高整体运行的可靠性,解决同步过程中的死锁。

5. 托管和非托管

宿主机托管代码完成 P/Invoke 调用,转换为本地机器码时可能会有问题,因为目前还没有在代码运行时检测何种锁被丢弃的方法。新的宿主 API 中允许任务在脱离 CLR 控制时通知宿主机。比如:当代码运行在 CLR 外部时, Yukon 会调整任务到 non-fibre scheduled 任务。同样这些非托管的代码块对数据库引擎是透明的-例如:数据访问的 API ,这些和其它的非托管代码是区别对待的。

6. 应用程序域中性代码

应用程序域中性允许加载一个程序集( assembly )时所有的应用程序域共享一个单独的即使编译的程序集镜像代码。在 Whidbey 以前版本, CLR 有 3 个选项:加载所有程序集的应用程序域中性;不加载程序集的应用程序域中性;只加载强名称程序集的应用程序域中性。

人们已经意识到这并没有为宿主机提供足够好的控制粒度。一个自己拥有一套系统程序集的宿主机也可能需要加载应用程序域中性,而所有用户的程序集,不论是强名称的还是弱名称的,可以被分别加载到每个应用程序域。这样在一个应用程序域关闭时可以卸载用户的程序集而保留系统的程序集。

Yukon 在加载所有的用户程序集到数据库的应用程序域,正是通过这个特征加载进程内提供者程序集和其它应用程序域中性的系统程序集,

7. 程序集查解( Assembly Resolution )

对于一个宿主机可以通过挂接应用程序域加载失败来定制程序集查解。这样做,宿主机就能将程序集加载到一个托管的字节数组并且传递这个数组到程序集分解器。这种机制对于 Yukon 来说还不够灵活,因为大多数的程序集是通过自己定制的方法加载。允许正常程序集查解失败而且还要传递字节数组,这些内存复制的操作需要消耗大量的性能开销。因此,寄宿 API 必须允许更多和程序集分解器的交互。

目前,寄宿 API 允许宿主机决定是否需要 CLR 执行程序集分解或者需要自己确保加载,这样可以将程序集返回非托管缓存来防止需要额外的内存复制。

Yukon 不是在文件系统中保存用户的程序集而是在数据库中,因此 Yukon 不是使用标准的程序集查解而是使用自己机制自动加载用户的程序集。

Yukon 使用的 C LR 版本是完全紧密集成在 Whidbey ,而不使用机器上最新的版本,例如:尽管 Yukon 可以执行以前版本的 CLR 编译的代码,但只要 Whidbey 兼容该代码就可以了。幸运地是 CLR 已经努力保持对前面版本的兼容,尽可能的将公司在 Yukon 下运行的大量托管代码在 Whidbey 平台下不需要重新编译。

程序集管理

下面我们来关注一下 CLR 是如何在 Yukon 中运行的?我们需要了解 Yukon 如何管理和保存程序集代码。 Yukon 不依赖标准的程序集控制进程(当一个程序集要加载时, CLR 接管定位这个程序集的进程)而是将需要的程序集保存到自己的数据库中。这样可以建立完整的程序集的数据库备份,而不必引用在数据库备份和恢复时可能改变的文件系统程序集。

使用 CREATE ASSEMBLY 命令可以将程序集加到数据库中,例如:

CREATE ASSEMBLY

 1<assembly identifier="">
 2
 3FROM <path assembly="" file="" on="" system="" to="">
 4
 5这个命令不仅将该程序集加载到数据库,而且还包括该程序集调用的其它非系统程序集。比如有一个程序集  Customer  需要调用另外一个程序集  Util  : 
 6
 7CREATE ASSEMBLY Customer 
 8
 9FROM ‘C:\build\customer\customer.dll’ 
10
11这个命令将  Customer  程序集和  Util  程序集都保存到数据库中。任何  Customer  和  Util  调用的系统程序集都不需要加入到数据库,因为  Yukon  已经知道这些系统程序集。系统程序集列表是不可配置的。对于  Yukon  来说是区别对待用户程序集和系统程序集,系统程序集是从文件系统加载的标准程序集。 
12
13程序集标识符(在这个实例中是:  Customer  )在数据库中必须是唯一的,  Yukon  根据这个标识符代替原来的  4  段程序集名称,原始的程序集名称被存储而且  Yukon  保证程序集在一个数据库中只允许保存一次。 
14
15程序集保存在数据库中,以通过  sys.assemblies  系统视图查看(如</path></assembly>
Published At
Categories with 数据库类
Tagged with
comments powered by Disqus