(转载)

假如,我们现在利用Select语句从数据库查询数据,Oracle数据库是如何运作的呢?从中我们可以领悟到什么呢?下面,就结合一条简单的select语句,看看Oracle数据库后台的运作机制。这对于我们之后的系统管理与故障排除非常有帮助。

  第一步:客户端把语句发给服务器端执行。

  当我们在客户端执行select语句时,客户端会把这条SQL语句发送给服务器端,让服务器端的进程来处理这语句。也就是说,Oracle客户端是不会做任何的操作,他的主要任务就是把客户端产生的一些SQL语句发送给服务器端。虽然在客户端也有一个数据库进程,但是,这个进程的作用跟服务器上的进程作用事不相同的。服务器上的数据库进程才会对SQL语句进行相关的处理。不过,有个问题需要说明,就是客户端的进程跟服务器的进程是一一对应的。也就是说,在客户端连接上服务器后,在客户端与服务器端都会形成一个进程,客户端上的我们叫做客户端进程;而服务器上的我们叫做服务器进程。所以,由于所有的SQL语句都是服务器进程执行的,所以,有些人把服务器进程形象地比喻成客户端进程的“影子”。

  第二步:语句解析。

  当客户端把SQL语句传送到服务器后,服务器进程会对该语句进行解析。同理,这个解析的工作,也是在服务器端所进行的。虽然这只是一个解析的动作,但是,其会做很多“小动作”。

  1、查询高速缓存。服务器进程在接到客户端传送过来的SQL语句时,不会直接去数据库查询。而是会先在数据库的高速缓存中去查找,是否存在相同语句的执行计划。如果在数据高速缓存中,刚好有其他人使用这个查询语句的话,则服务器进程就会直接执行这个SQL语句,省去后续的工作。所以,采用高速数据缓存的话,可以提高SQL语句的查询效率。一方面是从内存中读取数据要比从硬盘中的数据文件中读取数据效率要高,另一方面,也是因为这个语句解析的原因。

  不过这里要注意一点,这个数据缓存跟有些客户端软件的数据缓存是两码事。有些客户端软件为了提高查询效率,会在应用软件的客户端设置数据缓存。由于这些数据缓存的存在,可以提高客户端应用软件的查询效率。但是,若其他人在服务器进行了相关的修改,由于应用软件数据缓存的存在,导致修改的数据不能及时反映到客户端上。从这也可以看出,应用软件的数据缓存跟数据库服务器的高速数据缓存不是一码事。

  2、语句合法性检查。

  当在高速缓存中找不到对应的SQL语句时,则数据库服务器进程就会开始检查这条语句的合法性。这里主要是对SQL语句的语法进行检查,看看其是否合乎语法规则。如果服务器进程认为这条SQL语句不符合语法规则的时候,就会把这个错误信息,反馈给客户端。在这个语法检查的过程中,不会对SQL语句中所包含的表名、列名等等进行SQL他只是语法上的检查。

  3、语言含义检查。

  若SQL语句符合语法上的定义的话,则服务器进程接下去会对语句中的字段、表等内容进行检查。看看这些字段、表是否在数据库中。如果表名与列名不准确的话,则数据库会就会反馈错误信息给客户端。

  所以,有时候我们写select语句的时候,若语法与表名或者列名同时写错的话,则系统是先提示说语法错误,等到语法完全正确后,再提示说列名或表名错误。若能够掌握这个顺序的话,则在应用程序排错的时候,可以节省时间。

  4、获得对象解析锁。

  当语法、语义都正确后,系统就会对我们需要查询的对象加锁。这主要是为了保障数据的一致性,防止我们在查询的过程中,其他用户对这个对象的结构发生改变。对于加锁的原理与方法,我在其他文章中已经有专门叙述,在这里就略过不谈了。

  5、数据访问权限的核对。

  当语法、语义通过检查之后,客户端还不一定能够取得数据。服务器进程还会检查,你所连接的用户是否有这个数据访问的权限。若你连接上服务器的用户不具有数据访问权限的话,则客户端就不能够取得这些数据。故,有时候我们查询数据的时候,辛辛苦苦地把SQL语句写好、编译通过,但是,最后系统返回个“没有权限访问数据”的错误信息,让我们气半死。这在前端应用软件开发调试的过程中,可能会碰到。所以,要注意这个问题,数据库服务器进程先检查语法与语义,然后才会检查访问权限。

  6、确定最佳执行计划。

  当语句与语法都没有问题,权限也匹配的话,服务器进程还是不会直接对数据库文件进行查询。服务器进程会根据一定的规则,对这条语句进行优化。不过要注意,这个优化是有限的。一般在应用软件开发的过程中,需要对数据库的sql语言进行优化,这个优化的作用要大大地大于服务器进程的自我优化。所以,一般在应用软件开发的时候,数据库的优化是少不了的。

  当服务器进程的优化器确定这条查询语句的最佳执行计划后,就会将这条SQL语句与执行计划保存到数据高速缓存。如此的话,等以后还有这个查询时,就会省略以上的语法、语义与权限检查的步骤,而直接执行SQL语句,提高SQL语句处理效率。

 第三步:语句执行。

  语句解析只是对SQL语句的语法进行解析,以确保服务器能够知道这条语句到底表达的是什么意思。等到语句解析完成之后,数据库服务器进程才会真正的执行这条SQL语句。

  这个语句执行也分两种情况。一是若被选择行所在的数据块已经被读取到数据缓冲区的话,则服务器进程会直接把这个数据传递给客户端,而不是从数据库文件中去查询数据。若数据不在缓冲区中,则服务器进程将从数据库文件中查询相关数据,并把这些数据放入到数据缓冲区中。

  这里仍然要注意一点,就是Oracle数据库中,定义了很多种类的高速缓存。像上面所说的SQL语句缓存与现在讲的数据缓存。我们在学习数据库的时候,需要对这些缓存有一个清晰的认识,并了解各个种类缓存的作用。这对于我们后续数据库维护与数据库优化是非常有用的。

  第四步:提取数据。

  当语句执行完成之后,查询到的数据还是在服务器进程中,还没有被传送到客户端的用户进程。所以,在服务器端的进程中,有一个专门负责数据提取的一段代码。他的作用就是把查询到的数据结果返回给用户端进程,从而完成整个查询动作。

  从这整个查询处理过程中,我们在数据库开发或者应用软件开发过程中,需要注意以下几点:

  一是要了解数据库缓存跟应用软件缓存是两码事情。数据库缓存只有在数据库服务器端才存在,在客户端是不存在的。只有如此,才能够保证数据库缓存中的内容跟数据库文件的内容一致。才能够根据相关的规则,防止数据脏读、错读的发生。而应用软件所涉及的数据缓存,由于跟数据库缓存不是一码事情,所以,应用软件的数据缓存虽然可以提高数据的查询效率,但是,却打破了数据一致性的要求,有时候会发生脏读、错读等情况的发生。所以,有时候,在应用软件上有专门一个功能,用来在必要的时候清除数据缓存。不过,这个数据缓存的清除,也只是清除本机上的数据缓存,或者说,只是清除这个应用程序的数据缓存,而不会清除数据库的数据缓存。

  二是绝大部分SQL语句都是按照这个处理过程处理的。我们DBA或者基于Oracle数据库的开发人员了解这些语句的处理过程,对于我们进行涉及到SQL语句的开发与调试,是非常有帮助的。有时候,掌握这些处理原则,可以减少我们排错的时间。特别要注意,数据库是把数据查询权限的审查放在语法语义的后面进行检查的。所以,有时会若光用数据库的权限控制原则,可能还不能满足应用软件权限控制的需要。此时,就需要应用软件的前台设置,实现权限管理的要求。而且,有时应用数据库的权限管理,也有点显得繁琐,会增加服务器处理的工作量。因此,对于记录、字段等的查询权限控制,大部分程序涉及人员喜欢在应用程序中实现,而不是在数据库上实现

posted @ 2008-09-11 00:39 InsistYML 阅读(117) | 评论 (0)编辑
(转载)摘要:本文深入探讨了 SQL Server 体系结构的工作原理。其中介绍了数据库引擎的增强功能及其使用技巧,并提供了相关信息的链接。深入了解 SQL Server 的引擎有助于数据库管理员(数据库系统工程师)在设计、构建或改进数据库系统时充分利用 SQL Server 的优势。虽然本文主要面向数据库专业人士,但也可用于教学或营销目的。 

  一. 简介

  本文描述 Microsoft? SQL Server? 2000 中新增的存储引擎功能,并提供相关的使用技巧,同时探讨存储引擎的工作原理。初步掌握存储引擎的工作原理可使您最大限度地发挥 SQL Server 的性能。

  当今,人们的注意力集中在应用程序的高可扩展性方面。数据库的设计和实施周期不断缩短,而且由于开发需求的不断变更以及产品使用的增长,又使得数据库在不断地演变发展。为满足可扩展性、可用性和易于使用等方面的要求,需要有一种具有应变能力和灵活性的数据存储引擎。

  SQL Server 2000 的不同版本可支持不同规模的系统,其范围从用于 Pocket PC 的小型移动系统到运行在群集 Windows? 2000 Datacenter Server 上容量达数 TB 的高可用性事务处理或决策支持系统。所有这些系统都满足使命关键的业务系统所要求的灵活性、安全性和可靠性要求。

  由于存储引擎操作的智能化和自动化,因而可以在各种用途和规模的项目中部署 SQL Server 2000 应用程序。高度完善的体系结构改善了性能、可用性和可伸缩性。

  可用性

  由于在与物理文件进行交互时采用了新的算法,可靠性和并发能力得到了增强。这些算法减轻了日常维护工作,使您不必再运行数据库控制台命令 (DBCC)。然而,DBCC 仍旧可以使用,并且新增的 DBCC CHECK 命令的运行不会干扰联机处理工作。

  可扩展性

  存储子系统(由物理数据库文件及其在磁盘上的布局组成)既支持小型数据库,也支持超大型数据库。SQL Server 当前可支持最高达 64 GB 物理内存 (RAM) 和 32 颗处理器。

  易于使用

  增强的管理功能可帮助数据库管理员 (DBA) 实现服务器的自动管理和集中管理。这也使得对远程服务器和应用程序的维护变得容易,DBA 无需访问每个站点。由复杂算法管理的服务器配置可动态对服务器使用方案的变化作出反应,从而使 DBA 可以将精力集中在数据库管理和优化等任务上。

  二. 存储引擎的增强功能

  SQL Server 2000 的关系数据库服务器包括两个主要部分:关系引擎和存储引擎。两个引擎独立工作,它们通过本地数据访问组件(如 OLE DB)进行交互。关系引擎提供访问存储引擎的接口,而存储引擎由与基本数据库存储组件和功能进行交互的服务构成。

  存储引擎的主要任务包括: 
  提供改善管理存储组件易用性的功能 
  管理数据缓冲和对物理文件的所有 I/O 操作 
  控制并发、管理事务、锁定和日志记录 
  管理用于存储数据的文件和物理页 
  恢复系统故障 

  SQL Server 2000 中的存储引擎提供概念简单而实际操作比较灵活的新增功能,同时减少了详细规划容量和优化性能的工作。SQL Server 2000 可以对其环境作出反应,并准确而快速地适应数据库使用上的变化。这种技术上的突破已将数据库管理的重点转移到作为服务的数据简化上。SQL Server 2000 DBA 可以将注意力集中到设计一个可对数据流和数据使用作出响应的系统上,而不再需要将时间浪费在优化个别参数上。 

 

2003-6-7 19:21:00    
 查看评语???     

 2003-6-7 19:23:01      SQL Server 2000 中的变化建立在体系结构的增强上。这种增强是在 SQL Server 7.0 中引入的,其目的是为后来的改进和创新提供基础。存储引擎小组的主要目的是减少花在定期优化服务器上的时间和精力。由于绝大多数的调优参数设置是基于数据库使用的,所以引擎使用自适应算法动态调整这些设置,使其适合数据库环境。调优参数现在可以按这种方式自动调整,而在早期版本中,它们需要不断调整和测试。您仍可以手动调整调优功能,但 SQL Server 2000 可以完成更多的工作。只有很少的 SQL Server 客户才需要对调优参数进行调整;这种调整工作需要进行仔细的测试,并且需要经验丰富的数据库管理员进行监督。

  下表总结了 SQL Server 2000 存储引擎的主要增强功能。本文的后面将对这些内容进行详细阐述。


功能 描述及益处 
应用程序锁定管理器 如果需要控制对应用程序定义的资源(如表单)的并发访问,新增的存储过程允许您使用 SQL Server 的应用程序锁定管理器锁定这些资源。 
数据库控制台命令 (DBCC) DBCC CHECK 命令可以在联机处理过程中运行,且不会中断更新。新增的功能允许校验物理页的一致性,以检测硬件引起的错误。在 SQL Server 2000 企业版中,DBCC 可以在多个处理器上以并行方式运行。 
数据库选项 所有的数据库选项都可使用 ALTER DATABASE 进行修改。此功能简化了管理工作。 
差异备份 在 SQL Server 2000 中,由于改进后的功能可以在更广的层次上跟踪数据库的更改,差异备份的速度更快。 
动态调优 通过使用动态自适应算法,服务器可以自动调整以前是静态不变的配置设置。管理控制仍可用于管理系统范围的资源,但以后您不必使用它们。手动设置参数可以在它们的约束边界内动态调整。 
行内文本 在包含较小且使用频繁的文本列的表中,较小的文本值可以与标准数据行存储在同一页中,而不必存储在文本值页中。如果表中包含这种被频繁访问的文本数据,此功能可减少大量磁盘 I/O 操作。 
并行建立索引 在企业版中,索引建立过程自动使用为并行处理配置的所有处理器,减少了建立索引所需的时间;例如,在一台八处理器的服务器中,时间缩短到原来的六分之一。索引建立过程还可利用内存和 tempdb 中的可用资源。 
预读索引 读取索引的功能得到增强,提高了扫描索引的性能。 
重组索引 对 DBCC SHOWCONTIG 进行的改进提供了有关索引碎片的详细信息。新增的 DBCC 命令 INDEXDEFRAG 可联机重组索引页,且不会中断数据库服务,也不会导致数据库一致性或故障恢复方面的问题。 
降序排列索引中的键列 索引中的各个键列可单独指定为升序或降序。 
KILL 命令 此命令现在报告完成的进度。如果此命令正在等待另一个进程(例如回滚),则可以查看命令执行的进度。改进后的命令可以用于停止 Microsoft 分布式事务协调器 (MS DTC) 事务,而这些事务并不与特定会话相关联。 
对高内存量的支持 Windows 2000 中的技术改进了使用大量内存的企业版系统的性能。通过使用 Windows 2000 的 AWE 扩展,SQL Server 2000 可至多支持 64 GB 物理内存 (RAM)。 
锁定 改进后的锁定管理器可探测到其它资源(如线程和内存)的死锁情况。并发能力的改善同时也降低了死锁的发生,从而进一步加强了 SQL Server 2000 的可扩展性。 
逻辑日志标记 Transact-SQL 命令可在日志中创建书签,使数据库可以恢复到书签所示的时点。此功能还可同步恢复用于同一应用程序的多个数据库。 
联机索引重组 对 DBCC SHOWCONTIG 进行的改进提供了有关索引碎片的详细信息。新增的 DBCC 命令 INDEXDEFRAG 可联机重组索引页,且不会中断数据库服务,也不会导致数据库一致性或故障恢复方面的问题。 
优化的预读 I/O 操作 对于扫描所涉及的每个文件,SQL Server 2000 都会同时发出多个连续的、预读读取操作。为提高性能,查询优化器在扫描表和索引时使用连续的预读 I/O 操作。 
密码保护备份 可使用密码保护备份媒体和单独的备份。这样可以防止未授权的用户恢复备份并访问数据库。 
故障恢复模式 通过使用故障恢复模式,可以选择数据库的日志记录级别。这样事务日志管理更加灵活。故障恢复模式可联机更改,以适应一天当中不断变化的数据库使用。 
共享表扫描 在企业版中,对某个表的多次扫描可以利用其他进行中的对该表的扫描,减少了对磁盘的实际 I/O 操作。 
收缩日志 收缩日志命令可在更多的情况下立即运行。即使不能立即收缩日志,SQL Server 也会提供建议性的反馈,说明在继续或完成收缩操作之前必需完成的操作。 
快照备份 对第三方供应商提供的快照备份的支持进一步得到加强。快照备份采用存储技术,可以在几秒内备份或恢复整个数据库。如今,可以将这些备份与常规事务日志及差异备份相结合,为 OLTP 数据库提供完整的保护。此功能对于中型或大型数据库是非常有益的,因为在这种环境中可用性是非常重要的。 
节省空间的空表和索引 SQL Server 2000 不为空表和索引分配磁盘页。SQL Server 7.0 会给空表和索引分配多达三个磁盘页。 
前 n项排序 此新功能可优化前 n 项值的检索(例如,SELECT TOP 5 * FROM tablename)。 
Xlock SQL Server 2000 提供这种新的 Transact-SQL 锁定提示。它可用于明确调用互斥的、事务级别的页或表锁。 

  SQL Server 2000 中增加了许多功能,这些功能使数据交互更为有效,使管理更加灵活。以下部分将详细介绍这些增强功能,并提供相关的使用技巧。
三. 与数据进行交互

 

  在 SQL Server 2000 中,存储引擎的功能得到增强,在与数据进行交互时可提供更好的可扩展性及性能。了解这些增强的功能有助于更有效地使用 SQL Server。

  无论是通过用户界面还是自动执行的任务,数据交换都从查询开始。数据请求先被传递到关系引擎,然后关系引擎与存储引擎进行交互以获取数据,并将其传递给用户。无论是从用户还是 DBA 的角度来看,存储和关系引擎的功能是无法区分的。

  更有效地读取数据

  数据通过一系列事务在服务器和用户之间传递。应用程序或用户启动任务后,数据库将其传递给查询处理器进行处理,然后返回最终结果。查询处理器通过接收、解释和执行 SQL 语句来完成任务。

  例如,当用户会话发出 SELECT 语句时,将会执行以下步骤: 

  关系引擎将语句进行编译和优化后,将其纳入执行计划(获取数据所需的一系列步骤)。然后,关系引擎运行执行计划。执行步骤包括通过存储引擎访问表和索引。 

  关系引擎解释执行计划,调用存储引擎以收集所需的信息。 

  关系引擎将存储引擎返回的所有数据组合到最终的结果集中,然后将结果集返回给用户。 

  为提高此过程的性能,进行了两项改进。在 SQL Server 2000 中,关系引擎将核准查询谓词的工作交由存储引擎完成,这样在该过程中这些谓词能尽早得到处理,因而提高了存储和关系引擎之间数据交换的效率。此项改进使核准查询的效率显著提高。

  增强的 Top n 功能

  另一项改进是存储引擎处理从结果集中选择前 n 个记录的方式。在 SQL Server 2000 中,新的 Top n 引擎将分析类似以下语句的最佳操作路径:
SELECT top 5 * from orders order by date_ordered desc
在本例中,如果必须搜索整个表,则引擎会分析数据并只跟踪高速缓存中的前 n 项数值。这种方式将大幅提高上述 SELECT 语句的性能,因为只有前 n 项值需要排序,而非整个表。

  共享扫描

  在 SQL Server 2000 企业版中,两个或多个查询可共享正在进行的表扫描,此项功能可改善大型 SQL Server 2000 数据库的性能。例如,当查询使用无序扫描查询一个很大的表时,高速缓存中的页面将被清空,以便为流入数据腾出空间。如果另一个查询已经开始,对同一表的第二次扫描就会使磁盘 I/O 再次检索这些页面。在频繁进行表扫描的环境中,当两个查询搜索相同的数据页时,这将导致磁盘颠簸。 


图 1:共享扫描效果

  优化的进程可减少由此类数据访问模式造成的磁盘 I/O 操作。对表的第一个无序扫描将从磁盘中读取数据;后续的对同一表的无序扫描不必再读取硬盘,而只需使用已在内存中的信息。参见图 1。在对同一个表同时进行多个扫描操作时,此同步过程可将性能提高至多八倍。此项改进的效果在大型决策支持查询中更加明显,因为整个表的大小远远大于高速缓存的大小)。 

  当查询没有更有效的执行计划时,存储引擎将使用共享扫描功能协助查询。此功能的目的是提高频繁读取大型表的性能。当查询处理器确定最佳执行计划中包含表扫描时,将调用此功能。然而,尽管可以使用查询或索引优化强制进行共享扫描时,但强制进行表扫描并不会提高性能。此时使用状态良好的索引完成同样的工作效果不会差,而且可能会更好。

  并发

  为了在多个用户进行数据交互的同时维护事务的一致性,存储引擎会锁定资源以管理行、页、键、键范围、索引、表和数据库的依存性。通过在更改资源时将其锁定,引擎可防止多个用户同时更改同一数据。SQL Server 中的锁可在不同粒度级别上动态应用,以选择事务所需的限制最小的锁。

  在 SQL Server 2000 中,并发方面的改进进一步减少了死锁,避免了对资源不必要的锁定。例如,增强的锁定管理器可了解被竞用的其它资源(如线程和内存)。这种新的功能可帮助数据库管理员确定更广范围内的设计或硬件限制。 

 
 2003-6-7 19:30:13    锁定管理器中新增的 Transact-SQL 接口支持在编程代码中使用自定义的锁定逻辑。业务逻辑所需的锁可通过在 Transact-SQL 批处理中调用 sp_getapplock 命令来创建,这将允许您指定要锁定的应用程序定义的资源(例如锁定表单,而非数据行)、要使用的锁定模式、超时值以及锁的范围是事务还是会话。当使用新的应用程序锁管理器创建锁后,它们接受 SQL Server 的常规锁管理,如同它们是由存储引擎创建的一样,因此,不必担心当调用事务终止时,应用程序创建的锁仍处于打开状态。

  在 SQL Server 2000 中,用于获取锁的进程将考虑页中的数据是否都已提交。例如,若要对某个表运行 SELECT 语句,而该表中的数据在最近未发生变化(如 pubs 数据库中的表),则该进程不会产生锁,因为最近没有活动事务对表进行过更新。存储引擎是通过将数据页上的日志序列号与当前活动事务相比较来实现上述功能的。如果数据库中的绝大多数数据都早于最早的活动事务,则对于这样的数据库,这一功能将显著减少锁定操作,从而使性能大幅提高。 

  在使用锁保护事务中数据的同时,另一个进程 latching 控制对物理页的访问。闩锁是一种非常轻型的、短期同步对象,它保护事务生存期内不需要锁定的操作。当存储引擎扫描某页时,它先锁住该页,读取行,将行返回给关系引擎,然后再解除对页面的锁定,以使其它进程可以访问同一数据。存储引擎使用称为 lazy latching 的进程优化对数据页的访问,即只在另一个活动进程请求某页时,才释放对该页的锁存。如果没有活动进程请求同一数据页,则在对该页的整个操作过程中,单个闩锁将始终有效。

  为改进系统的并发性能,应将精力集中在数据库系统的设计以及与其相关的代码对象上。SQL Server 2000 支持 TB 级的数据存储,其扩展能力可线性增长,且不受限制。数据库管理员的任务是管理数据库生命周期,即所有数据库组件(从代码到磁盘上的数据存储)的设计和优化周期,以确保设计始终满足服务级协议的要求。

  四. 表和索引

  在物理数据结构方面也进行了改进,提高了设计和维护的灵活性。

  随着表或索引的增长,SQL Server 以八个为一组分配新的数据页;这些数据页称为扩展。虽然 text、ntext 或 image 类型的列可存储在不同的页中,但一行数据不能超出一页,所以它只能拥有 8 KB 数据。拥有聚集索引的表按键的顺序存储在磁盘上。堆是不带聚集索引的表,它们是无序的。记录按插入的顺序存储。

  SQL Server 2000 支持索引视图,在其它数据库产品中常常称为实体视图。在某个视图上创建聚集索引时,该视图将从派生对象转为存储在数据库中的基本对象,并且其结构与带有聚集索引的表相同。索引视图可用于存储预先计算的值或复杂联接的结果,但前提是维护开销不能超过性能上的收益。在 SQL Server 2000 企业版中,只要索引视图可以优化查询计划,查询处理器就会自动使用它。对于很少更改但又经常作为复杂联接或计算查询组成部分的数据,索引视图可改善查询速度。

  行内文本

  行内文本可用于在主页面中存储小文本数据。例如,如果某个表中有一文本列,但文本值通常小到可与行中的其余内容放在同一普通页中,则可以在文本列中设置阈值。阈值用来确定可存储在主页面而非单独的文本页上的最大文本长度。如果大多数数据可放在主页面上,而只有小部分数据比较大,需要创建文本页,采取这种做法就可使性能获得提高。

  若要确定在何种情况下使用此新功能,则需要权衡存储密度(或每个数据页上存储的行数)以及 I/O 性能的改善。例如,某个表中的文本列用于存放注释。该列中有 20% 的文本值较长,而其它 80% 的文本值都小于 100 个字节。对于这种情形,似乎可以采用行内文本解决方案;但是,只有在这样的列中的数据被频繁访问时才应考虑使用行内文本。如果用户频繁访问此表,但只在进行特殊搜索时才查看此注释列,则使用行内文本未必是最好的做法。由于每页存储的行数少,所以存储密度会降低;并且由于表包含更多的页,所以表扫描响应时间也会增加。所以,实现行内文本的最好情况是,存在需要频繁访问的文本列,并且该列的许多值都小于 8 K,可以存储在行中。

  新增数据类型

  SQL Server 2000 引入了三种新的数据类型。bigint 是 8 字节整数类型。sql_variant 可以存储不同数据类型的数据值。第三种数据类型 table 可用于优化性能。Table 变量使 tempdb 的使用效率更高,并且比临时表更加快速。与其它变量一样,它们的作用范围是声明它们的批处理。table 变量的功能类似于临时表,但其性能要高于临时表或游标,并且可更加有效地利用服务器资源。通常,在创建与数据库交互的代码时,一定要考虑利用服务器上可用资源的最佳方法。 

  索引

  通过使用索引,可以优化对数据的访问。因为是否建立索引取决于使用情况,所以不正确的索引是造成数据库缓慢最主要的原因。标准的索引维护工作应该包括周期性校验当前索引方案以及通过适当增删索引使其适应当前的系统使用情况。

  SQL Server 2000 中的几个新增功能使索引维护更加有效,索引管理更加容易。这些增强功能减少了磁盘 I/O 操作,从而增加了索引扫描的性能。在范围扫描可以使用辅助索引时,这一功能尤其有用。 

 
 2003-6-7 19:31:33    建立索引

  建立索引时,存储引擎对行进行采样,并计算使用服务器资源建立索引的最佳方法。通过使用选项,可以控制建立索引的方式,因而可选择控制系统资源的分配方式。可以使用这些选项,并结合您在特定数据库系统方面的知识,平衡对于整体系统的性能是至关重要的进程中的资源,从而使建立索引的操作对事务处理的影响最小。

资源 命令 选项 说明 
内存 sp_configure(高级)
index create memory
指定建立索引操作可以使用的内存总量。 
TempDB create index
sort_in_tempdb
从 tempdb 中分配在索引建立期间用于排序的磁盘空间。如果 tempdb 在单独的磁盘上,此命令会产生更高的 I/O 带宽;并且如果数据库所在空间不是非常连续,该命令还可以使索引页的布局在物理上更加连续。 
CPU sp_configure(高级)
最大并行程度
限制在并行操作中(服务器范围)可使用的处理器(CPU) 的个数。 

  大型系统的另一个可扩展性功能是并行索引建立。SQL Server 2000 企业版具有此功能。在发出单个 CREATE INDEX 语句时,此过程将被自动调用。存储引擎计算数据的要求,然后创建单独的线程,每个线程建立一段索引。 


图 2:并行索引优化

  索引建立也可以使用共享表扫描,从而使这一过程进一步优化。

  整理索引碎片

  SQL Server 2000 支持联机索引重组,相比于以前的版本,这是一个非常大的进步。联机索引重组对事务的吞吐量影响非常小,并且可随时停止并重新启动,而不会影响其运行效果。索引重组操作按较小增量进行,并且可完全恢复。

  随着在表中插入、删除和更新信息,聚集和非聚集索引页最终将变得零碎,从而降低对数据的范围查询的效率。因此,定期整理索引碎片是非常有益的。可以使用 DBCC SHOWCONTIG(该命令在 SQL Server 2000 中已有所改进)分析并报告碎片。

  如果确定索引已变为碎片,就可以使用 DBCC INDEXDEFRAG 命令对其进行重组。该命令以逻辑键的顺序记录页,同时压缩可用空间,移动已建立的扩展中的行以满足填充因子设置。通过提高页面中内容的密度以减少数据扫描时读取的页数,从而提高读取性能。如果索引经常得到维护并且其分布不是完全散碎的,那么运行 DBCC INDEXDEFRAG 对联机性能的影响要远远小于重建索引。

  DBCC INDEXDEFRAG 是众多长期运行的管理操作中的一个,它们内部都使用短小的事务。这些短小的事务可最大限度提高服务器中的并发操作,允许操作停止而不影响工作,并且这些事务被全部记录以便在发生故障时进行恢复。
五. 日志记录和故障恢复 

 

  事务日志是一个记录流,它记录了从数据库创建到当前时点对数据库所做的更改。每个记录的操作都创建一个日志记录。日志记录由事务生成,并在事务提交时写入磁盘。相反,被事务修改的数据页不会立即写入磁盘,而是先保留在 SQL Server 的缓冲区高速缓存中,稍后再写入磁盘。推迟将数据写入磁盘可最大限度地提高对数据页进行多路访问的性能,并避免中断扫描。在提交时强制将日志写入磁盘是为了确保在服务器关机时不会丢失已完成的工作。

  故障恢复可确保在将数据库变为联机状态之前保持其在事务上的一致性。如果数据库在事务上是一致的,则所有提交的工作都已生效,而任何未提交的工作都变为无效。日志总是定义数据库的正确视图。简而言之,故障恢复就是将数据与事务日志在某一给定时点保持一致的过程。 

  当 SQL Server 启动时,当数据库被连接时,或在从备份恢复数据库的最后一步时,故障恢复将自动执行。在 SQL Server 启动时执行的故障恢复称为重新启动故障恢复或启动故障恢复。使用备份进行故障恢复通常是由于磁盘发生故障。此类故障恢复称为媒体故障恢复。

  重新启动故障恢复是自动进行的,通常可恢复到最近的时点。在使用备份进行故障恢复时,DBA 可以选择恢复到较早的时点。这种故障恢复需要满足一些限制条件。

  每当启动一个 SQL Server 实例时,启动故障恢复会自动运行,它将回滚上次关闭实例时尚未完成的所有事务。在使用备份进行故障恢复时,DBA 可以选择恢复到较早的时点。这种故障恢复需要满足一些限制条件。无论何种情况,故障恢复操作都基于此目标时点。 

 
 2003-6-7 19:32:58    故障恢复分为两个阶段: 

  恢复所有更改,直到达到事务日志中的目标时点。 

  撤消由在恢复停止点仍处于活动状态的事务所执行的所有操作。

  SQL Server 使用检查点加速重新启动故障恢复。检查点强制将当前缓冲区高速缓存中所有已修改的数据页保存到磁盘上。这将为故障恢复的恢复阶段创建一起点。由于检查点的开销非常大,所以 SQL Server 自动对检查点进行管理,以保证在尽量缩短重新启动所花时间的同时尽可能提高性能。

  在 SQL Server 2000 中,成功完成的写入必须可持久存储在磁盘中。如果使用写缓存磁盘存储,请与您的存储设备供应商联系,确认高速缓存是否容错。容错能力表示高速缓存可不受电源故障或操作员操作的影响。如果缓存没有容错能力,则应不使用。

  逻辑日志标记

  在 SQL Server 7.0 中,已经可以恢复到任何指定时点。如果出现硬件故障,则恢复过程是相当简单的。然而,对数据库的另一种威胁可能是输入了无效的数据,或者有效数据被用户操作所破坏。在这种情况下,需要确定问题发生的开始时间。在 SQL Server 7.0 中,解决这种问题的唯一方法是将日志恢复成数据库副本,直到问题重现,然后再对产品映像执行恢复操作,直到在所发现的问题出现时刻之前的时点。

  在 SQL Server 2000 中,可以在日志中标记事务。之后,如果需要恢复,就可以参考执行时使用的标记,而不必使用规定的时刻。为此,请使用 BEGIN TRANSACTION 的语句和 WITH MARK [说明] 子句。标记存储在 msdb 中。故障恢复可以包括包含标记的事务,也可以恰恰在包含标记的事务前停止。例如,如果某个进程以批处理方式运行并且更改了许多记录,那么可以使用此功能以确保,当进程运行在错误环境中时可以将数据回滚到执行命令的时点。

  标记名称不必唯一。若要指定所需的事务,请指定 datetime 值。该操作的语法为:
RESTORE LOG WITH [ STOPBEFOREMARK|STOPAFTERMARK ] = @TaggedTransaction AFTER @datetime
也可以对分布式事务使用标记(称为分布式标记),以支持将多个相关数据库恢复到事务上一致的状态。相关数据库可以位于 SQL Server 的同一或不同实例上。可以定期对一组数据库设置分布式标记(例如,每五分钟一次)。如果其中某个数据库的事务日志被损坏,则必须将这组数据库恢复到更早的时点。分布式标记可提供这一时点。使用分布式标记,就可以在对多个相关数据库进行备份时不用费心地确定备份的时刻。

  收缩事务日志

  SQL Server 7.0 中不能立即执行日志收缩操作。该操作被推迟到下一次备份或删节事务日志。这种方式使许多 SQL Server 7.0 客户非常烦恼。SQL Server 2000 可以立即收缩日志,并且可在日志备份后指出是否可以进行进一步收缩。这时,可以在日志备份完成后再次运行收缩命令。

  日志大小取决于当前故障恢复模式以及应用程序设计。如果发现需要定期收缩日志,请查明造成问题的原因。应该进一步调查日志添满的原因,而不要只是一味地使用收缩命令维护日志。

  故障恢复模式

  使用 SQL Server 2000 中增加的故障恢复模式可以方便到实施数据保护计划。这些模式都在性能、日志空间要求和媒体(磁盘)故障保护之间进行了取舍。共有三种模式,它们是:简单故障恢复、完全故障恢复和大容量记录。

  选择故障恢复模式时,应考虑数据库的使用情况和可用性要求,同时选择的模式应有助于确定适当的备份和恢复过程。这些故障恢复模式只适用于媒体故障恢复,即使用备份进行故障恢复。重新启动故障恢复所有提交的工作。

  故障恢复模式间的转换非常容易。例如,在大型数据库中,既可使用完全模式,也可以使用大容量记录模式,或同时使用这两种模式。可以在白天使用完全模式,而在夜晚或在包含大容量插入以及重建索引的数据装载过程中使用大容量记录模式。也可以在运行数据装载时切换到大容量记录模式,然后再切换回完全模式,运行事务日志备份,而且能够恢复到模式切换时点,而不必运行完全数据库备份。此功能可更有效地进行大容量处理操作;而所要做的只是将以前的事务日志进行备份。

  要更改故障恢复模式,请使用以下语法:
  ALTER DATABASE SET RECOVERY RecoveryModel

  简单故障恢复模式

  简单故障恢复模式通常需要很少的日志空间,但如果数据或日志文件被损坏,则它造成的潜在工作损失是最大的。因为在这种模式下只记录基本故障恢复所需的事件。使用简单故障恢复模式时,只能进行完全数据库备份和差异数据库备份。在出现故障时,必须重新完成自从上次备份后所有提交的工作。此模式对管理员是最简单的,但并不适用于关键性任务的应用程序,因为这种程序通常不允许丢失已提交的工作。

  此模式类似于 SQL Server 7.0 及以前版本中的 truncate log on checkpoint 选项。 

 
 2003-6-7 19:34:47    完全故障恢复模式

  在完全故障恢复模式中,所有一切都被记录。完全故障恢复模式提供了全面的保护,以防止损坏的数据文件对工作造成损失。如果事务日志被损坏,则从最近一次日志备份后提交的工作都将丢失,并且必须重新手动完成。

  即使使用完全故障恢复模式,也最好使用容错磁盘存储事务日志,以防止数据丢失。完全故障恢复模式还允许恢复到指定的时点。

  大容量记录故障恢复模式

  大容量记录故障恢复模式为大容量操作提供了最高的性能。而且,这些操作在该模式下占用的日志空间要小于在完全故障恢复模式下占用的空间。例如,新页的分配将被记录,而插入页中的数据则不被记录。在 SQL Server 2000 中,大容量操作由大容量装载(BCP 和 BULK INSERT,包括当他们在 DTS 包中运行时)、SELECT INTO、CREATE INDEX、WRITETEXT 和 UPDATETEXT 组成。

  与完全故障恢复模式相比,大容量记录故障恢复模式减少了对大容量操作的日志记录。请记住,在需要进行故障恢复时,如果日志被损坏或在最近一次日志备份后又进行了大容量操作,则在最后一次日志备份后对数据库进行的更改将会丢失。

  此模式不支持恢复到指定的时点,但它允许恢复到包含大容量更改的事务日志备份的末尾。使用大容量记录故障恢复模式进行的事务日志备份包含由大容量操作修改的扩展。此功能改善了对日志传送的支持,因为不用担心备份在大容量操作后会变为无效。SQL Server 维护映射以跟踪修改的数据扩展,这样做,可优化 SQL Server 用于标识更改的进程。

  改善的备份功能

  除引入故障恢复模式以简化常规数据保护外,SQL Server 2000 还改善了管理特性:快照技术、差异备份和安全性都已得到加强。 

  事务日志备份链永远不会断开。在 SQL Server 7.0 中,某些操作(如向数据库中添加文件)会中断日志链,并且要求以后进行完全数据库备份。 

  备份操作不会与应用程序或其他管理操作发生冲突。例如,备份可与大容量操作(如创建索引和批处理装载)同时进行。 

  日志和文件备份可以同时进行。 

  无论系统正在进行何种活动,SQL Server 2000 都对无人值守备份操作实现良好的支持。

  SQL Server 支持与独立硬件和软件供应商共同完成的快照备份和恢复技术。快照备份使得在进行备份时占用的系统资源最少,甚至可以不占用资源。这种技术对于中型或大型数据库尤其有益,因为在这种环境中可用性是非常重要的。这种技术的主要优势在于: 

  可在非常短的时间(通常可以秒计)内创建备份,基本上不会对服务器造成任何影响。 

  可以使用磁盘备份非常快地恢复数据库。 

  另一台主机可创建备份,且不会对生产系统造成影响。 

  可以立即创建生产数据库的副本,以用于报告或测试目的。 

  快照备份和恢复技术是与第三方硬件和/或软件供应商协作共同完成的,这些供应商使用了 SQL Server 2000 为实现该技术而提供的特定功能。备份技术通常使用拆分磁盘镜像集的方法,创建要备份的数据的即时副本。在恢复时,原有数据就可立即投入使用。基本磁盘的同步是在后台进行的,因此几乎可以实现即时恢复。

  差异数据库备份的运行时间与上次完全备份后数据更改的总量成正比。数据更改越少,备份越快。SQL Server 2000 使用映射跟踪自最近一次数据库或文件备份后发生更改的数据扩展,以确保可更有效地定位这些扩展。此外,SQL Server 2000 支持文件差异备份。

  备份仍会收集自最近一次完全备份后对数据库进行的更改,运作方式与故障恢复相同。然而,这种备份是非常快的,因为它们只记录少部分更改过的信息,尤其是当数据库非常大而更改的数据又非常少时。

  为确保安全,可以使用密码保护备份媒体和备份集。这样就可防止未授权的用户在备份中添加数据或恢复数据库。

  六. 增强的管理功能

  在 SQL Server 2000 中,存储引擎的若干管理功能得到了加强。

  数据库验证

  DBCC 提供了各种管理能力,包括验证数据库一致性的 CHECK 命令。

  使用 SQL Server 7.0 和 SQL Server 2000 的经验表明,数据库的不一致性是由硬件问题引起的,但是数据库引擎或应用程序在正常操作中不一定能检测到这种问题。这种情况更可能出现在不经常访问的数据上。为解决这种问题,SQL Server 2000 引入一种检查模式 Physical_Only,可以探测到绝大部分由硬件引发的问题。探测过程非常快,速度与磁盘扫描速度相当,并且其对资源的消耗也很小。

  由于 SQL Server 存储引擎在基础体系结构上的改进(从 SQL Server 7.0 开始),已不必在常规维护时进行数据库验证。然而,Microsoft 仍然将数据库验证工具作为管理任务关键数据的重要组成部分。Microsoft 建议: 

  根据对基本硬件(特别是磁盘子系统)的信心,不定期运行 Physical_Only 检查。 

 
 2003-6-7 19:36:16    在关键时刻,如在硬件或软件升级时,或怀疑出现任何问题时,进行完整数据库检查。 

  Microsoft 不推荐在进行常规维护时执行完整数据库检查。

  SQL Server 2000 还在数据库验证方面作出了如下重大改进: 

  默认情况下,检查可以在联机状态下完成。联机检查对事务工作负载的影响很小。这种影响的大小取决于系统负载、硬件配置和 tempdb 的速度。Microsoft 的实验结果表明,对于中等 OLTP 工作负载(50% 的 CPU 使用率),这种影响为 15% 到 20%。提供的 TABLOCK 选项会强制检查索取共享表锁,这可以使检查的运行速度更快,但会妨碍更新。 

  检查操作在对称的多处理器 (SMP) 计算机中是以并行方式完成的,它受限于在该 SQL Server 实例中设置的最大并行程度。 

  SQL Server 2000 检查命令继续支持 SQL Server 7.0 中引入的修复功能。在某些情形,脱机修复可以替代备份恢复。

  数据库状态控制

  SQL Server 2000 对 ALTER DATABASE 语句进行了改进,改进后的语句允许通过 Transact-SQL 对数据库状态实现更多的控制。现在,所有数据库选项都可通过 ALTER DATABASE 命令进行灵活修改;在以后的版本中,将不再更新 sp_dboption 和 databaseproperty()。Transact-SQL 命令 sp_helpdb 和 DatabasePropertyEx() 提供有关数据库状态的更多信息。

  下表列出了数据库状态选项。


选项类型 可用设置 
用户权限 SINGLE_USERRESTRICTED_USERMULTI_USER 
可用性 ONLINEOFFLINE 
更新能力 READ_ONLYREAD_WRITE 


  SQL Server 还根据数据库中的条件设置以下状态:复原 (restoring)、恢复 (recovering) 和待定 (suspect)。数据库选项可通过以下方式进行设置:ALTER DATABASE 语句的 SET 子句、sp_dboption 系统存储过程或 SQL Server Enterprise Manager(在某些情况下)。

  在数据库状态发生变化后,更改数据库状态的会话仍然保持连接,而与新状态不一致的会话可被终止,并且其事务将被回滚。会话终止选项如下: 

  立即终止 

  在指定时间后终止 

  允许活动进程正常完成 

  检查活动,如果发现活动用户会话,则忽略状态更改 

  如下为语法的两个示例:

  alter database accting set read_only with rollback immediate
  alter database accting set single_user with rollback after 60 seconds

  系统进程 ID 和工作单元

  管理方面的另一个改进是 KILL 命令,该命令在停止进程时使用。改进后的 KILL 命令具有状态反馈。所以,如果要了解 KILL 命令的状态,请运行以下命令:

  KILL SPID WITH STATUSONLY

  在试图停止已由其它 KILL 命令停止的系统进程 ID (SPID) 时,系统将返回相同的状态信息。

  在 SQL Server 2000 中,MS DTC 事务可以在没有相关连接或 SPID 的情况下 存在。因此,在等待事务或工作单元完成之前,连接可由其他进程使用。当 MS DTC 事务管理器发送消息声明任务已完成时,您可以提交事务,也可以回滚事务。这就叫做一个工作单元 (UOW),它是 MS DTC 用于事务的事务标识符。UOW 没有 SPID。 

 
 2003-6-7 19:37:22    动态调优

  在 SQL Server 2000 中,基于使用的性能优化是动态管理的,无需手动调整。静态参数已被去除,但仍然保留了对某些资源的管理控制(例如,设置 SQL Server 可以使用的最大内存数)。相对于根据平均值和估计值进行手工计算的系统,这种方法更加精确,反应更加快捷。这样,您可以将注意力集中在数据库管理的设计方面。传统的数据库系统需要大量的手工管理和调优工作。例如,为了根据使用情况来优化系统,DBA 必须监视系统,不断记录大量的统计数据,以便选择可提供最佳系统性能的静态设置。然后,DBA 要重新评估系统以确定新设置的效果,接着又从头开始调优过程。

  SQL Server 2000 在存储引擎中引入了动态算法,它可主动监视服务器的使用情况,并在内部调整设置。SQL Server 2000 中的动态反馈和分析可将设置保持在绝对优化值的 10% 以内(参见图 3),从而使系统的性能更优,适应性更强。
七. 数据存储组件

 

  SQL Server 2000 协同 Windows 2000 操作系统平衡所有可用 CPU 的工作量。如果正在运行一个特定的 SQL Server 实例,并且其他应用程序未占用相同的资源,请将处理器相关设置保持为默认值,以便使全部的处理器得到充分利用。SQL Server 可利用多个处理器上的并行处理能力执行查询、索引建立、DBCC 和其他操作。SQL Server 2000 标准版最多可支持四个处理器和 2 GB 物理内存 (RAM)。企业版提高到新的水平,支持多达 32 个处理器和 64 GB 物理内存 (RAM)。

  SQL Server 实例的主内存源称为它的内存池。在 SQL Server 实例中几乎所有使用内存的数据结构都是从内存池分配的。从内存池分配的对象示例包括缓冲区高速缓存(其中存储最近读取的数据)和过程高速缓存(其中存储最近的执行计划)。 

  内存池中的分配是高度动态的。为优化性能,SQL Server 不断调整分配给不同区域的内存池大小。例如,当存储的执行计划的数量很少时,会通过将更多可用内存分配给数据高速缓存来调整内存池,从而优化资源的使用。

  SQL Server 2000 尽可能使用内存以减少磁盘 I/O。为此,SQL Server 在物理内存 (RAM) 中使用缓冲区高速缓存装载最近引用的数据,这样,这些数据可被重复使用。减少磁盘 I/O 和提供数据库系统速度的潜在方法是增加 SQL Server 可用的物理内存 (RAM)。

  通常,内存设置不需要任何调整。然而,在某些情况下可以对它们进行控制。例如,当在同一服务器上运行 SQL Server 的多个实例时,特别是在使用故障转移群集时,需要特别关注内存。如果在运行 SQL Server 的服务器上运行其他应用程序,也需要监视内存的使用情况。 

 
 2003-6-7 19:38:17      SQL Server 2000 利用 Windows 2000 的新功能,可寻址超过 3GB 的物理内存 (RAM) 。参见图 4。SQL Server 2000 企业版可以使用 Windows 2000 Advanced Server 或 Windows 2000 Datacenter Server 所允许的内存量。

  文件、文件组和磁盘

  SQL Server 在磁盘文件中存储数据和日志。在基本安装中,默认情况下,创建的数据和日志文件保存在服务器配置中指定的默认位置。然而,为了获得最优的性能和管理能力,可以应用以下几条基本原则: 
尽可能将数据分布到多个磁盘、信道和控制器中。 

  通常,磁盘越多(无论其单个容量),访问磁盘(控制器和信道)的速度以及存储引擎读写数据的速度也就越快。系统使用量越大,数据文件与日志文件的分离程度(将它们存储在不同的物理驱动器上)也就越重要。此外,由于 tempdb 的使用已发生变化,所以应该将 tempdb 存储在大磁盘集上;例如,与数据文件存放在一起或一组磁盘上。 

  使用文件组,使企业数据库更易于管理。 

  每个数据库都以一个默认的文件组开始。由于 SQL Server 2000 可在不附加文件组的情况下高效工作,因此许多系统都无需添加用户定义的文件组。然而,随着系统的增长,使用附加的文件组可提供更高的管理能力,当然这要求由称职的 DBA 实施和维护。

  在 SQL Server 2000 中,如果将数据库上的特定文件组设置为只读,则该文件组中的数据不能更改,但仍可以管理诸如权限等目录信息。

  注意:在 SQL Server 2000 中,数据库引擎中的异步 I/O 操作数实现了动态管理,并且不受使用的文件或文件组个数的影响,这一点与 SQL Server 7.0 相同。

  实施或优化数据库设计时,数据库管理员(数据库系统工程师)需要考虑数据库存储组件的配置,尤其是物理和逻辑磁盘的布局、数据库文件在磁盘中的排列。

  八. 总结

  灵活性的增强和性能控制的提高,使数据库管理员可以在掌握数据库技术的使用技巧和丰富数据库的使用经验时,将精力集中在管理数据库代码、设计和存储组件等方面,并以此作为数据库系统管理的最佳途径。SQL Server 2000 数据库引擎为各种数据库实现提供了通用的可扩展性和灵活性。 

作者:TerryLee
出处:http://terrylee.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2008-08-18 11:19 InsistYML 阅读(70) | 评论 (0)编辑
     摘要: Compilation and Deployment in ASP.NET 2.0By Rick Strahlhttp://www.west-wind.com/Last Update: February 12, 2008Compilation and deployment in ASP.NET 2.0 has brought some of the biggest changes to the A... 阅读全文
posted @ 2008-08-04 16:05 InsistYML| 编辑

(转载)在本篇文章的第一部分:[原创]深入剖析ASP.NET的编译原理之一:动态编译(Dynamical Compilation),详细讨论了ASP.NET如何进行动态编译的,现在我们来谈谈另外一种重要的编译方式:预编译(Precompilation)。

1.为什么要进行预编译

ASP.NET 2.0的编译方式大体可以分成两种:动态编译和预编译,要回答为什么要进行预编译,我们先要看看动态编译有什么不好的地方。我们回顾一下上一篇介绍的ASP.NET进行动态编译的简单的流程:当来自Brower的一个基于aspx的Http request抵达Web server,IIS handle这个request,通过分析注册在IIS中的Application Mapping,将Request 传给aspnet_isapi.dll ISAPI extension。ISAPI extension通过HttpRuntime进入Http Runtime Pipeline,HttpRuntime为每个Request创建一个单独的HttpContext对象,用于保存request的Context信息。在Http Runtime Pipeline中,Http request会被注册的一系列的Http module处理,比如OutputCache Module,Session Module,Authentication Module,Authorization,ErrorHandler Module等等。在Pipeline的终端,ASP.NET需要需要根据request创建对应的HttpHandler对象来处理该Request,并生成结果Response到Client。对于一个基于Aspx的Http request,对应的Http handler对象一般就是一个System.Web.UI.Page对象。

ASP.NET会先判断对应的Page type是否存在于被Cache的Assembly中,如果存在,直接创建Page对象,否则ASP.NET会先对该Page的相关的Source code (包括code behind,html等等) 进行编译,我们也说过这种编译是一Directory为单位的,也就是说,处于同一个Directory下的需要编译的文件会被编译成到同一个Assembly中。编译生成的Assembly会被Cache,用于后续的Request。

正是因为对资源的首次访问会导致一次编译(这样说不太准确,因为动态编译是以directory为单位进行的,应该对对某个Directory下的资源进行首次访问),这样会严重降低Web Application的响应速度。所以我们为了避免这种情况,需要预先对web site进行编译,所以提高web site的响应是进行预编译的最重要的原因。

同时动态编译就以为着Web server上放置的是Source code,而且他们是可被修改的。而对于一个开发完毕的Web Application,我们更希望以Binary Assembly的方式进行部署,这样Server上部署的都是Binary Assembly,不怕被别人篡改而导致系统的崩溃,从知识产权来讲,也更利于保护商业秘密。这也是我们为什么要进行预编译的另一个原因。

下面我们就来讲讲如何进行预编译,以及与编译背后的原理。同时在这里我需要特别提出的是,在上一部分讲的一些术语和原理,比如Preservation file,FastObjectFactory,同样适用于预编译,重复的内容,在这里就不必再介绍了。同时我也将沿用上一部的Sample。如果想看看相关的内容,请参阅[原创]深入剖析ASP.NET的编译原理之一:动态编译(Dynamical Compilation)。

2.In Place Pre-compilation V.S. Pre-compilation for Deployment

对于预编译,有可以分为In Place Pre-compilation和Pre-compilation for Deployment,In Place Pre-compilation很简单,实际上就是把整个Web site编译到我们一个临时的目录下面,这个临时目录也就是我们在介绍动态编译提到的那个临时目录。而且这个编译的方式,包括生成的文件也和动态编译完全一样,唯一不同就是编译的时间:预先编译,编译的范围:整个Web site。这种编译就是你常用的在VS的build。这种编译方式一般用于开发阶段。

为了部署为目的的编译是我们今天讨论的重点,下面我们就着重来讨论Pre-compilation for Deployment。

注:在ASP.NET的编译都是通过一个叫做aspnet_compiler的工具执行的,该工具随ASP.NET 2.0一起发布,你完全可以利用此工具以命令行的方式的执行编译,并通过传递不同的命令行开关设置不同的编译选项。该工具被置于了VS中,使你可以利用VS进行可视化的编译。

3.Non-updatable Pre-compilation V.S. Updatable Pre-compilation

ASP.NET 2.0为我们提供了几种不同方式的预编译和部署。为了弄清楚这些预编译和部署方式,我们先来回顾一下ASP.NET 1.x下的编译方式。我们知道在ASP.NET 1.x时代对整个Web site进行编译,实际上我们只会对所有C#和VB.NET等后台代码进行编译,并生成一个单一的Assembly。而Web page的aspx是不会参与编译的。所以当我们访问一个Web page的时候,ASP.NET必须对aspx进行动态编译。

这一切之所以能够进行是因为Web page采用的是aspx + code behind的模式。

<%@ Page Language="C#" AutoEventWireup="false" 
    Codebehind
="Default.aspx.cs"
    Inherits
="Default" %>

 

 

从上面我们可以看到aspx和Code behind是一种继承的关系,aspx继承和它对应的Code Behind。ASP.NET可以把Code behind和aspx分开进行编译,把它们编译到不同的Assembly中。我们就是上面的Code为例,
我们现在若对该Web site进行编译的话,Default.aspx.cs会被编译到一个Assembly中,假设这个Assembly为App_Web.dll. 我们把该Dll和aspx部署到Production Server上。如果我们现在访问defaut.aspx。ASP.NET
会对aspx进行动态编译,生成的Assembly可以暂时成为App_Web_aspx.dll。对于Default.aspx,如果我们如C#代码来描述的话,应该像下面一样定义:

 这种编译方式,我自己把它叫做对asXx的动态编译。在ASP.NET2.0 中也沿用了这种编译方式。这种编译方式的主要特征是对Code behind和所有的后台代码进行预编余,aspx(确切地说应该是asXx:asax,asmx,asax等)原样部署。由于这种方式的预编译,asXx是可以修改的(当然这种修改是有一定限制的,因为code behind已经编译好了,所以这种修改只可能是和code behind无关的修改),所以又叫做Updatable Pre-compilation。

除了Updatable Pre-compilation之外,ASP.NET还提供另外一种高效的预编译方式,Non-updatable Pre-compilation,之所以叫做不可修改的预编译,这是因为:这种编译方式把asXx、Code behind、后台代码甚至是部分Resource都进行预编译,从而避免了运行时对asXx的动态编译,从而最大程度地提高了整个Web site的响应。在部署的时候,我们除了把生成的Assembly进行部署之外,所有的通过编译生成的asXx也必须进行部署。 不过需要特别说明的是,此时的asXx文件仅仅是一个占位的文件而已,它里面不具有任何HTML。

4.Partial class

在ASP.NET 1.x,由于采用的aspx + code behind的机制,对于任何一个Web page或者其他ASP.NET 基于axXx的对象来说,都是由两个文件、两个class组成。两个文件是指axXx和code behind,两个class是指Code behind定义的继承自System.Web.UI.Page的class,和一个继承自它的由axXx生成的class。

对于使用过ASP.NET 1.x来说,一定会很熟悉这样一种情况:对于每个在aspx中通过HTML定义的Server Control,在Code behind中必须具有一个对应的protected成员,否则你不能通过编程的方式访问这个Server control。以不同方式呈现的同一个Server control通过ID关联起来,如果在Code behind中改了Server control的ID,Server control的Server端的Event handler将会失去原有的作用。

但是在ASP.NET 2.0来说,这种情况发生了改变,在aspx中的Server control在Code behind中却没有相应的成员变量,但是我们可以毫无障碍地访问到每个Server control。这使得我们的code behind更加简洁,通过避免了Server control在aspx和code bebind中的不匹配的问题。这一切都得益于.NET Framework 2.0提供的partial class的机制:把同一个class分布于不同文件中进行定义。有了这个概念,我们来看ASP.NET 2.0的code behind机制。

比如我们有这样的一个Page:

 

Code behind如下:

 

 而实际上,ASP.NET会为我们创建一个隐藏的.cs文件(这个文件有人 把它称之为Sibling partial class):

 

 这个文件会随着aspx文件的改变而动态变化,所以code behind中的Server control永远和aspx中的Server control是完全匹配的。所以我们说ASP.NET 2.0的Page是由3个文件、两个class组成的。

5.编译的粒度和Assembly的命名

到现在为止,我们所讲的ASP.NET的预编译都是以Directory为单位的,同一个Directory下的所有需要编译的文件被编译到同一个Assembly中。ASP.NET还支持以Page为单位的预编译,也就是每个Page编译成一个Assembly。

在默认的情况下,ASP.NET预编译生成的Assembly名称是随机生成的,也就是每次生成的Assembly都具有不同的name。所以我们在部署Web site的时候,一般需要把原来的Assembly删掉,再部署新的Assembly。不过ASP.NET为我们提供了另外一种选择,使得每次编译生成的Assembly具有相同的名称,这样我们部署的时候就可以直接把新的Assembly 拷贝到Production Server上,自动覆盖掉同名的Assembly。

6.  Sample

我们沿用上一部份是用的Sample,我们通过采用不同的预编译方式看看程序将如何运行。

    6.1 Non-updatable Pre-compilation

我们采用如上图所示的默认的发布方式,ASP.NET 将会进行Non-updatable Pre-compilation。浏览目标文件夹,我们会发现 如下的文件结构.


除了多了一个Bin目录和PrecompiledApp.config之外,整个结构和Source code中的结构完全一样。通过上面的分析,我们知道这种预编译方式是将asXx、code behind、后台代码已经Resource一起编译成Assembly。我们说过对于这样的预编译方式,aspx仅仅是一个站位的文件而以,其中HTML已经没有任何意义了,那么对于编译后的aspx中到底是什么东西呢。我们来一探究竟。打开每个aspx都是一段如下如下一样文字,并无任何HTML。

 

This is a marker file generated by the precompilation tool, and should not be deleted!

 

PrecompiledApp.config里面具有一段简短的configuration,表明version和是否可以进行进一步的修改。

 

<precompiledApp version="2" updatable="false"/>

 

所有的Assembly被编译到Bin目录中,我们来看看到底生成了一些什么样的文件在Bin目录中。

在Bin目录由两类文件构成:Assembly和以complied作为扩展名的Preservation file。Preservation file的内容和作用在第一部分已经详细介绍过了,相信大家不会感到陌生。Preservation file在这里和动态编译所起的作用一样。唯一有一点不同的是,他的结构更加简洁,去掉的Dependence file的列表,因为对于Non-updatable Pre-compilation来说,每个Page的以来的文件都是不可更改的。

<preserve resultType="6" virtualPath="/Artech.ASPNETDeployment/App_Code/" hash="439abe7d" filehash="" flags="140000" assembly="App_Code" />

我们来运行以下程序,和动态编译情况下的输出结果比较,看看有什么不同。我们照例先运行Default Page。

 

输出的结果印证我们前面的讨论:处于同一个目录下的Default 和Default2被编译到同一个Assembly中,关注于处理逻辑的code behind的class name为Default和Default2,关注与可视化界面render的aspx对应的class name被加上的_aspx后缀,如果对default_aspx和default2_aspx进行Reflect的话,你会发现他们分别继承Default和Default2,而后者直接继承自System.Web.UI.Page。所以default_aspx和default2_aspx是真正的意义上基于Web page的Http handler。像动态编译一样,预编译生成一个基于Assembly的FastObjectFactory Type,对该对象的描述请参照第一部分。

有了前面的理论基础,相信大家已经猜到这时候,我浏览Part I下的Page1和Page2时的输出是什么 样子,由于预编译是以目录为单位的,我们对Part I下的任何一个page的访问,都会加载相同的Assembly,所以此时对这两个Page的访问会得到一样的输出结果:


   6.2 Updatable Pre-compilation

接下来我们来对Web Page进行Updatable Pre-compilation,相关的编译设置如下:选择Allow this precompiled site to be updatable。


生成的文件及其结构和进行Non-updatable Pre-compilation,不同的又一下3点:

  • PrecompiledApp.config:updatable被设置为true。
    <precompiledApp version="2" updatable="true"/>
  • asXx和我们进行开发时内容一样,你如aspx包含的就是HTML,我们可以在部署之后对他们进行和code behind无关的修改。
  • Preversation file中有加上了Page对应的dependence file列表。
    <?xml version="1.0" encoding="utf-8"?>

    <preserve resultType="9" virtualPath="/Artech.ASPNETDeployment/App_GlobalResources/" hash="439abe7d" filehash="ff21249472dbf6cb" flags="140000" assembly="App_GlobalResources" resHash="1cba48dd56e28538" />

我们来运行一下Web site,看看现在的输出结果又有何不同。首先打开Default Page:


通过上图,我们发现此时加载了两个相关的Assembly。我们来分析一下为什么会这样。在分析Updatable Pre-compilation时,我们说过:asXx是不会才与编译的,只有他们的code behind, 所有的后台代码,资源文件才会本编译。对于一个page 来说,page的code behind被编译到Assembly中,aspx则不会。Aspx在运行时实行动态编译,所以aspx是可被修改的。在本例中,我们访问Default Page,ASP.NET先对aspx进行编译,其对应的class name为default_aspx,由于default_aspx继承与Default,并且Default存在于预编译生成的Assembly中,所以这个Assembly被加载进来。

由于同一个page最重本编译到两个不同的Assembly中,所以我们此时访问Part I中的Page1或者Page2,又会有两个Assembly被加载进来:


     6.3 以Page为单位进行预编译

前面我们进行的都是以directory为单位的预编译,现在我们缩小编译的粒度,以Page为单位进行编译。我们选择了“Use fixed naming and single page assemblies”选项。那么现在进行的是 基于单个page的non-updatable pre-compilation。通时由于采用的是fixed naming的编译方式,每次进行编译生成的Assembly的名称都是一样的。



现在我们来看看,编译之后生成的Assembly:

我们看到编译器为每个Page生成了一个单独的Assembly。此时运行程序,你看到的又将不同。如果此时你访问Default Page,你将看到:


是不是和上面不同,Assembly只有Default对应的两个Type,没有了处于同一个目录下的Default2的Type。因为Default2有它独自的Assembly .所以你该会想到,如果我们现在每访问一个没有被访问过的Page,就会有一个新的Assembly被加载。比如我么访问Part I下的Page1:


     6.4编译强类型的Assembly

我们知道可以通过一个Public key/Private key pair对Assembly进行签名,进而把它部署到GAC中,我们来看看如何做。

首先我通过SN.exe生成Public key/Private key pair并保存到一个文件中(比如D:\MyKey.keys),然后进行如下的编译设置


那么我们进行编译就会生成强类型的Assembly。我们可以运行我们的程序来证明:


每个Assembly有具有一样的PublicKeyToken,因为我们使用的一样的Public key/Private key pair进行对每个Assembly签名的。

 

 

 

 

posted @ 2008-07-29 15:36 InsistYML 阅读(84) | 评论 (0)编辑

(转载)

 

C#中提供了三种类型的计时器:
1、基于 Windows 的标准计时器(System.Windows.Forms.Timer)
2、基于服务器的计时器(System.Timers.Timer)
3、线程计时器(System.Threading.Timer)
下面我就通过一些小实验来具体分析三种计时器使用上面的异同点,特别是和线程有关的部分。
实验例子截图:

一、基于 Windows 的标准计时器(System.Windows.Forms.Timer)
首先注意一点就是:Windows 计时器是为单线程环境设计的
此计时器从Visual Basic 1.0 版起就存在于该产品中,并且基本上未做改动
这个计时器是使用最简单的一种,只要把工具箱中的Timer控件拖到窗体上,然后设置一下事件和间隔时间等属性就可以了

实验出来的结果也完全符合单线程的特点:
1、当启动此计时器后,会在下方子线程ID列表中显示子线程ID,并且和主线程ID相同

        private void formsTimer_Tick(object sender, EventArgs e)
        
{
            i
++;
            lblSubThread.Text 
+= "子线程执行,线程ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + "\r\n";
        }


2、当单击主线程暂停5秒后,子线程会暂停执行,并且当5秒之后不会执行之前被暂停的子线程,而是直接执行后面的子线程(也就是会少输出几行值)
            System.Threading.Thread.Sleep(5000);
3、在子进程的事件中暂停5秒会导致主窗口相应无响应5秒
4、定义一个线程静态变量:
        [ThreadStatic]
        private static int i = 0;

在子线程事件中每次加一,再点击线程静态变量值会得到增加后的i值

二、基于服务器的计时器(System.Timers.Timer)
System.Timers.Timer不依赖窗体,是从线程池唤醒线程是传统的计时器为了在服务器环境上运行而优化后的更新版本
在VS2005的工具箱中没有提供现成的控件,需要手工编码使用此计时器
使用方式有两种,
1、通过SynchronizingObject属性依附于窗体

            System.Timers.Timer timersTimer = new System.Timers.Timer();
            
            timersTimer.Enabled 
= false;
            timersTimer.Interval 
= 100;
            timersTimer.Elapsed 
+= new System.Timers.ElapsedEventHandler(timersTimer_Elapsed);

            timersTimer.SynchronizingObject 
= this;

通过这种方式来使用,实验效果几乎和基于 Windows 的标准计时器一样,只是在上面的第二条实验中,虽然也会暂停子线程的执行,不过在5秒之后把之前排队的任务都执行掉(也就是不会少输出几行值)
2、不使用SynchronizingObject属性
这种方式就是多线程的方式了,即启动的子线程和主窗体不在一个线程。不过这样也存在一个问题:由于子线程是单独的一个线程,那么就不能访问住窗体中的控件了,只能通过代理的方式来访问:

        delegate void SetTextCallback(string text);
        .
        .
        
void timersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        
{
            
//使用代理
            string text = "子线程执行,线程ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + "\r\n";
            SetTextCallback d 
= new SetTextCallback(SetText);
            
this.Invoke(d, new object[] { text });
            i
++;
        }


        
private void SetText(string text)
        
{
            lblSubThread.Text 
+= text;
        }

这样我们再次实验就会得到如下的结果:
1、当启动此计时器后,会在下方子线程ID列表中显示子线程ID,并且和主线程ID不相同

2、当单击主线程暂停5秒后,子线程会一直往下执行(界面上可能看不出来,不过通过在子线程输出文件的方式可以很方便的看出来)
3、在子进程的事件中暂停5秒不会导致主窗口无响应
4、在子线程事件中每次给线程静态变量加一,再点击线程静态变量值得到的值还是0(不会改变主窗口中的线程静态变量)

 三、线程计时器(System.Threading.Timer)
 线程计时器也不依赖窗体,是一种简单的、轻量级计时器,它使用回调方法而不是使用事件,并由线程池线程提供支持。
对消息不在线程上发送的方案中,线程计时器是非常有用的。
使用方法如下:

        System.Threading.Timer threadTimer;
        
public void ThreadMethod(Object state)
        
{
            
//使用代理
            string text = "子线程执行,线程ID:" + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString() + "\r\n";
            SetTextCallback d 
= new SetTextCallback(SetText);
            
this.Invoke(d, new object[] { text });
            i
++;
        }


        
private void Form1_Load(object sender, EventArgs e)
        
{
            threadTimer 
= new System.Threading.Timer(new System.Threading.TimerCallback(ThreadMethod), null-1-1);
        }



暂停代码:
                threadTimer.Change(-1, -1);

实验的效果和基于服务器的计时器(System.Timers.Timer)的第二种方式是一样的,
当然具体的使用方法和原理是不一样的,最主要的就是这种方式使用的是代理的方式而不是事件的方式,并且可以不依赖于窗体和组件而单独执行


下面列出老外总结的一张表(三种方式的区别):

Feature description System.Timers.Timer System.Threading.Timer System.Windows.Forms.Timer
Support for adding and removing listeners after the timer is instantiated. Yes No Yes
Supports call backs on the user-interface thread Yes No Yes
Calls back from threads obtained from the thread pool Yes Yes