 TechNet Magazine March 2007

TechNet Magazine March 2007

SQL Server: 诊断索引健全性的新工具

要获得 SQL Server 的最佳性能，您需要监督和维护健全的索引。崭新的动态管理视图和函数为您深入 SQL Server 提供了一个机会，借此您可以更好地了解索引的工作情况并发现潜在的性能问题。

Randy Dyess SQL Server: 实现 SQL Server 的高可用性

SQL Server 2005 提供了多个选项来提高数据库的可用性，范围从复制到群集再到日志传送。我们将细致了解用于配置 SQL Server 的各种选项并帮助您查明哪些选项恰好适合于您的环境。

Zach Nichter SQL Server: 利用表分区简化数据库维护

在过去，处理分区和较大的表是一项繁重且令人乏味的工作，但使用 SQL Server 2005 以后，表分区就变得简单多了。现在，您可以轻松创建多个分区、来回移动这些分区、删除旧分区，甚至更改数据的分区方式。方法就在这里。

Noah Gomez SQL Server: 关于 SQL Server 群集的几个关键技巧

服务器群集提供了故障转移功能，确保有更多正常运行时间来执行您的关键操作。但是，要想群集有效，必须加以正确实现。下面提供了一些有助于您迅速建立群集并使其可靠运行的关键技巧。

Tom Moreau, PhD Windows 管理: 了解 Windows Vista 内核：第 2 部分

本月我们将继续深入探讨 Windows Vista 内核中的新功能。在这一期，我们将回顾 Windows Vista 在内存管理方面的一些改进，并探究系统启动、关闭和电源管理方面的内容。

Mark Russinovich 编辑视角: 荣誉窗

Joshua Trupin 工具箱: 面向 IT 专业人员的新产品

Greg Steen 实用工具特别推荐: SyncToy

Jay Munro SQL 技术问答: 夏令时、服务器内存及更多内容

Edited by Nancy Michell 网络专家: DHCPv6 协议

Joseph Davies Windows Powershell: 变量的力量

Don Jones 您好，脚本专家！: 回答您有关 OU 的问题……只挣 5 美分

The Microsoft Scripting Guys Exchange 技术问答: 事务日志、Cmdlet、远程管理及更多内容

KC Lemson and Nino Bilic 安全观察: 部署 EFS：第 2 部分

John Morello 桌面文件: PsTools 入门

Wes Miller Microsoft.com 技术内幕: 数据库镜像入门

Saleem Hakani 现场注意事项: 医生来了

Matt Hester Windows 机密: 靠愚蠢显得聪明

Raymond Chen


 SQL Server: 诊断索引健全性的新工具
SQL Server Randy Dyess

诊断索引健全性的新工具
概览:
  • 索引碎片
  • 索引使用情况
  • 索引操作活动
下载这篇文章的代码: DyessSQLIndex2007_03.exe (151KB)

在所有 SQL Server 性能调整检查表中的主要项目之一便是调整数据库中的索引。SQL Server 查询优化器在执行查询期间是否能够正确使用索引不仅取决于是否创建了有效的索引，还
取决于索引是否健全。借助 SQL Server™ 2005 中所引入的一系列动态管理视图 (DMV) 和函数 (DMF)，数据库管理员能够确定其索引的有效性以及发现存在的任何性能问题。
通过 DMV 和 DMF，您能够深入了解服务器并返回服务器状态信息，这些信息可帮助您监视服务器实例的运行状况和性能以及诊断问题。那些熟悉先前版本 SQL Server 的数据库管理员会发现这些 DMV 和 DMF 将会替代以下一系列操作：使用 DBCC 命令、执行一定的系统存储过程、查询许多系统表以及利用 SQL 事件探查器捕获事件。
提供了三个重要的函数和视图（即 sys.dm_db_index_physical_stats、sys.dm_db_index_usage_stats 和 sys.dm_db_index_operational_stats）来帮助您了解索引是否如预计一样工作。可利用它们来查看索引的 I/O 和锁定模式，并了解查询优化器使用索引的方式是否不会在数据库中导致不必要的争用问题。

索引碎片
创建 sys.dm_db_index_physical_stats DMF 是为了替换 DBCC SHOWCONTIG，并能够显示索引的碎片。但是，DBCC SHOWCONTIG 是将共享锁 (S) 放置在包含索引的表上，而 sys.dm_db_index_physical_stats 仅放置一个意图共享锁 (IS)，从而在函数执行期间极大地减少了表的阻塞。
要确定使用 sys.dm_db_index_physical_stats 所查到的索引碎片，请将该函数输出结果中的三列组合在一起进行检查。通过查看 avg_fragmentation_in_percent 列中所返回的值可确定索引的逻辑碎片（堆的区碎片）。逻辑碎片是在索引的叶级别中次序混乱页面的百分比，而区碎片是在索引的叶级别中次序混乱区的百分比。由于磁头只有左右跳动才能按照顺序读取页面，因此，逻辑碎片和区碎片会因为其需要额外的 I/O 和磁头运动而影响索引的性能。您应该尽量保证逻辑碎片和区碎片均接近于零。
索引的内部碎片是页填充度的百分比。当然，您会希望索引页尽量填满，但是也需要根据插入到索引页面中的页面数量来平衡填充度，以保证页面分割的数量绝对最小。
应该检查 sys.dm_db_index_physical_stats 的 avg_page_space_used_in_percent 参数以确定索引页填充度。为了正确配置该数字以使其尽量接近 100%，请在调整索引填充因子的同时观察所出现的页面分割数量。在某一刻，页面分割的数量会开始急剧增加，这表明您设置的索引填充因子数值高于其应有的数值。调整索引的填充因子需要花费一定的时间并需要进行测试，而且必须在事先进行合理规划。（如果未向索引中随意插入，则可以将索引填充因子设置为 100，且不必担心增加的页面分割数。）
要确定 AdventureWorks.HumanResources.Employee 表中所有索引的碎片级别，您可以使用如下语句：
SELECT *
FROM sys.dm_db_index_physical_stats (DB_ID('AdventureWorks')
,OBJECT_ID('HumanResources.Employee')
,NULL
-- NULL to view all indexes;
-- otherwise, input index number
,NULL -- NULL to view all partitions of an index
,'DETAILED') -- We want all information
利用此 DMF，能够自动确定出哪些索引需要重建、哪些需要重组以及哪些不需要维护。检查此 DMF 的 avg_page_space_used_in_percent 和 avg_fragmentation_in_percent 列的值可得出超出所认可的逻辑和密度阈值的索引碎片，这样会有助于您确定要对索引执行哪项操作。
根据您索引的状态，图 1 中所示的示例可能不会返回 AdventureWorks 示例数据库副本中的数据，但对其经过简单调整即可适用于其他数据库。
Figure 1 检查页填充度和碎片 (Close Figure 1)
--Reorganize the following indexes in the AdventureWorks database
USE AdventureWorks
GO

SELECT OBJECT_NAME([object_id]) AS 'Table Name',
index_id AS 'Index ID'
FROM sys.dm_db_index_physical_stats (DB_ID('AdventureWorks')
,NULL -- NULL to view all tables
,NULL -- NULL to view all indexes; otherwise, input index number
,NULL -- NULL to view all partitions of an index
,'DETAILED') --We want all information
WHERE ((avg_fragmentation_in_percent > 10
AND avg_fragmentation_in_percent < 15) -- Logical fragmentation
OR (avg_page_space_used_in_percent < 75
AND avg_page_space_used_in_percent > 60)) --Page density
AND page_count > 8 -- We do not want indexes less than 1 extent in size
AND index_id NOT IN (0) --Only clustered and nonclustered indexes

--Rebuild the following indexes in the AdventureWorks database
USE AdventureWorks
GO

SELECT OBJECT_NAME([object_id]) AS 'Table Name',
index_id AS 'Index ID'
FROM sys.dm_db_index_physical_stats (DB_ID('AdventureWorks')
,NULL -- NULL to view all tables
,NULL -- NULL to view all indexes; otherwise, input index number
,NULL -- NULL to view all partitions of an index
,'DETAILED') --We want all information
WHERE ((avg_fragmentation_in_percent > 15) -- Logical fragmentation
OR (avg_page_space_used_in_percent < 60)) --Page density
AND page_count > 8 -- We do not want indexes less than 1 extent in size
AND index_id NOT IN (0) --Only clustered and nonclustered indexes
将查询结果存储到某个表变量中，然后遍历该表变量，为正确的 ALTER INDEX 语句生成一个动态字符串，这是很容易实现的（请参见图 2）。
Figure 2 生成动态 ALTER INDEX 字符串 (Close Figure 2)
--Rebuild the following indexes in the AdventureWorks database
USE AdventureWorks
GO

--Table to hold results
DECLARE @tablevar TABLE(lngid INT IDENTITY(1,1), objectid INT,
index_id INT)

INSERT INTO @tablevar (objectid, index_id)
SELECT [object_id],index_id
FROM sys.dm_db_index_physical_stats (DB_ID('AdventureWorks')
,NULL -- NULL to view all tables
,NULL -- NULL to view all indexes; otherwise, input index number
,NULL -- NULL to view all partitions of an index
,'DETAILED') --We want all information
WHERE ((avg_fragmentation_in_percent > 15) -- Logical fragmentation
OR (avg_page_space_used_in_percent < 60)) --Page density
AND page_count > 8 -- We do not want indexes less than 1 extent in size
AND index_id NOT IN (0) --Only clustered and nonclustered indexes

SELECT 'ALTER INDEX ' + ind.[name] + ' ON ' + sc.[name] + '.'
+ OBJECT_NAME(objectid) + ' REBUILD'
FROM @tablevar tv
INNER JOIN sys.indexes ind
ON tv.objectid = ind.[object_id]
AND tv.index_id = ind.index_id
INNER JOIN sys.objects ob
ON tv.objectid = ob.[object_id]
INNER JOIN sys.schemas sc
ON sc.schema_id = ob.schema_id

索引使用情况
尽管替换了 DBCC SHOWCONTIG 的 sys.dm_db_index_physical_stats 功能强大，并有助于显示索引是否健全，但您还会经常遇到更为复杂的问题，如确定哪些索引可用于针对表而执行的查询。通常，数据库开发人员或管理员会针对他们认为在查询执行期间查询优化器会用到的表来生成索引。在 SQL Server 的先前版本中，很难知道这些索引是否确实在使用中。用户只有删除相应索引然后查看查询性能是否受到影响，或者是捕获查询的执行计划然后扫描索引的使用情况。
现在有了一个新的动态管理视图 - sys.dm_db_index_usage_stats，这样，您很容易就可以了解到查询优化器使用索引的情况及针对表格执行查询的情况。您可以通过检查此视图来确定索引的有用性，这样您就可以删除查询优化器未在使用的任何索引。您不必再担心索引是否只是在浪费存储空间，或者对无用索引的维护会降低数据库性能。
通过检查此 DMV 的输出结果可得出没有经过搜索和扫描的索引，这样就可确定自上次启动 SQL Server 以来某索引是否曾经使用过。不过，请记住，许多 DMV 和 DMF 不是永久有效，一旦 SQL Server 重启，它们就会将自身重置为零。在使用 DMV 或 DMF 确定索引的使用情况时，请将这一点考虑进去。某索引可能会自上次重新启动服务以来一直没有使用过，但在周末、月末或季度报表查询时却需要该索引。
要查看自上次 SQL Server 服务重启以来某实例中一直未曾使用的全部索引，可以使用以下语句：
SELECT DB_NAME(database_id),OBJECT_NAME([object_id])
FROM sys.dm_db_index_usage_stats
WHERE user_seeks = 0
AND user_scans = 0
AND user_lookups = 0
AND system_seeks = 0
AND system_scans = 0
AND system_lookups = 0

索引操作活动
如果希望了解索引的操作活动，sys.dm_db_index_operational_stats DMF 会对您很有帮助。您可以使用它来查看数据库中每个索引的 I/O、锁定、闭锁和访问方法活动，它们能够帮助您了解索引的使用情况以及诊断因过多的 I/O 活动或索引中存在“热点”而引起的索引锁定问题。
使用此 DMF 的闭锁等待列可帮助确定 READ 和 WRITE 操作为获取对某索引资源的访问权限而花费的时间量。这可帮助您确定用于存储索引的磁盘子系统是否足以应付索引的 I/O 活动。它还可以指出索引的设计和使用情况是否引发了热点；热点是由于在索引的一个或多个页面中活动频繁从而导致争用这些页面中所包含的数据。这样的争用经常会导致对该区域尝试进行的 READ 或 WRITE 操作的大量阻塞。
图 3 显示了如何确定 AdventureWorks.HumanResources.Employee 表中所有索引的锁定和 I/O 模式。
Figure 3 确定锁定和 I/O 模式 (Close Figure 3)
SELECT page_latch_wait_count --page latch counts
,page_latch_wait_in_ms --page latch wait times
,row_lock_wait_in_ms --row lock wait times
,page_lock_wait_in_ms --page lock wait times
,row_lock_count --row lock counts
,page_lock_count --page lock counts
,page_io_latch_wait_count --I/O wait counts
,page_io_latch_wait_in_ms --I/O wait times
FROM sys.dm_db_index_operational_stats (DB_ID('AdventureWorks')
,OBJECT_ID('HumanResources.Employee')
,NULL -- NULL to view all indexes; otherwise, input index number
,NULL -- NULL to view all partitions of an index
)

详细信息
本文所介绍的 DMV 和 DMF 除了具有此处所描述的那些功能外，还有许多其他用途。在“SQL Server 联机丛书”文章中介绍了“其他资源”边栏中所指出的那些函数和视图，请花点时间回顾一下，以了解可以使用它们获得和查阅的更多信息。
要了解本文中未作讨论的一些其他索引 DMF 和 DMV，请参阅由 SQL Server 查询优化团队发布的博客文章，网址为 blogs.msdn.com/queryoptteam/570176.aspx。
其他资源
  • sys.dm_db_index_physical_stats
  • sys.dm_db_index_usage_stats
  • sys.dm_db_index_operational_stats

Randy Dyess是 Solid Quality Learning 公司的顾问，他专门研究 SQL Server OLTP 系统。Randy 编著并撰写了大量关于 SQL Server 的书籍和文章。他是 www.TransactSQL.Com 和 www.Database-Security.Info 网站的创始人和首席作者。
© 2007 Microsoft Corporation 与 CMP Media, LLC.保留所有权利；不得对全文或部分内容进行复制.


 SQL Server: 实现 SQL Server 的高可用性
SQL Server Zach Nichter

实现 SQL Server 的高可用性
概览:
  • 镜像
  • 数据库快照
  • 日志传送
  • 群集
  • 复制
下载这篇文章的代码: NichterHA2007_03.exe (151KB)

高可用性是每个数据库管理员都应该理解的一个概念。它指的是系统的响应性和可访问性。有时，高可用性表示响应时间为几秒钟，而其他情况下则要求响应时间以

分数秒计。我曾咨询过一家公司，那里的 Web 服务器要求 SQL 查询的往返响应时间为几毫秒；如果响应超出该限制，则数据库系统被视为停用，Web 服务器将重新连接到下一个可用的数据库服务器。
随着用户对应用程序速度的要求越来越高，了解如何实现高可用性和快速响应时间将有助于您明智地规划依赖于数据的应用程序。
幸运的是，SQL Server™ 2005 具有多个用于提高可用性的选项，其中包括复制、群集、数据库镜像、数据库快照和数据库日志传送。我将对这些功能进行介绍并深入讲解如何确定哪些功能恰好适合于您的环境。让我们先来看一下图 1，其中介绍了 SQL Server 2005 中的可用性选项。
Figure 1 高可用性选项 (Close Figure 1)

技术特点 | 复制 | 数据库镜像 | 群集 | 数据库快照 | 日志传送
高可用性选项 |  |  |  |  |
可承受高事务要求 |  |  |  |  |
实时数据可用性 |  |  |  |  |
数据映像为只读 |  |  |  |  |
硬件配置唯一 |  |  |  |  |
成本低 |  |  |  |  |
提供数据恢复 |  |  |  |  |
自动故障转移 |  |  |  |  |
实现/管理可能较为复杂 |  |  |  |  |
可能需要考虑性能问题 |  |  |  |  |

定义高可用性
规划高可用性应用程序时的首要目标之一就是定义其在特定环境中的含义。对于一些组织，高可用性意味着必须要有等同于生产硬件的冗余硬件，同时要求数据和硬件的正常工作时间和可用性都能达到 99.995% 或更高。其他组织可能仅要求数据本身具有高可用性，而对生产级性能关注较少（假如故障转移是必需的）。在确定恰好适合您情况的解决方案时，定义高可用性非常重要。
另外，还需要确定可能遇到的停用类型，并指明它们对服务级协议 (SLA) 有何影响。可能对可用性产生影响的停用包括计划内、计划外停用以及性能下降。
计划内停用通常是一段安排好的维护期，将会就此预先通知系统用户。计划外停用通常是由于硬件或软件故障造成数据无法访问而导致的。性能下降也可能造成停用，它常常以最终用户的响应时间来进行衡量，通常，企业以及 IT 组织已预先以某种形式的 SLA 对此达成共识。
除了识别潜在的停用源之外，还必须确定数据的活动级别，以及数据必须始终在线还是可以有时近线或离线。还必须决定可用性选项将处于同一地理位置还是处于远程位置。在决策时可能还需要考虑预算限制。现在就让我们来看看每一个可用性选项。

数据库镜像
在深入探讨数据库镜像之前，我要首先定义术语。
主体 是存放数据库的主生产服务器，它不断地将其事务日志发送给镜像服务器和数据库。
镜像 是存放数据库备份副本的辅助服务器。镜像副本与主体数据库始终保持同步。
角色 指明了特定服务器的用途，即它是担当主体还是担当镜像。
见证方 是在主体和镜像服务器各行其职时对其进行监视的实例，并且它可以发起自动故障转移。
伙伴 可以是主体服务器也可以是镜像服务器。
在典型环境下，将在主体实例上备份主体数据库，并且将在镜像实例上对备份进行还原（请参见图 2）。一旦还原了数据库，就必须使用 SQL Server Management Studio (SMSS) 中主体数据库的属性窗口或使用 T-SQL 脚本在主体服务器上建立镜像。
Figure 2 Database mirroring architecture
Figure 2 Database mirroring architecture
在配置了镜像并建立好镜像会话后，主体和镜像数据库即会进行同步。主体数据库随后会发送自上次在镜像上应用备份以来所发生事件的事务日志。镜像将接收该日志并尝试尽快应用它。如果使用的是 SQL Server 2005 Enterprise Edition，则此进程为多线程，否则便是单线程操作。这些日志一经应用于镜像，数据库即被视为已同步并且将继续保持同步直到镜像会话中断。
在客户端执行新事务时，主体服务器会针对主体数据库执行这些事务，与此同时，还会将事务日志记录从主体数据库传送到镜像数据库的重做日志或日志队列，在此会选取日志记录并将其应用到镜像数据库。在镜像数据库上应用并提交事务后，即会向主体发送回复，宣布已在镜像上提交了事务。主体在从镜像那里收到确认之前，将不会确认任何可能进入系统的新事务。
在发生故障的情况下，镜像可以发起自动故障转移，而见证方会通过判断主体数据库是否可用来支持该过程。发生故障后，必须先解决镜像伙伴上目前所出现的问题，然后它才能再次成为主体伙伴。解决了镜像伙伴上出现的问题后，便开始退回到镜像伙伴服务器，并且数据库会再次进行同步。一旦它们进入同步状态，便可再次开始镜像会话。
这种特殊的镜像模式具有高度安全性，它通过开启事务安全性来提供同步事务性操作，但它不需要见证方，因为它并不利用自动故障转移，所有故障转移都是手动发起的。还有另一种提供同步事务性操作的镜像模式，即高可用性模式。该模式不仅需要开启事务安全性，还需要使用见证方在发生故障时自动进行故障转移。
镜像时可以使用的第三种也是最后一种模式为高性能。该模式需要关闭事务安全性以行使异步操作支持，进而它允许主体伙伴上的事务不必等待事务记录被写入镜像即可提交。高性能模式在配置中不需要见证方。
请注意，镜像操作要求主体和镜像上的 SQL Server 为同一种版本，但不包括见证方，其上可以是 SQL Server Express Edition。此外，主体数据库应处于完全恢复模式，这一点很重要。
ADO.NET 2.0 已与 SQL Server 2005 集成在一起，其中加入了支持数据库镜像的功能并且提供了应用程序到镜像环境的透明故障转移。这就使得 ADO.NET 应用程序在无法与主体数据库建立连接时，无需另外进行任何编码或配置即可自动进行故障转移。其配置就像在两个环境之间指定公共用户以及在连接字符串中指定故障转移伙伴一样容易。以下是 ADO.NET 连接字符串的一个示例，用于为镜像数据库环境确定故障转移伙伴：
"Provider=SQLNCLI.1;Data Source=MirrorDB;Failover Partner=SQL03;
 Initial Catalog=AdventureWorks;
Persist Security Info=True;User ID=TestUser; Password=TestPswd; Pooling=True;
Connect Timeout=5;Application Name=ADOMirrorTest"
根据您的应用程序和数据需求，数据库镜像对于贵公司来说可能是一个不错的选择，但是为了获得最佳性能，还有许多事情需要考虑。例如，系统的事务率有多高，数据更改量有多大？在考虑数据库镜像时，关键是要判断带宽和网速是否足以处理该数据量和事务处理率。另外，还要考虑链路的饱和度。在镜像位于另一不同地理位置的情况下，这一点尤为重要。预先监视系统对于判断是否存在防碍镜像有效工作的任何环境限制至关重要。
如果您力图压缩成本，则数据库镜像可能是一个非常不错的选择。事实上，数据库镜像体系结构不需要共享磁盘，并且无需具备高级或专门的技能便可运行该环境。而且与群集不同的是，数据库镜像不需要伙伴双方具有相同的硬件。此外，使用数据库属性窗口的镜像选项卡上已有的设置向导（请参见图 3），镜像实现起来很简单。我还建议大家阅读白皮书《Database Mirroring Best Practices and Performance Considerations》（数据库监视最佳实践与性能注意事项）以了解更多有帮助的信息，其网址为 go.microsoft.com/fwlink/?LinkId=80897。
Figure 3 Mirroring setup wizard
Figure 3 Mirroring setup wizard

数据库快照
数据库快照是 SQL Server 2005 Enterprise Edition 中提供的一项新技术，但它未被视为高可用性选项。数据库快照在与其他技术一起使用时应作为一种恢复手段或可行的报告选项来使用。快照只不过是数据库在特定时间点的只读视图。
快照是使用如下的 CREATE DATABASE 命令创建的：
CREATE DATABASE SnapDB_20061028_2030 ON
(NAME = SnapDB_Data, FILENAME =
    'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\SnapDB_20061028_2030.snp')
AS SNAPSHOT OF SnapDB;
GO
创建数据库快照后，它会使用称为稀疏文件的一个或多个文件，而不是像典型数据库那样使用数据文件。这些稀疏文件实质上是虚拟存放所，最初并没有任何数据。仅当在源数据库中更改或删除了数据时，才会使用它们来存放该数据。数据以一次一个数据页的方式写入这些稀疏文件，并且实际的数据库快照仅显示自取得快照以来更改过的数据。其余的数据来自源数据库的数据页。
数据库快照文件是以取得快照时的数据库大小进行分配的。所分配的大小并未表明它们实际存放了多少数据。要获取该信息，请运行如下的 T-SQL 语句：
SELECT *
FROM fn_virtualfilestats(DB_ID(N'SnapDB_20061028_
    2030'), 1);
GO

鉴于数据在稀疏文件和源数据库中的存储方式，在访问数据库快照时，会同时对原始数据库数据文件中的数据页和快照的稀疏文件中的数据页进行检索。由于需要共享数据页，快照只能与从中取得快照的源数据库一起呆在相应的服务器上。由于此体系结构并没有减轻源数据库上的 I/O，而且快照体现不出数据库的真正状态，因此快照不是一种行之有效的报告选项。
考虑一个数据库镜像与快照结合使用的方案。这就使得报告数据、镜像以及快照数据库得以从主体数据库中物理分离出来。数据库快照使用 SQL Server 代理和一个定期提供刷新快照的自定义脚本进行了计划安排。图 4 中的示例脚本显示了用于实现此目的的存储过程。它设计用来在作业内部对所处环境中特定数据库的快照创建和丢弃进行管理。这就为一个可接受的报告解决方案留下了余地，因为报告数据将会与生产数据隔离开来。
Figure 4 用于对快照进行计划安排的存储过程 (Close Figure 4)
use msdb;
GO
set nocount on
GO

CREATE PROCEDURE usp_snaprefresh
     @database    sysname = NULL    --name of the database to snapshot
    ,@keepsnap    int        = 24   --# of hours to keep a snapshot
                                    --after it was created
                                    --use a value of '0' to keep all
                                    --existing snapshots
    ,@fileloc    sysname            --location for snapshot
        = 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\'
AS

DECLARE
     @dt            datetime        ,@cnt          int
    ,@databaseid    int             ,@snap         sysname
    ,@sql           nvarchar(1000)  ,@yy           varchar(4)
    ,@mm            varchar(2)      ,@dd           varchar(2)
    ,@h             varchar(2)      ,@m            varchar(2)
    ,@lname         sysname         ,@pname        sysname
    ,@file          sysname         ,@pos1         int
    ,@pos2          int

-- initialize variables
SELECT
     @dt = getdate()
    ,@cnt = 0, @pos1 = 0, @pos2 = 0
    ,@databaseid = db_id(@database)

-- check if valid database was provided
IF @databaseid IS NULL
BEGIN
    RAISERROR ('Missing database name. Rerun the procedure specifying a
       valid database name.',16,1)
    RETURN (0)
END

--determine if snapshots should be kept
IF @keepsnap <> 0
BEGIN
    -- determine if other snapshots exist for this server older than @
    -- keepsnap value hrs
    SELECT ROW_NUMBER() OVER(ORDER BY name DESC) AS [RowNum],
        name INTO #t1
    FROM master.sys.databases
    WHERE source_database_id = @databaseid
        AND create_date < dateadd(hh,-(@keepsnap),getdate())

    IF (SELECT max(RowNum) FROM #t1 where name is not null) > 0
    BEGIN
        WHILE @cnt <= (SELECT max(RowNum) FROM #t1)
        BEGIN
            SELECT @snap = name FROM #t1 WHERE RowNum = @cnt

            PRINT 'Dropping snapshot ''' + @snap + ''''

            SET @sql = 'DROP DATABASE ' + @snap

            EXEC(@sql)
            SELECT @cnt = @cnt + 1
        END
    END
END

-- break apart point in time date time information for file name
SELECT @yy = convert(varchar(4),year(@dt)),@mm = convert(varchar(2),
   month(@dt))
    ,@dd = convert(varchar(2), day(@dt)),@h = convert(varchar(2),
         datepart(hh,@dt))
    ,@m = convert(varchar(2), datepart(mi,@dt))

-- piece together the database snapshot name and the file name
SELECT @file = @database + '_' + @yy + @mm + @dd + '_' + @h + @m

-- identify logical file name of primary data file
SET @sql = 'SELECT name INTO tempdb..t1
FROM ' + @database + '.sys.database_files
WHERE file_id = 1'

EXEC(@sql)

--setting logical filename for the snap
SELECT @lname = name FROM tempdb..t1

-- making sure the file location ends with '\'
IF substring(@fileloc, len(@fileloc), 1) <> '\'
BEGIN
    SET @fileloc = @fileloc + '\'
END

-- build sql statement to be run
SET @sql = N'CREATE DATABASE ' + @file + ' ON
(NAME = ' + @lname + ', FILENAME = ''' + @fileloc + @file + '.snp'')
AS SNAPSHOT OF ' + @database

EXEC(@sql)

-- cleanup
DROP TABLE tempdb..t1;
GO
不过，请记住，之所以认为数据库快照是临时性的，是因为快照无法备份并且没有源数据库便不能存在。如果稀疏文件用尽了空间，快照将被视为已毁坏而必须丢弃。
此外，若使用数据库快照，在对源数据库执行数据修改操作期间，系统性能可能会下降，因为对于取自源数据库数据文件的每个快照，均会将数据页写入稀疏文件。这就使得写入次数按数据库所具有的快照数成倍增长。
读取权限由源数据库在快照点定义，并且无法更改。快照必须与源数据库驻留在同一个实例中，因为它们共享数据页以及同一个缓冲区高速缓存。
仅当镜像也在使用时，才应考虑使用快照作为报告解决方案；否则，您的环境不会有性能上的改进。数据库快照可能是在系统上运行可疑操作之前保留数据的最快方式。可使数据库重新回到快照状态，也可从快照中取出数据以替代源数据库中的数据。
您将需要为快照数据库确定一个命名标准。我使用的标准是 originaldatabasename_date_time.snp。它首先指定了源数据库，然后是取得快照的日期和时间（采用 24 小时格式）。

日志传送
日志传送是一个有限的高可用性选项，它可利用备份和恢复建立一个成本非常低的解决方案。日志传送定期利用事务日志备份来使辅助数据库保持最新。
SQL Server 2005 中的日志传送使用向导来完成设置、计划安排、初始化和监视的整个过程（请参见图 5）。此过程很简单，几分钟之内即可完成。
Figure 5 Log shipping setup wizard
Figure 5 Log shipping setup wizard

确定了主数据库之后，会创建一个时间表并为备份文件确定文件时效。接下来，必须为备份文件建立文件共享。设置了文件共享之后，需要建立辅助数据库文件位置，并且必须通过完成主数据库的还原来初始化该数据库。最后，必须为备份文件副本以及事务日志备份恢复连同每一步所需的任何警报和延迟设置时间表。
设置完成后，日志传送即会按预定时间将数据库的事务日志备份到某个共享的网络位置。将文件发送到共享位置后，会按照设定的时间表将备份应用到辅助数据库。
日志传送简单方便，因此它在许多情况下都工作良好。要实现成本较低且能够处理高事务系统的高可用性，这是一个不错的选择。日志传送中采用的辅助数据库可以在只读模式下使用，从而为报告数据库提供了便利。日志传送所需的开销最低，但在处理故障的情况下，警报策略对它而言确实是必不可少的。
如果您正在考虑在您的环境中使用日志传送，那么您是考虑到了这样几点，即它设置和管理起来简单易行而且只需很少或根本不需要专门技能。然而，日志传送不是一种实时的高可用性解决方案，尽管可以将其与数据库镜像结合起来以实现此目的。它会受到时间表以及诸如备份、文件复制和还原等操作的限制。它还需要手动进行故障转移。出于上述原因，日志传送只能为那类对时间要求不太敏感的环境提供一种简单的解决方案。

SQL Server 群集
服务器群集工作在 OS 级别，其中涉及到重复的硬件以及群集要访问的共享磁盘资源。群集功能也许对最终用户的干扰最少，但它也可能是最为昂贵的。它所需的硬件数量至少是运行非群集实例所需硬件数量的两倍。
群集主题涉猎范围很广，因而我在此将不对各种细节进行探究，而只是给出一般性概述。群集需要两个或多个服务器，它们全都必须装有相同版本的 Windows® 2000 Advanced 或 Datacenter edition，或者 Windows Server® 2003 Enterprise 或 Datacenter edition。它还需要安装 Microsoft® Cluster Services (MSCS)，用于处理服务器之间共享资源的所有权并管理 IP 地址、共享磁盘和网络名。群集还需要共享的磁盘资源，其形式通常为存储区域网络 (SAN) 或 SCSI 连接存储器。
SQL Server 实例也被视为一种资源，标准版和企业版的 SQL Server 2005 均可以安装在群集配置中。有关这两版 SQL Server 2005 均支持的功能列表，请参阅“SQL Server 2005 的功能比较图表”（英文）(microsoft.com/sql/prodinfo/features/compare-features.mspx)。
在群集上建立了资源后，群集的辅助节点即会通过群集两节点间的专用网络上所建立的检测信号与群集主节点保持定期通信。检测信号是按固定时间间隔取得的检查点，用以确定主节点是否发生故障。
倘若主节点发生故障，资源将被移至辅助节点，同时保持逻辑服务器原来的状态。这使得客户端在交互时只需稍事停顿就可以继续工作。整个故障转移过程所需时间可能不等，少则不超过 5 秒，而在某些情况下则需要 30 秒或更多的时间，具体取决于群集中所包括的硬件、软件和联网组件。
群集可能是一种昂贵而又复杂的技术，需具备专门技能方可解决系统上发生的故障；不过，它可为选用任何自动故障转移的最终用户提供最平稳的故障转移。每个应用程序各有不同，有些可能无法识别群集或与之不兼容，在最坏的情况下需要应用程序重新连接。

复制
SQL Server 2005 复制也可用于高可用性体系结构。它提供了四种类型的复制：快照、事务、对等以及合并。对等实际上就是某种形式的事务性复制，所以我在此将不对其进行讨论。
使用复制，您可以选择采用辅助站点和数据库来实现高可用性，它们在功能上与主数据库完全一样。可以通过合并复制来达到此目的，这种复制从主数据库和辅助数据库中取得事务，然后将两者之中的更改相互合并。您可能已经猜到，在此配置中需要一个冲突解决过程。
事务性复制在逻辑设计上与数据库镜像非常相似。应用到主数据库的事务将被传送给辅助数据库以确保环境保持一致。事务到达后即会将其应用到辅助数据库，随后，该数据库将等待要在系统中应用的下一个事务。
快照复制在以下方面与日志传送非常相似：它们都按预定的时间间隔运行并以批量更改方式更新辅助数据库，而非在提交事务时将每个事务应用到两个系统。两种技术的利用方式也几乎相同。
复制需要具备专业知识，这对于没有专门 DBA 的环境是一个很大的问题。复制在故障排除方面可能有些复杂，如果要将其用作高可用性选项，确实需要进行更加繁杂的设计。
复制可以适当地满足高可用性解决方案的需求。此项技术在记录级别使用事务性复制做到了数据库镜像所能做到的事情，只是不具有自动故障转移选项。假如您有足够的资源和一点创造性，为自动故障转移编写解决方案脚本应该不成问题。
与数据库镜像不同的是，源数据库和目标数据库对于客户端应用程序都可以完全访问。复制可凭借快照复制提供与日志传送相同的功能。
不过，有件事情可以考虑，复制技术已身经百战并且有详实的文档资料。尽管使用复制作为高可用性解决方案有一些缺陷而且性能也值得关注，但那充其量也与数据库镜像差不多。使用复制设计的任何高可用性解决方案都极可能有一个更为复杂的体系结构需要管理，该体系结构不一定更先进，但绝对更为棘手。此外，需要考虑的最大障碍之一是，如果数据库表结构发生变化或是您想要添加即将复制的表，则必须中断并重定义发布，才能使更改最终在两个数据库中都生效。

总结
您现在可以看到，为您的环境创建高可用性解决方案是何以需要一点创造性的。每种 SQL Server 2005 高可用性技术都有其强项和弱点，并且各自适合于不同的情况。
对于主 Web 数据库与辅助数据库在地理上分开的环境（尤其是当它们不要求辅助数据实时可用时），在高性能模式下，日志传送、快照复制，甚至于数据库镜像，都很不错。
另一方面，如果辅助数据库有实时数据方面的要求，则在主服务器上的事务率较低且两个环境站点之间的链路较快并且不饱和的情况下，事务性复制或数据库镜像可以担当此任。
还要考虑您对这些技术的惬意程度。如果您已有其中某些技术的工作经验，可能会好一些。如果您是经理而没有专门的 DBA，请尽量避开像复制那样较为复杂的技术，其中存在许多移动部件并且故障排除可能会很复杂。也可以考虑聘请一位有经验的 SQL Server 顾问帮助您设计、实现一个适合您环境的新的高可用性方案，并在可能的情况下帮助培训您的员工对其进行管理。
如果贵组织中的高可用性只要求数据在绝大部分时间可用并且对数据停用时间非常注重，则群集可能会是您的选择。
总而言之，SQL Server 2005 提供了许多实现高可用性的新选项，它们可以适应各种不同的环境。单个可用性选项就可能适合您的需要，或者，您也可以选择组合利用多种技术，但正如您现在所知道的，有各种各样的选择可供您采用。

Zach Nichter 是一位具有 10 多年经验的 SQL Server 专业人员。他承担过多种 SQL Server 支持角色，包括 DBA、团队领导、经理和顾问。目前，Zach 受雇于 Levi Strauss & Co. 担任 DBA 架构师，重点关注 SQL Server 性能、监视、体系结构以及其他战略性动议。


 SQL Server: 利用表分区简化数据库维护
SQL Server Noah Gomez

利用表分区简化数据库维护
概览:
  • 创建已分区表
  • 添加及合并分区
  • 创建和管理已分区索引
下载这篇文章的代码: GomezPartitioning2007_03.exe (156KB)

以前，对于那些管理着含有数百万行数据的表的数据库管理员而言，他们不得不创建多个表。在这些表分区以后，管理员必须在对它们执行大量查询期间，
将表再关联起来。将分区关联起来包括创建一个已分区视图或包装存储过程以指明数据所在位置，然后执行另外一个存储过程以便仅点击所需分区即可返回数据集。
虽然这些方法奏效，但执行起来却比较繁琐。对多个表及其索引的管理以及用于再次将表关联在一起的方法常常会引发管理和维护方面的问题。另外，通过创建多个表将数据分区会造成灵活性不够，因为存储过程、维护工作、数据转换服务 (DTS) 工作、应用程序代码及其他进程必须对分区的特性有所了解。因此，为了允许您在不更改代码的情况下添加或删除这些类似分区，这些元素通常是以非动态方式创建的，从而也导致了其效率比较低下。
SQL Server™ 2005 的 Enterprise 和 Developer 版本可让您将一个表中所含的大量数据划分成多个小分区，以便更加有效地进行管理和维护。这种创建可通过单一入口点进行访问的数据段的能力减少了采用旧方式执行此类操作时所带来的许多管理方面的问题。使用单一入口点（表名称或索引名称）可隐藏应用程序代码中的多个数据段并允许管理员或开发人员根据需要更改分区而无需调整基本代码。
简言之，您根本不必调整应用程序中的代码便可创建多个分区、来回移动这些分区、删除旧分区，甚至更改数据的分区方式。您的应用程序代码只是继续调用同一个基表或索引名称。同时，您可以通过减少每个索引中所包含的数据量来减少这些索引的维护时间，还可以通过将数据加载到空分区来加快数据加载速度。
从技术上讲，每个 SQL Server 2005 表都进行了分区，即每个表中至少包含一个分区。SQL Server 2005 所要做的就是让数据库管理员为每个表创建附加分区。表分区和索引分区是很难定义的，这是因为允许单一入口点（表名称或索引名称）的不带应用程序代码的行级分区（不允许按列分区）需要知道入口点背后的分区数目。分区可以存在于基表以及与该表相关联的索引中。

创建已分区表
需使用分区函数和分区方案创建一个能够包含多个分区的表（不只是仅包含一个默认分区）。这些对象允许您将数据划分成特定的段并控制着这些数据段在您的存储设计中的位置。例如，您可以根据数据的使用寿命或使用其他常用微分函数将数据分布在多个驱动器阵列中。注意，可根据表中的某列对表进行分区，每个分区必须含有数据，且这些数据不能置于其他分区内。
分区函数 在为表分区时，首先要确定您需要如何将数据划分到不同的段中。分区函数用于将每一行数据都映射到不同的分区中。这些单独的数据行可以通过除 text、ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max) 之外的任何列类型、别名数据类型或公共语言运行库 (CLR) 用户定义数据类型进行映射。但分区函数必须能够将一行数据完全置于一个表分区中 - 不允许一行数据同时属于多个分区。
要想进行表分区，必须在目标表中创建分区列。此分区列可在首次创建表时就存在于表架构中，您也可以更改该表并在以后添加该分区列。该列可以接受 NULL 值，但默认情况下，含有 NULL 值的所有行将被置于表最左侧的分区内。要避免发生这种情况，请在创建分区函数时指定将 NULL 值置于表最右侧的分区内。使用左侧分区还是使用右侧分区是一项重要的设计决策，因为您要修改分区方案并添加更多分区或删除现有分区。
在创建分区函数时，可以选择 LEFT 或 RIGHT 分区函数。LEFT 和 RIGHT 分区的不同之处在于，分区方案中边界值所放置的位置。LEFT 分区（默认情况）在分区中包括了边界值，而 RIGHT 分区将边界值放到了下一分区中。
为了更好地理解这一概念，让我们看一下简单的 LEFT 和 RIGHT 分区：
CREATE PARTITION FUNCTION Left_Partition (int) AS RANGE LEFT
FOR VALUES (1,10,100)

CREATE PARTITION FUNCTION Right_Partition (int) AS RANGE RIGHT
FOR VALUES (1,10,100)
在第一个函数 (Left_Partition) 中，值 1、10 和 100 分别位于第一、第二和第三分区内。在第二个函数 (Right_Partition) 中，这些值分别位于第二、第三和第四分区内。
在创建已分区表时，要尽可能平均地进行分区，这是很重要的。这有助于您了解分区的空间需求。对 LEFT 和 RIGHT 分区的使用将决定着数据的放置位置，这反过来又决定了分区的大小及针对该分区创建的任何索引的大小。
通过使用 $PARTITION 函数，您能够确定某数据值将要放置到的分区的分区号，如下所示：
SELECT $PARTITION.Left_Partition (10)
SELECT $PARTITION.Right_Partition (10)
在第一个 SELECT 语句中，结果为 2。第二个 SELECT 语句将返回 3。
分区方案 在创建完函数并确定了如何划分数据后，您必须确定要在磁盘子系统的哪个位置上创建各个分区。需使用分区方案来创建此磁盘布局。分区方案通过利用文件组将每个分区放到磁盘子系统的相应位置上来管理各个分区的磁盘存储。您可以将分区方案配置为所有分区都放在一个文件组中、所有分区分别放在不同的文件组中或是多个分区共享文件组。后一种方法使得数据库管理员在分布磁盘 I/O 时能够具有较大的灵活性。
图 1 显示了可将一个或多个文件组分配给分区方案的一些方法。您应注意，在创建分区方案之前，分区方案所使用的文件组必须已经存在于数据库中。
Figure 1 将文件组分配给分区方案 (Close Figure 1)
--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Primary_Left_Scheme
AS PARTITION Left_Partition
--Partition must currently exist in database
      ALL TO ([PRIMARY])

--Place all partitions into the different filegroups
CREATE PARTITION SCHEME Different_Left_Scheme
AS PARTITION Left_Partition
--Partition must currently exist in database
      TO (Filegroup1, Filegroup2, Filegroup3, Filegroup4)
--Filegroups must currently exist in database

--Place multiple partitions into the different filegroups
CREATE PARTITION SCHEME Multiple_Left_Scheme
AS PARTITION Left_Partition
--Partition must currently exist in database
      TO (Filegroup1, Filegroup2, Filegroup1, Filegroup2)
--Filegroups must currently exist in database
如果创建了如图 1 中所示的示例分区函数并利用分区方案创建了一个表，随后便可确定各数据行在新建已分区表中的位置。然后，可在这些数据行插入到已分区表以后查看其分布情况。执行所有这些操作的代码将与图 2 中所显示的那些代码类似。
Figure 2 放置数据行并查看分布情况 (Close Figure 2)
--Prepare database
IF OBJECT_ID('Partitioned_Table') IS NOT NULL
DROP TABLE Partitioned_Table
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = 'Primary_Left_Scheme')
DROP PARTITION SCHEME Primary_Left_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = 'Left_Partition')
DROP PARTITION FUNCTION Left_Partition
GO

--Create partitioned table
CREATE PARTITION FUNCTION Left_Partition (int) AS RANGE LEFT
FOR VALUES (1,10,100)

--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Primary_Left_Scheme
AS PARTITION Left_Partition
      ALL TO ([PRIMARY])

CREATE TABLE Partitioned_Table
(
col1 INT
,col2 VARCHAR(15)
) ON Primary_Left_Scheme (col1)

--Determine where values will be placed (this is not required)
--You should try to do this before executing the code
SELECT $PARTITION.Left_Partition (1)
SELECT $PARTITION.Left_Partition (2)
SELECT $PARTITION.Left_Partition (3)
SELECT $PARTITION.Left_Partition (4)
SELECT $PARTITION.Left_Partition (10)
SELECT $PARTITION.Left_Partition (11)
SELECT $PARTITION.Left_Partition (12)
SELECT $PARTITION.Left_Partition (13)
SELECT $PARTITION.Left_Partition (14)
SELECT $PARTITION.Left_Partition (100)
SELECT $PARTITION.Left_Partition (101)
SELECT $PARTITION.Left_Partition (102)
SELECT $PARTITION.Left_Partition (103)
SELECT $PARTITION.Left_Partition (104)

--Insert data into partitioned table
INSERT INTO Partitioned_Table VALUES (1,'Description')
INSERT INTO Partitioned_Table VALUES (2,'Description')
INSERT INTO Partitioned_Table VALUES (3,'Description')
INSERT INTO Partitioned_Table VALUES (4,'Description')
INSERT INTO Partitioned_Table VALUES (10,'Description')
INSERT INTO Partitioned_Table VALUES (11,'Description')
INSERT INTO Partitioned_Table VALUES (12,'Description')
INSERT INTO Partitioned_Table VALUES (13,'Description')
INSERT INTO Partitioned_Table VALUES (14,'Description')
INSERT INTO Partitioned_Table VALUES (100,'Description')
INSERT INTO Partitioned_Table VALUES (101,'Description')
INSERT INTO Partitioned_Table VALUES (102,'Description')
INSERT INTO Partitioned_Table VALUES (103,'Description')
INSERT INTO Partitioned_Table VALUES (104,'Description')

--View the distribution of data in the partitions
SELECT ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] = OBJECT_ID('Partitioned_Table')

修改已分区表
尽管做了详细的预先规划，但有时还是需要您在创建并填充已分区表后对其进行调整。您的分区方案可能已按照预期方式进行工作，但是您可能需要（比如说）在累积了新数据后添加新的分区，或是一次删除大量的已分区数据。幸运的是，已分区表和基本分区结构允许您在表启用并填充有数据后对其进行改动。
添加分区 许多分区计划都囊括了在以后添加新分区的能力。这一时间点可以是某一特定日期，也可以取决于增量标识列中的某个值。但如果没有预先对此进行规划，您还可以在以后进行规划并将新分区添加到已分区表中。请考虑图 2 中所创建的表。您可以向此表中添加一个新分区，使其包含大于 500 的值，如下所示：
--Determine where values live before new partition
SELECT $PARTITION.Left_Partition (501)  --should return a value of 4

--Create new partition
ALTER PARTITION FUNCTION Left_Partition ()
SPLIT RANGE(500)

--Determine where values live after new partition
SELECT $PARTITION.Left_Partition (501)  --should return a value of 5
这种添加分区的能力给予了极大的灵活性。图 3 显示了如何才能将分区添加到函数的左侧。在本例中，您需要将在何处放置新分区通知给分区方案，因为您已经用完了在首次构建分区方案时所创建的所有文件组。即使对于所有分区，您一直在使用 PRIMARY 文件组，还是要必须将对于新分区再次使用 PRIMARY 文件组这个事实通知给分区方案。
Figure 3 将分区添加到函数的左侧 (Close Figure 3)
--Determine where values live after new partition
SELECT $PARTITION.Left_Partition (5)   --should return a value of 2
SELECT $PARTITION.Left_Partition (1)   --should return a value of 1
SELECT $PARTITION.Left_Partition (10)  --should return a value of 2

--Add new filegroups to the partitioning scheme
ALTER PARTITION SCHEME Primary_Left_Scheme
NEXT USED [PRIMARY]

--Create new partition
ALTER PARTITION FUNCTION Left_Partition ()
SPLIT RANGE(5)

--Determine where values live after new partition
SELECT $PARTITION.Left_Partition (5)   --should return a value of 2
SELECT $PARTITION.Left_Partition (1)   --should return a value of 1
SELECT $PARTITION.Left_Partition (10)  --should return a value of 3
合并两个分区 SQL Server 允许您从表中删除单个分区而保留其中的数据。这可用于将旧的活动数据合并到已存档数据中，或是减少所拥有的分区数，从而简化您对已分区表的管理。还可以利用这种方法合并分区，将数据从一个文件组移动到另一个文件组以释放特定驱动器阵列上的磁盘空间。图 4 中的代码显示了如何将数据从一个分区移动到同一文件组上的另一个分区。
Figure 4 将数据从一个分区移动到另一个分区 (Close Figure 4)
--Prepare database
IF OBJECT_ID('multiple_partition') IS NOT NULL
DROP TABLE multiple_partition
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = 'Primary_Left_Scheme')
DROP PARTITION SCHEME Primary_Left_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = 'Left_Partition')
DROP PARTITION FUNCTION Left_Partition
GO

--Create partitioned table
CREATE PARTITION FUNCTION Left_Partition (int) AS RANGE LEFT
FOR VALUES (1,10,100)

--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Primary_Left_Scheme
AS PARTITION Left_Partition
--Partition must currently exist in database
      ALL TO ([PRIMARY])

CREATE TABLE multiple_partition
(
col1 INT PRIMARY KEY CLUSTERED
,col2 VARCHAR(15)
) ON Primary_Left_Scheme (col1)

INSERT INTO multiple_partition VALUES (1,'Description')
INSERT INTO multiple_partition VALUES (2,'Description')
INSERT INTO multiple_partition VALUES (3,'Description')
INSERT INTO multiple_partition VALUES (4,'Description')
INSERT INTO multiple_partition VALUES (10,'Description')
INSERT INTO multiple_partition VALUES (11,'Description')
INSERT INTO multiple_partition VALUES (12,'Description')
INSERT INTO multiple_partition VALUES (13,'Description')
INSERT INTO multiple_partition VALUES (14,'Description')
INSERT INTO multiple_partition VALUES (100,'Description')
INSERT INTO multiple_partition VALUES (101,'Description')
INSERT INTO multiple_partition VALUES (102,'Description')
INSERT INTO multiple_partition VALUES (103,'Description')
INSERT INTO multiple_partition VALUES (104,'Description')

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] = OBJECT_ID('multiple_partition')

--Check where data would be placed
SELECT $PARTITION.Left_Partition (1)
SELECT $PARTITION.Left_Partition (10)
SELECT $PARTITION.Left_Partition (100)
SELECT $PARTITION.Left_Partition (101)

--Merge two partitions
ALTER PARTITION FUNCTION Left_Partition()
MERGE RANGE (10)

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] = OBJECT_ID('multiple_partition')

--Check where data would be placed
SELECT $PARTITION.Left_Partition (1)
SELECT $PARTITION.Left_Partition (10)
SELECT $PARTITION.Left_Partition (100)
SELECT $PARTITION.Left_Partition (101)
将单个分区表移动到已分区表中 在加载例程期间，通常是必须先将大量数据加载到数据库中，然后进行修改或合计，最后再将这些数据移动到实际的数据表中。SQL Server 2005 分区允许您将单个分区表移动到包含多个分区的表中。这意味着您可以先将数据加载到单个加载表中，修改那些数据，然后再将整个表移动到一个现有表中，这样就省去了逐一移动每行数据的开销。此分区层并不包括更改基本分区结构，只是修改已分区表。图 5 中的代码显示了您可以如何来达到这一目的。
Figure 5 将整个表移动到现有表中 (Close Figure 5)
--Prepare database
IF OBJECT_ID(‘multiple_partition’) IS NOT NULL
DROP TABLE multiple_partition
GO

IF OBJECT_ID(‘single_partition’) IS NOT NULL
DROP TABLE single_partition
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = ‘Primary_Left_Scheme’)
DROP PARTITION SCHEME Primary_Left_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = ‘Left_Partition’)
DROP PARTITION FUNCTION Left_Partition
GO

--Create single partition table
CREATE TABLE single_partition
(
col1 INT PRIMARY KEY CLUSTERED
,col2 VARCHAR(15)
)

--Table must have a CHECK Constraint
ALTER TABLE single_partition
WITH CHECK
ADD CONSTRAINT CK_single_partition
    CHECK (col1 > 100)

INSERT INTO single_partition VALUES (101,’Description’)
INSERT INTO single_partition VALUES (102,’Description’)
INSERT INTO single_partition VALUES (103,’Description’)
INSERT INTO single_partition VALUES (104,’Description’)

--Create partitioned table
CREATE PARTITION FUNCTION Left_Partition (int) AS RANGE LEFT
FOR VALUES (1,10,100)

--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Primary_Left_Scheme
AS PARTITION Left_Partition
--Partition must currently exist in database
      ALL TO ([PRIMARY])

CREATE TABLE multiple_partition
(
col1 INT PRIMARY KEY CLUSTERED
,col2 VARCHAR(15)
) ON Primary_Left_Scheme (col1)

INSERT INTO multiple_partition VALUES (1,’Description’)
INSERT INTO multiple_partition VALUES (2,’Description’)
INSERT INTO multiple_partition VALUES (3,’Description’)
INSERT INTO multiple_partition VALUES (4,’Description’)
INSERT INTO multiple_partition VALUES (10,’Description’)
INSERT INTO multiple_partition VALUES (11,’Description’)
INSERT INTO multiple_partition VALUES (12,’Description’)
INSERT INTO multiple_partition VALUES (13,’Description’)
INSERT INTO multiple_partition VALUES (14,’Description’)
INSERT INTO multiple_partition VALUES (100,’Description’)

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] IN (OBJECT_ID(‘multiple_partition’), OBJECT_
      ID(‘single_partition’))

--Move the single table into the partitioned table
ALTER TABLE single_partition SWITCH TO multiple_partition PARTITION 4

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] IN (OBJECT_ID(‘multiple_partition’), OBJECT_ID(‘single_partition’))
将分区从一个表移动到另一个表 一项常见的管理任务是将旧数据移动到单独的存档表中。存档过程通常会涉及到一系列语句，它们用于在事务日志中创建附加资源使用。但将分区的所有权从一个表转交给另一个表却是存档大量数据而无需任何事务日志开销的简便方法。此功能允许数据库管理员将旧数据段从活动表移动到已存档表中。但由于实际上并未进行数据的移动，所以它所花费的时间要明显少于移动各个数据行所花费的时间。图 6 显示了如何进行此操作。
Figure 6 将旧数据移动到已存档表中 (Close Figure 6)
--Prepare database
IF OBJECT_ID('active_data') IS NOT NULL
DROP TABLE active_data
GO

IF OBJECT_ID('archive_data') IS NOT NULL
DROP TABLE archive_data
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = 'Active_Scheme')
DROP PARTITION SCHEME Active_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = 'Active_Partition')
DROP PARTITION FUNCTION Active_Partition
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = 'Archive_Scheme')
DROP PARTITION SCHEME Archive_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = 'Archive_Partition')
DROP PARTITION FUNCTION Archive_Partition
GO

--Create active function
CREATE PARTITION FUNCTION Active_Partition (int) AS RANGE LEFT
FOR VALUES (1,10,100)

--Create archive function
CREATE PARTITION FUNCTION Archive_Partition (int) AS RANGE LEFT
FOR VALUES (100,200,300)

--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Active_Scheme
AS PARTITION Active_Partition
--Partition must currently exist in database
      ALL TO ([PRIMARY])

--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Archive_Scheme
AS PARTITION Archive_Partition
--Partition must currently exist in database
      ALL TO ([PRIMARY])

CREATE TABLE active_data
(
col1 INT PRIMARY KEY CLUSTERED
,col2 VARCHAR(15)
) ON Active_Scheme (col1)

CREATE TABLE archive_data
(
col1 INT PRIMARY KEY CLUSTERED
,col2 VARCHAR(15)
) ON Archive_Scheme (col1)

INSERT INTO active_data VALUES (1,'Description')
INSERT INTO active_data VALUES (2,'Description')
INSERT INTO active_data VALUES (3,'Description')
INSERT INTO active_data VALUES (4,'Description')
INSERT INTO active_data VALUES (10,'Description')
INSERT INTO active_data VALUES (11,'Description')
INSERT INTO active_data VALUES (12,'Description')
INSERT INTO active_data VALUES (13,'Description')
INSERT INTO active_data VALUES (14,'Description')
INSERT INTO active_data VALUES (100,'Description')

INSERT INTO archive_data VALUES (200,'Description')
INSERT INTO archive_data VALUES (300,'Description')
INSERT INTO archive_data VALUES (400,'Description')

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] IN (OBJECT_ID('active_data'),OBJECT_ID('archive_
      data'))

--Switch ownership of partition to another table
ALTER TABLE active_data SWITCH PARTITION 3 TO archive_data PARTITION 1

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] IN (OBJECT_ID('active_data'),OBJECT_ID('archive_
      data'))
使用单个分区创建新表 您可以将单个分区从现有的已分区表移动到一个空的未分区表中。这样做之后，数据库管理员可针对单个分区执行索引维护，或是轻松地删除大量数据而不必记录该删除过程。图 7 中的示例显示了如何将一个分区移动到空表中，然后使用该新表来删除数据。
Figure 7 移动和删除数据 (Close Figure 7)
--Prepare database
IF OBJECT_ID('active_data') IS NOT NULL
DROP TABLE active_data
GO

IF OBJECT_ID('archive_data') IS NOT NULL
DROP TABLE archive_data
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = 'Active_Scheme')
DROP PARTITION SCHEME Active_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = 'Active_Partition')
DROP PARTITION FUNCTION Active_Partition
GO

--Create active function
CREATE PARTITION FUNCTION Active_Partition (int) AS RANGE LEFT
FOR VALUES (1,10,100)

--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Active_Scheme
AS PARTITION Active_Partition
--Partition must currently exist in database
      ALL TO ([PRIMARY])

CREATE TABLE active_data
(
col1 INT PRIMARY KEY CLUSTERED
,col2 VARCHAR(15)
) ON Active_Scheme (col1)

CREATE TABLE archive_data
(
col1 INT PRIMARY KEY CLUSTERED
,col2 VARCHAR(15)
)

INSERT INTO active_data VALUES (1,'Description')
INSERT INTO active_data VALUES (2,'Description')
INSERT INTO active_data VALUES (3,'Description')
INSERT INTO active_data VALUES (4,'Description')
INSERT INTO active_data VALUES (10,'Description')
INSERT INTO active_data VALUES (11,'Description')
INSERT INTO active_data VALUES (12,'Description')
INSERT INTO active_data VALUES (13,'Description')
INSERT INTO active_data VALUES (14,'Description')
INSERT INTO active_data VALUES (100,'Description')

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] IN (OBJECT_ID('active_data'),OBJECT_ID('archive_
      data'))

--Switch ownership of partition to another table
ALTER TABLE active_data SWITCH PARTITION 3 TO archive_data

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] IN (OBJECT_ID('active_data'),OBJECT_ID('archive_
      data'))

--Drop all archive data without logging
DROP TABLE archive_data
GO

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] IN (OBJECT_ID('active_data'),OBJECT_ID('archive_
      data'))

已分区索引
在能够对表中数据分区的同时，您也能够创建已分区索引。这样，数据库管理员可根据已划分数据而非整个表的数据来设计索引结构。创建已分区索引会生成单独的已分区索引 B 树。划分索引会创建出更小的索引，这样，在数据修改、添加和删除期间，存储引擎便能够更加轻松地维护这些索引。为了使大型数据集能够得到更好的索引维护，这些小型索引也可由数据库管理员单独维护。
创建已分区索引 创建已分区索引时，您既可以创建对齐索引，也可以创建非对齐索引。对于对齐索引，利用与已分区数据的直接关系创建该索引。（对于非对齐索引，请选择其他分区方案。）
如果您创建了分区表，随后在不指定其他分区方案的情况下创建索引，则对齐是首选方法且会自动执行。使用对齐索引提供了为表创建附加分区的灵活性，并能够将分区所有权转交给另一个表。由于有这些功能存在，因此数据库管理员往往会在一开始就创建已分区表，然后对于索引仅仅使用表分区方案就可能会达到分区目的。
您可以根据表来创建索引，其中，索引中的数据并不与表中的数据对齐。如果数据位于已分区表中，则允许您以不同的方式连接数据（通过查询优化器可将已分区数据与其他已分区数据有效连接起来）。您也可以利用未分区表执行此项操作，此方法可允许您创建已分区索引（根据单个分区表），从而能够方便地进行索引维护。
图 8 中的代码将根据已分区表创建一个已分区的、非群集索引。该非群集索引将与该表对齐并将表的分区列用作非群集索引键。
Figure 8 针对已分区表的已分区、非群集索引 (Close Figure 8)
--Prepare database
IF OBJECT_ID('multiple_partition') IS NOT NULL
DROP TABLE multiple_partition
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = 'Primary_Left_Scheme')
DROP PARTITION SCHEME Primary_Left_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = 'Left_Partition')
DROP PARTITION FUNCTION Left_Partition
GO

--Create partitioned table
CREATE PARTITION FUNCTION Left_Partition (int) AS RANGE LEFT
FOR VALUES (1,10,100)

--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Primary_Left_Scheme
AS PARTITION Left_Partition
--Partition must currently exist in database
    ALL TO ([PRIMARY])

CREATE TABLE multiple_partition
(
col1 INT
,col2 VARCHAR(15)
) ON Primary_Left_Scheme (col1)

--Create partitioned non-clustered index
CREATE NONCLUSTERED INDEX cl_multiple_partition ON multiple_
      partition(col1)

INSERT INTO multiple_partition VALUES (1,'Description')
INSERT INTO multiple_partition VALUES (2,'Description')
INSERT INTO multiple_partition VALUES (3,'Description')
INSERT INTO multiple_partition VALUES (4,'Description')
INSERT INTO multiple_partition VALUES (10,'Description')
INSERT INTO multiple_partition VALUES (11,'Description')
INSERT INTO multiple_partition VALUES (12,'Description')
INSERT INTO multiple_partition VALUES (13,'Description')
INSERT INTO multiple_partition VALUES (14,'Description')
INSERT INTO multiple_partition VALUES (100,'Description')
INSERT INTO multiple_partition VALUES (101,'Description')
INSERT INTO multiple_partition VALUES (102,'Description')
INSERT INTO multiple_partition VALUES (103,'Description')
INSERT INTO multiple_partition VALUES (104,'Description')

--Verify partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] = OBJECT_ID('multiple_partition')

--Verify index partitions
SELECT partition_id, index_id FROM sys.partitions pt
WHERE pt.[object_id] = OBJECT_ID('multiple_partition')
图 9 中的代码将根据已分区表创建一个非对齐的、非群集索引。此非群集索引会将不同的列用作其索引键，这可用于与其他已分区表进行对照连接。
Figure 9 针对已分区表的非对齐、非群集索引 (Close Figure 9)
--Prepare database
IF OBJECT_ID('multiple_partition') IS NOT NULL
DROP TABLE multiple_partition
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = 'Primary_Left_Scheme')
DROP PARTITION SCHEME Primary_Left_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_schemes
WHERE [name] = 'Index_primary_Left_Scheme')
DROP PARTITION SCHEME Index_primary_Left_Scheme
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = 'Left_Partition')
DROP PARTITION FUNCTION Left_Partition
GO

IF EXISTS(SELECT [name] FROM sys.partition_functions
WHERE [name] = 'Index_Left_Partition')
DROP PARTITION FUNCTION Index_Left_Partition
GO

--Create partitioned index function
CREATE PARTITION FUNCTION Index_Left_Partition (int) AS RANGE LEFT
FOR VALUES (10,50,100)

--Create partitioned table
CREATE PARTITION FUNCTION Left_Partition (int) AS RANGE LEFT
FOR VALUES (1,10,100)

--Place all index partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Index_primary_Left_Scheme
AS PARTITION Index_Left_Partition
--Partition must currently exist in database
      ALL TO ([PRIMARY])

--Place all partitions into the PRIMARY filegroup
CREATE PARTITION SCHEME Primary_Left_Scheme
AS PARTITION Left_Partition
--Partition must currently exist in database
      ALL TO ([PRIMARY])

CREATE TABLE multiple_partition
(
col1 INT
,col2 INT
) ON Primary_Left_Scheme (col1)

--Create non-aligned partitioned nonclustered index
CREATE NONCLUSTERED INDEX cl_multiple_partition ON multiple_
     partition(col2)
ON Index_primary_Left_Scheme (col2)

INSERT INTO multiple_partition VALUES (1,10)
INSERT INTO multiple_partition VALUES (2,10)
INSERT INTO multiple_partition VALUES (3,10)
INSERT INTO multiple_partition VALUES (4,10)
INSERT INTO multiple_partition VALUES (10,50)
INSERT INTO multiple_partition VALUES (11,50)
INSERT INTO multiple_partition VALUES (12,50)
INSERT INTO multiple_partition VALUES (13,50)
INSERT INTO multiple_partition VALUES (14,50)
INSERT INTO multiple_partition VALUES (100,100)
INSERT INTO multiple_partition VALUES (101,100)
INSERT INTO multiple_partition VALUES (102,100)
INSERT INTO multiple_partition VALUES (103,100)
INSERT INTO multiple_partition VALUES (104,100)

--Verify row count on partitioned data
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] = OBJECT_ID('multiple_partition')
AND p.index_id = 0

--Verify row count on partitioned index
--Row counts will not match those found in the data partitions
SELECT OBJECT_NAME(ps.[object_id])
,ps.partition_number
,ps.row_count
FROM sys.dm_db_partition_stats ps
INNER JOIN sys.partitions p
ON ps.partition_id = p.partition_id
AND p.[object_id] = OBJECT_ID('multiple_partition')
AND p.index_id <> 0
维护已分区索引 在过去，针对含有数百万甚至数十亿行数据的大型表执行索引维护通常会花费大量时间，而数据库管理员却很难抽出这么多时间。而这种维护往往又因为重建索引时数据被锁定而无法执行。使用 SQL Server 2005 以后，数据库管理员可以在线执行索引维护而不必长时间锁定基础表。但即使是这种方法（允许您在用户访问数据时执行索引维护）也会由于资源使用而减慢系统的运行速度。有一种更好的方法是将索引划分成更小的段，然后针对这些小的分区执行索引维护。例如，要针对某个索引分区执行索引维护，只需将以下代码片段附加在图 8 中所示的代码末尾。
ALTER INDEX cl_multiple_partition
ON multiple_partition
REBUILD Partition = 2
请注意，针对单个索引分区的索引维护必须离线执行，且会在索引维护期间导致表锁定。为防止这种情况发生，您可以将此单个分区移动到一个单独的分区中，执行索引维护，然后再将该分区移回主表。在分区移回表中以及更新群集索引时，此过程会引发某些性能上的问题，但由此所产生的问题与锁定整个表相比要少得多，且只需较少的系统资源。

总结
正如您所看到的，SQL Server 2005 表分区为存储和维护大型表中的数据提供了改进的灵活性，从而不必重新修改应用程序代码或 SQL Server 进程。由于拥有上述功能，SQL Server 证明了自己是适用于企业级、重要数据库的合格平台。

Noah Gomez是 Verizon 的高级 SQL Server 开发 DBA，专门研究 VLDB 及大型应用程序。他是 SQL Server 专业协会 (PASS) 的成员，曾在 Verizon DBA 团队工作过，该团队主要研究多 TB VLDB，并于 2003 年获得了 Winter 公司 Top Ten 大奖奖项。


 SQL Server: 关于 SQL Server 群集的几个关键技巧
SQL Server Tom Moreau 博士

关于 SQL Server 群集的几个关键技巧
概览:
  • 在群集上运行 SQL Server
  • 硬件和软件要求
  • 将一个节点加入群集
  • 节省成本的方法

服务器群集允许您连接许多物理服务器（或节点），用作彼此的故障转移合作伙伴。群集所提供的冗余性为您的关键操作带来了更多的正常运行
时间。在使用 SQL Server™ 的 13 年期间，我实现了许多群集，每个群集都有其自己的一系列问题。这些经历使我积累了许多技巧，它们会帮助您轻松成功地实现群集。
服务器群集利用了 Windows Server® 系列的 Enterprise Edition 中的内置群集功能。实际上，对于群集，使用 Windows Server 2003 要比 Windows 2000 Advanced Server 好得多。要想使您从群集中获得的好处最大化，您需要合适的硬件，而这涉及到一些费用。只是利用共享磁盘将几个服务器拼凑在一起是不够的，您不能依赖这样的事实，即单独的硬件组件可能存在于 Windows® 目录（以前称为硬件兼容性列表）中。系统作为整体必须存在于 Windows 目录中。但不要担心，还有其他一些经过认可的、低成本群集解决方案可用。图 1 显示了一种典型的群集配置。
Figure 1 A typical cluster
Figure 1 A typical cluster
当然，群集比硬件需要更多的条件 - 您还需要选择合适版本的 SQL Server 2005。Enterprise Edition 支持群集功能以及其他一些有用功能，如能够利用更多 CPU、分布式和可更新已分区视图、内置日志传送、自动使用索引视图。如果已经拥有 Enterprise Edition 许可证，则应考虑群集：您是否有构成传统群集所必需的两到八台服务器（我们马上会讨论单节点群集）。如果拥有 SQL Server 2005 Standard Edition，则可以安装两节点群集。
Windows Server 2003 Enterprise Edition 和 Datacenter Edition 附带内置群集功能。安装群集只需运行群集管理器。您可以同时添加所有节点，也可以每次添加一个节点。类似地，在安装 SQL Server 时，您可以选择安装在单独的非群集服务器上，也可以选择将虚拟实例安装在群集上。如果选择安装虚拟实例，可以安装在群集的所有节点上，也可以安装在一部分节点上，甚至仅安装在一个节点上。
最后，为了达到群集的真正目标，即高可用性，需要为您提供合格的人员以及在出现问题时所遵循的预先演练好的过程。尽管群集是防止出现硬件故障的有力保障，但它无法阻止用户出错。例如，午夜时分，一位睡眼朦胧的 DBA 删除了一份重要的表。

单节点群集
尽管您在此刻只拥有一台服务器，也可以考虑创建一个单节点群集。如果这样做，您可以在以后选择升级到群集，从而无需重建。但是，请务必确保您所选择的硬件位于 Windows 目录的群集部分。
这样做不仅仅只是为了实现能够在以后添加节点这个高可用性。如果您发现您的服务器恰好没有必需的功能，那么您猜会发生什么事情。这意味着您需要迁移 - 既费时又费力。如果您有一个单节点群集，则迁移过程就会变得很容易，停机时间也少得多。您需要向群集中添加新节点，将 SQL Server 二进制文件和服务包添加到该新节点，然后故障转移到该新节点。接下来，添加任何服务包之后的更新程序，最后删除旧节点。停机时间只是故障转移时间与添加更新程序（如果有）时间之和。

添加节点
由于一个群集中的所有节点必须相同，您应该立刻（而不是稍后）采取行动，获得另外的节点。如果等待时间太长，节点可能会退出生产。曾经就有这样一个项目，我不得不在 SQL Server 2000 群集中重建节点。我请操作系统/网络管理员处理了基本的计算机构建，然后我投入工作，将构建的计算机添加回群集并准备将其用作 SQL Server 节点。一切都进行得很顺利，直到我需要故障转移到新节点。但令我非常沮丧的是，它却直接执行了故障恢复。长话短说，尽管我已经准备了有关如何构建新群集的详细文档，其中包括如何将群集服务帐户和 SQL Server 服务帐户添加到这两个节点，但显然管理员并没有遵循该文档。管理员没有将这些服务帐户添加到重建节点，所以，他们在重建之前所拥有的权限便不再存在。
我花了很长时间才追捕到这个原因。有一天，我突然想到查看一下本地组成员身份。当我添加了这两个帐户之后，故障转移便顺利进行了。于是我开始思考。虽然您只是偶尔才需要重建节点，但如果需要重建节点，那便是在紧急时刻。尽管我已经提供了文档，但人们并不利用它。只需编写一个简短的脚本来添加这两个帐户及进行任何其他必要的自定义，就能使安全部分自动完成。在 SQL Server 2005 中，事情得到了改善。安装程序要求您为 SQL Server 服务帐户设置域级组。
当然，这让我想得更多。您可以创建几个脚本，它们调用 CLUSTER.EXE 将节点添加到 Microsoft® Cluster Server (MSCS) 群集中。您只需为脚本提供节点名称，然后由脚本处理其余工作。在紧急情况下，自动化确实是您的朋友。

N+1 群集
有时，向群集添加节点的原因不是您要更换节点。您可以将更多的 SQL Server 实例添加到群集中且每个实例都需要不同的磁盘资源。虽然多个实例可以在一个节点上运行，但这些实例会共享 CPU 和 RAM，因此可能会导致性能降低。理想情况下，在一个节点上仅运行一个实例。但在发生故障转移时如何能确保做到这一点呢？很简单：答案是，有一个节点上不运行任何服务，而其他节点则是每个节点上运行一个 SQL Server 实例。实际上，这就是 N+1 群集的定义：在 N+1 个节点上运行 N 个实例。额外的节点是备用节点。

升级 SQL Server
升级 SQL Server 的群集实例不是因为胆小：构建群集只为一个原因 - 您需要正常运行时间。但 SQL Server 2005 提供了许多您想利用的增强功能。所以，如果您准备升级，无需太多停机时间便可以继续进行。
您会选择哪种方案？我们首先看一看成本最高的解决方案：创建整个新群集。这意味着要创建若干新服务器，或许还要创建新的存储区域网络 (SAN)。您或许可以保留现有的网络交换机，但这大约就是您所要保留的全部。显然，这种方法的成本很高，但它具有一定的优势。与旧硬件相比，新硬件的运行通常要好得多，因为磁盘容量和速度都得到了增长。因此，仅仅使用新硬件，您的性能就会得到迅速提高。您甚至可能会租用设备，而这只是为了保持领先地位。
硬件到位后，您可以在此安装上创建新的虚拟 SQL Server，将生产数据库复制过来，然后考察新系统的性能，从而在移交日期之前留有充足的时间来解决程序错误。但别忘了编写脚本，从现有服务器中退出。（万一发生灾难性故障，最好访问 support.microsoft.com/kb/246133 来更新登录构建脚本。）
为了将停机时间减到最少，您很可能必须使用日志传送，除非您的数据库相当小并且在一段时间内没有用户建立连接。在移交之前，您都可以正确执行日志传送。接着，删除这些用户，剪切并传送最后的日志，然后指向新实例上的应用程序。（有关感兴趣的日志传送替代方法，请参阅下面的数据库镜像部分。）如果使用 DNS 别名，您甚至可能不需要指向新实例上的应用程序，而是只需更新 DNS 别名。这种方法的优点是，如果您的迁移只进行了一部分，但必须要回退到原始状态，那您至少还有原始文件。
您还可以采用一种成本较低的方案，但需要您做更多的预先规划。一个群集可以支持多个 SQL Server 实例，但每个实例必须有其自己的磁盘资源。因此，在划分 SAN 时，请留出一个 LUN，以备将来升级。要执行升级，请在此磁盘资源上安装 SQL Server 二进制文件。您可以演习一下该系统。当您准备好后，关闭当前 SQL Server，将磁盘资源从旧的 SQL Server 组中移出，更新依赖关系，然后使新 SQL Server 实例在线。连接旧实例中的数据库，然后启动并运行。（您已提早备份了所有数据，对吗？）
这就是成本较低的方法，实行这个方法需要承担一些风险。如果出现故障，您无法将数据库与新实例分离开来并放回原来位置。您的操作已简化为从备份恢复 - 这意味着需要很长的停机时间。
还有一种方法是将两个 SQL Server 实例都放在您的 SAN 中，前提是您有足够的磁盘空间。将生产备份（和日志传送）恢复为新实例，然后按前面介绍的步骤继续进行。但现在您有退路了。而且，一旦完成迁移，您还可以释放旧实例占用的 SAN 资源。您只需增加额外的磁盘。

负载平衡
让我们首先揭穿这样一个常见误解。MSCS 群集是用于获得高可用性的，而非用于实现负载平衡。此外，SQL Server 没有任何内置的、自动负载平衡功能。您必须通过应用程序的物理设计来实现负载平衡。这意味着什么？
随着表的逐渐增长，您可能会预料到性能会降低，特别是在涉及到表扫描操作时。当行数达到数百万或数十亿时，传统的解决方案会使用已分区视图，这种视图由若干具有相同结构、使用 UNION ALL 挂接在一起的表组成。此外，还会在适当位置放置 CHECK 约束来区分这些成员表，而这会阻止跨已分区视图复制数据。如果在 CHECK 约束中使用的列也是主键的一部分，则该视图是可更新的。
如果成员表在其自己的文件组中，则如果这些文件组中的文件分别位于不同的物理驱动器上，那么您会获得更佳的磁盘性能。这些表甚至也可以位于不同的数据库中。但是，在 SQL Server 2005 中，只要所有数据均在同一个数据库中，您就可以使用表分区，而表分区实现起来就容易得多了。
但是，假设您已经尽可能地利用了表分区或（本地）已分区视图，但性能仍然很低。如果您拥有 SQL Server 2000 或 SQL Server 2005，就可以利用分布式已分区视图了。主要差别在于，成员表可以位于不同的 SQL Server 实例上，而且这些实例可以安装在 N+1 群集上。为什么鼓励您这样做？如果已分区视图中的任何一个成员表转入离线状态，则整个视图也将转入离线状态。使这些成员成为群集的一部分可以为您提供支持性能和实现负载平衡所需的可靠性。

您真的需要群集吗？
或许您有一些备用服务器无事可做，但这些服务器不在 Windows 目录的群集部分中。如果您在这些服务器可用的情况下，只是为了支持群集就必须出去购置新服务器，那么这是一种浪费可耻的行为。
数据库镜像可能是最适合替代群集的一种方法。镜像涉及到三个元素：存储镜像数据库的实例称为主体；备份服务器称为镜像；如果要实现自动故障转移，还需要第三台服务器，称为见证方。简而言之，主体上的数据库中的事务会在镜像中再次运行。当主体出现故障时，如果有见证方，数据库会自动故障转移到镜像。您必须为每个应用程序数据库设置镜像，但不能镜像系统数据库。
镜像是单独的 SQL Server 实例，与群集不同的是，镜像可以位于几千英里以外。其高速缓存中填充的是由于从主体中复制事务而发生的更新活动。当然，还可以假设，除了从主体接收镜像事务之外，镜像上没有其他活动。既然 SQL Server 已经在镜像中运行，所以，故障转移的速度通常要比在群集中快。由于至少有部分高速缓存已准备好，所以，初始性能并不像在群集方案中那样低。另请注意，当镜像数据库发生故障转移时，主体和镜像会互换角色。
数据库镜像的不足之处是，需要的总磁盘容量是群集的两倍。如果您想在同步模式下运行且不想丢失任何数据，那么您还会需要更多的 CPU 处理能力。正如我所说的，要想实现高可用性，需要花费很高的成本。

组合方法
由于镜像与主体之间的距离可以相当遥远，所以对于灾难恢复 (DR) 计划来说，选择镜像是非常明智的。群集是您的第一道防线。但是，如果您要同时利用群集和镜像，那会出现什么情况呢？在群集故障转移中，如果您的镜像配置中有见证方，则当群集 SQL Server 转入在线状态时，镜像会成为主体。但是，请注意，从新主体回到（群集的）新镜像的故障转移不是自动进行的。因此，当与群集结合使用时，最好不要对您的镜像数据库启用自动故障转移。
灾难恢复并不是您使用镜像的唯一原因；当您必须向主体应用服务包或修补程序时，镜像也是非常有用的。在这种情况下，您可以手动故障转移到镜像。在应用服务包或修补程序时，旧的主体服务器暂时处于离线状态，在新主体上发生的已提交事务会排队等候，等待被发送回新镜像（旧主体）。在完成服务包或修补程序的安装之后将会进行同步。最终，这两台服务器将完全处于同步状态。现在您便可以在主体和镜像之间转换角色了。故障转移与恢复只需要几秒钟的停机时间。您可以使用这种方法将 SQL Server 迁移到另一台计算机。只是不能实现故障恢复。

虚拟服务器添加灵活性
虚拟化允许您在一台物理服务器上并行运行一个或多个操作系统。虚拟化软件为群集概念添加了另外一层功能，因为您可以将软件加入群集。因此，如果主机正在其上运行的服务器出现故障，则主机及其来宾 OS 会故障转移到备份节点。这可能是迁移来宾服务器的最简便方法。补充一点，来宾 OS 不必具有群集功能。因此，您可以在运行于某群集中的 Microsoft Virtual Server 2005 之上的来宾 Windows Server 2003 内部运行 SQL Server Workgroup Edition。实质上，您会间接拥有群集 Workgroup Edition（请参见图 2）。
Figure 2 Using a virtual server
Figure 2 Using a virtual server

在控制之下
如果您在负责 SQL Server 实现，您需要确信您的服务器始终处于可用状态。服务器群集会帮助确保您的服务器始终可用。本文提供了一些来之不易的技巧，以帮助您入门。您可以在“群集资源”边栏中找到更多有用信息。
群集资源
有关本文所用方法的详细信息以及建立 SQL Server 群集所需的各种产品，请参阅以下几篇文章：
  • SQL Server 2005 Failover Clustering（英文）
  • SQL Server 2000 故障转移群集
  • Windows Server 2003 R2 Enterprise Edition–Cluster Server
  • Microsoft Cluster Server Administrator’s Guide（英文）
  • Microsoft Virtual Server 2005 R2（英文）

Tom Moreau 博士曾获得理学学士和博士学位，拥有 MCSE 和 MCDBA 认证，他是一位自由顾问，还是 SQL Server 数据库管理、设计和实现方面的专家，现居住于多伦多地区。Tom 从 1993 年开始就一直使用 SQL Server，并从 2001 年开始担任 MVP。他曾撰写过 100 多篇文章，还与人合著过有关 SQL Server 的书籍。感谢 SQL Server MVP Geoff Hiten 提供的有用信息。


 Windows 管理: 了解 Windows Vista 内核：第 2 部分
Windows 管理 Mark Russinovich

了解 Windows Vista 内核：第 2 部分
概览:
  • 内存管理
  • 启动和关闭
  • 电源管理

上个月，我在这个由三部分构成的系列文章的第一部分，分析了 Windows Vista 内核在进程和 I/O 方面的增强功能。
这一次，我将涵盖到 Windows Vista 对内存管理方式的提升以及系统启动、关闭和电源管理方面的主要改进（第 1 部分）。
Windows® 随着每个发行版本的问世都在可伸缩性和性能方面进行了改进，Windows Vista™ 也不例外。Windows Vista 内存管理器包含了大量增强功能，例如，更大范围地使用无锁同步技术、锁定粒度更细、数据结构包更紧密，分页 I/O 更大、增加了对现代 GPU 内存体系结构的支持，并更有效利用了硬件的转换旁路缓冲器。另外，Windows Vista 内存管理如今还针对不同工作负荷的要求提供动态地址空间分配。
以下四种采用新技术的性能增强型功能首次在 Windows Vista 操作系统上登台亮相：SuperFetch、ReadyBoost、ReadyBoot 和 ReadyDrive。本文稍后将详细讨论这些功能。

动态内核地址空间
Windows 以及其上所运行的应用程序占用的地址空间已经达到了 32 位处理器的地址空间极限。默认情况下，Windows 内核被限制在 2GB，或者是 32 位虚拟地址空间总量的一半，另一半则预留给当前正在 CPU 中运行线程的进程使用。在其自己的那一半虚拟地址空间中，内核必须映射其自身、设备驱动程序、文件系统缓存、内核堆栈、每会话代码数据结构，以及由设备驱动程序分配的非分页（锁定的物理内存）缓冲区和分页缓冲区。在 Windows Vista 推出之前，内存管理器在引导时确定针对这些用途分配多少地址空间，但这种不变性有时会造成其中某一区域空间被占满，而其他区域仍有大量可用空间的情况。区域空间被用尽会导致应用程序出现故障，并会妨碍设备驱动程序完成 I/O 操作。
在 32 位 Windows Vista 中，内存管理器动态管理内核的地址空间，根据工作负荷的具体需求为各种用途分配和释放空间。这样，用于存储分页缓冲区的虚拟内存量会随着设备驱动程序需求量增加而增加，并会在驱动程序释放它时缩小。因此，Windows Vista 将能够处理更大范围的工作负荷，同样，即将推出的代号为“Longhorn”的 32 位版本 Windows Server® 也将升级到可以处理更多的并行终端服务器用户。
当然，在 64 位 Windows Vista 系统上，地址空间约束目前还不属于实质性的限制，因此，在将这些约束配置为相应最大值时，不需要对其进行任何特殊处理。

内存优先级
就像 Windows Vista 添加 I/O 优先级（如上一部分中所述）一样，它还实现内存优先级。要了解 Windows 如何使用内存优先级，就需要掌握内存管理器如何实现其内存缓存（称为“待机列表”）。在 Windows Vista 之前的所有 Windows 版本中，当某个进程所拥有的物理页面（大小一般为 4KB）被系统回收时，内存管理器通常将该页面置于“待机列表”末尾。如果进程想要再次访问该页面，内存管理器会从“待机列表”获取该页面，然后将其重新分配给该进程。如果进程想要使用物理内存的新页面，但没有可用的内存，则内存管理器会为其提供“待机列表”前端的页面。此方案基本上对待机列表中的所有页面都同等对待，仅按页面被置于列表中的时间来对它们进行排序。
在 Windows Vista 上，内存的每个页面都具有一个在 0 到 7 之间的优先级，这样，内存管理器会将“待机列表”划分为八个列表，每一个都用来存储具有特定优先级的页面。当内存管理器想要从“待机列表”中获取某一页面时，它会先从低优先级列表获取页面。页面的优先级通常反映的是第一个导致该页面分配的线程的优先级。（如果页面是共享的，它反映的就是共享线程的最高内存优先级。）线程会从其所属的进程继承它的页面优先级值。当内存管理器预见到某一进程要访问内存时，会将低优先级用于它从磁盘推测性读取的页面。
默认情况下，进程的页面优先级值为 5，但应用程序和系统可通过函数更改进程和线程的页面优先级值。只有从宏观上理解页面的相对优先级时，才能意识到内存优先级的真正价值，也就是 SuperFetch 所发挥的作用。

SuperFetch
内存管理器的重大改变体现在它对物理内存的管理方式。先前版本 Windows 所使用的“待机列表”管理有两个局限性。首先，页面的优先化仅取决于进程最近过去的行为，而不会预见到它们未来的内存需求。其次，用于优先化的数据仅限定于进程在任意给定时刻所拥有的页面列表。这两个缺点会导致出现“午餐后综合症”之类的状况，即您离开计算机一段时间，但需要内存密集型的系统应用程序在此期间一直都在运行（例如病毒扫描或磁盘碎片整理）。此应用程序会强制您的活动应用程序已在内存中进行缓存处理的代码和数据由内存密集型活动重写。等您回来后，就会发现性能变得非常缓慢，因为各应用程序必须从磁盘请求它们的数据和代码。
Windows XP 采用了预取支持，该功能基于以前的引导和应用程序启动来执行大规模的磁盘 I/O，以向内存预加载所预期到的代码和文件系统数据，从而改进了引导和应用程序启动性能。Windows Vista 凭借 SuperFetch 又向前迈进了一大步，SuperFetch 是一种通过历史信息和前瞻性内存管理来增强“least-recently accessed”（最近最少访问的）方法的内存管理方案。
SuperFetch 作为在服务主机进程 (%SystemRoot%\System32\Svchost.exe) 内运行的 Windows 服务在 %SystemRoot%\System32\Sysmain.dll 中实现。该方案依赖于内存管理器提供的支持，因此它可以检索页面使用历史，以及引导内存管理器将来自磁盘文件或分页文件的数据和代码预加载到“待机列表”中，并为各页面指定优先级。SuperFetch 服务基本上是将页面跟踪扩展到曾经存储在内存中但已被内存管理器重新使用以为新数据和代码让出空间的数据和代码。该服务会将这一信息存储在 %SystemRoot%\Prefetch 目录中扩展名为 .db 的场景文件中（位于用于优化应用程序启动的标准预取文件旁边）。在对内存使用情况的这种深入了解基础上，SuperFetch 可在物理内存变为可用时预加载数据和代码。
只要内存变为可用（例如，当某应用程序退出或释放内存时），SuperFetch 便会要求内存管理器提取最近被驱出的数据和代码。这将以每秒少数几页的速率完成，并且 I/O 的优先级为“非常低”，以便预加载操作不会影响用户或其他活动应用程序。因此，如果您离开计算机去享用午餐，并且某个内存密集型的后台任务导致活动应用程序的代码和数据在您离开期间被驱出内存，则 SuperFetch 通常会在您回来之前将所有或大多数代码和数据返回到内存中。SuperFetch 还包含了对休眠、待机、快速用户切换 (FUS) 和应用程序启动的特定场景支持。例如，当系统处于休眠状态时，SuperFetch 会将数据和代码存储在它预期（基于以前的休眠）将在后续恢复期间被访问的休眠文件中。相比之下，当您恢复 Windows XP 时，先前缓存的数据在被引用时必须从磁盘重新读取。
请参阅边栏“观察 SuperFetch”以简单了解 SuperFetch 如何影响可用内存。
观察 SuperFetch
在您使用 Windows Vista 系统一段时间后，您会发现任务管理器“性能”页面上的“可用物理内存”计数器的数值很低。这是因为 SuperFetch 和标准 Windows 缓存处理使用了所有可用物理内存来缓存磁盘数据。例如，在您首次引导时，如果您立即运行任务管理器，则您会注意到，可用内存值会随着已缓存内存量的增加而减少。如果您运行一个内存需求很大的程序，然后退出该程序（分配大量内存并在之后释放内存的任何免费软件“RAM 优化程序”都适用），或者刚刚复制了一个非常大的文件，则在系统回收已释放内存时可用内存量将增加，并且物理内存用量图将下降。但随着时间的过去，SuperFetch 会用被强制离开内存的数据重新填充缓存，这样，已缓存内存量将增加，可用内存量将下降。
Watching memory

ReadyBoost
CPU 和内存的速度快到超越了硬盘的速度，因此磁盘是一个常见的系统性能瓶颈。随机磁盘 I/O 成本尤为高昂，因为磁头寻道时间约为 10 毫秒，这对于现今的 3GHz 处理器来说有些漫长。尽管 RAM 是用于缓存磁盘数据的理想选择，但它的成本也相对较高。不过，闪存通常较为便宜，它随机读取的速度最高可比典型硬盘快 10 倍。因此，Windows Vista 中加入了一个名为 ReadyBoost 的功能来利用闪存存储设备，方法是在这些设备上创建一个逻辑上介于内存和磁盘之间的中间缓存层。
ReadyBoost 由一个在 %SystemRoot%\System32\Emdmgmt.dll 中实现的运行于服务主机进程中的服务和一个卷过滤器驱动程序 (%SystemRoot%\System32\Drivers\Ecache.sys) 组成。（Emd 是“外部内存设备”的简称，即 ReadyBoost 在开发期间所使用的工作名称。）当您将 USB 密钥之类的闪存设备插入到系统中时，ReadyBoost 服务会查看该设备以确定其性能特征并将测试结果存储在 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\Currentversion\Emdmgmt 中（如图 1 所示）。
Figure 1 ReadyBoost device test results in the registry
Figure 1 ReadyBoost device test results in the registry
如果您还未使用设备进行缓存处理，并且新设备大小介于 256MB 和 32GB 之间、对于 4KB 随机读取的传输率为 2.5MB/s 或更高、对于 512KB 随机写入的传输率为 1.75MB/s 或更高，则 ReadyBoost 将询问您是否想要将高达 4GB 的存储空间专门用于进行磁盘缓存。（尽管 ReadyBoost 可以使用 NTFS，它还是会将最大缓存大小限制在 4GB，以适应 FAT32 限制。）如果您同意，该服务便会在设备的根目录下创建一个名为 ReadyBoost.sfcache 的缓存文件，并要求 SuperFetch 在后台预先填充缓存。
在 ReadyBoost 服务对缓存进行初始化之后，Ecache.sys 设备驱动程序会将所有读写数据截取到本地硬盘卷（例如 C:\），并将要写入的所有数据复制到该服务创建的缓存文件中。Ecache.sys 会将数据压缩，压缩比通常达到 2:1，这样，4GB 的缓存文件通常将包含 8GB 数据。驱动程序会联合使用高级加密标准 (AES) 和一个随机生成的每引导会话密钥对其写入的每个块进行加密，以在将设备从系统移除的情况下保证缓存中数据的保密性。
当 ReadyBoost 确定可从缓存满足随机读取需求时，它便会从那里向随机读取提供服务，但由于硬盘的有序读取访问要胜过闪存，因此，它允许有序访问模式中的读数据直接移至磁盘，即使该数据位于缓存中。

ReadyBoot
如果系统的内存不到 512MB，则 Windows Vista 会使用与 Windows XP 一样的引导时预取，但如果系统的 RAM 为 700MB 或以上，它便会使用 RAM 内缓存来优化引导进程。缓存的大小取决于可用 RAM 总量，但这足以创建适当的缓存，并还可以为系统留出要顺利引导所需的内存。
在每一次引导后，ReadyBoost 服务（就是刚刚介绍的用于实现 ReadyBoost 功能的服务）会使用空闲 CPU 时间来为下一次引导计算引导时缓存计划。它会分析来自前五次引导的文件跟踪信息，并标识出访问了哪些文件以及这些文件在磁盘上的位置。该服务将已处理的跟踪信息以 .fx 文件形式存储在 %SystemRoot%\Prefetch\Readyboot 中，并将缓存计划保存在 HKLM\System\CurrentControlSet\Services\Ecache\Parameters 下的 REG_BINARY 值（这些值针对它们所引用的内部磁盘卷而命名）中。
缓存由实现 ReadyBoost 缓存处理的同一设备驱动程序 (Ecache.sys) 实现，但缓存的填充则是由 ReadyBoost 服务在系统引导时带领完成。尽管引导缓存像 ReadyBoost 缓存一样进行压缩，但 ReadyBoost 和 ReadyBoot 缓存管理之间的另一个区别是，在 ReadyBoot 模式下，除了 ReadyBoost 服务的更新之外，缓存不会变为反映在引导期间读取或写入的数据。ReadyBoost 服务会在引导开始后 90 秒时（或者在其他内存需求批准它的情况下）将缓存删除，并将缓存的统计信息记录在 HKLM\System\CurrentControlSet\Services\Ecache\Parameters\ReadyBootStats 中（如图 2 所示）。Microsoft 性能测试表明，与旧有 Windows XP 预取器相比，ReadyBoot 使性能提高了约 20%。
Figure 2 ReadyBoot Performance statistics
Figure 2 ReadyBoot Performance statistics

ReadyDrive
ReadyDrive 是一项利用了名为 H-HDD 的新型混合硬盘驱动器的 Windows Vista 功能。H-HDD 是一种带有嵌入式非易失性闪存（还称为 NVRAM）的磁盘。典型 H-HDD 所包含的缓存大小介于 50MB 和 512MB 之间，但 Windows Vista 缓存限制为 2TB。
Windows Vista 使用 ATA-8 命令来定义要在闪存中存放的磁盘数据。例如，Windows Vista 会在系统关闭时将引导数据保存到缓存，从而可以更快速地重新启动。它还会在系统处于休眠状态时将某些部分的休眠文件数据存储在缓存中，以便加速后来的恢复过程。即使在磁盘盘片降速时也会启用缓存，因此，Windows 可以将闪存用作磁盘写入缓存，这样可避免在系统靠电池电源运行时将磁盘盘片加速。使磁盘轴保持关闭状态可以节省由磁盘驱动器在正常使用期间所消耗的大量电源。

引导配置数据库
Windows Vista 在启动和关闭方面增强了许多功能。随着“引导配置数据库”(BCD) 的采用，启动功能在存储系统和 OS 启动配置、系统启动进程的新流程和组织、新登录体系结构以及对延迟式自动启动服务的支持方面都有所改进。Windows Vista 关闭方面的变化包括 Windows 服务的预关闭通知、Windows 服务关闭排序以及 OS 对电源状态转换的管理方式的重大改变。
启动进程的最明显变化之一是，系统卷的根目录中没有了 Boot.ini。这是因为，引导配置现在存储在 BCD 中（在先前版本的 Windows 上，它存储在 Boot.ini 文本文件中）。Windows Vista 使用 BCD 的其中一个原因是，BCD 将 Windows 支持的两个当前引导体系结构合为了一体：主引导记录 (MBR) 和可扩展固件接口 (EFI)。MBR 通常由 x86 和 x64 桌面系统使用，而 EFI 则由基于 Itanium 的系统使用（但在不久的将来，桌面 PC 很有可能会附带 EFI 支持）。BCD 对固件进行了抽象化，并具有超越 Boot.ini 的其他优点，例如，对 Unicode 字符串和备用预引导可执行文件的支持。
BCD 实际上存储在磁盘上的某个加载到 Windows 注册表中以通过注册表 API 进行访问的注册表配置单元中。在 PC 上，Windows 将其存储在系统卷上的 \Boot\Bcd 中。在 EFI 系统上，它位于 EFI 系统分区上。在加载了该配置单元后，它会在 HKLM\Bcd00000000 下显示，但因其内部格式没有文档记录，所以编辑它时需要使用 %SystemRoot%\System32\Bcdedit.exe 之类的工具。也可通过 Windows Management Instrumentation (WMI) 将用来操纵 BCD 的接口提供给脚本和自定义编辑器使用，并且可以使用“Windows 系统配置实用程序”(%SystemRoot%\System32\Msconfig.exe) 来编辑或添加基本参数（如内核调试选项）。
BCD 将平台范围的引导设置（例如，默认 OS 选择和引导菜单超时时间）与 OS 特定的设置（例如，OS 引导选项和 OS 引导加载器的路径）分割开来。例如，图 3 显示了在您未使用命令行选项运行 Bcdedit 时，BCD 会在输出上部的“Windows 引导管理器”部分显示平台设置，接着在“Windows 引导加载器”部分显示 OS 特定的设置。
Figure 3 Settings displayed in BCDEdit
Figure 3 Settings displayed in BCDEdit
当您引导 Windows Vista 安装时，这个新方案会将在先前版本 Windows 上由操作系统加载器 (Ntldr) 处理的任务划分为两种不同的可执行文件：\BootMgr 和 %SystemRoot%\System32\Winload.exe。Bootmgr 会读取 BCD 并显示 OS 引导菜单，而 Winload.exe 会处理操作系统加载。如果您要执行干净引导，则 Winload.exe 会加载引导启动设备驱动程序和核心操作系统文件（包括 Ntoskrnl.exe），并将控制权移交给操作系统；如果系统要从休眠状态恢复，则 Winload.exe 会通过执行 %SystemRoot%\System32\Winresume.exe 将休眠数据加载到内存中并恢复 OS。
Bootmgr 还包含对其他预引导可执行文件的支持。Windows Vista 还随带了 Windows 内存诊断 (\Boot\Memtest.exe)，该工具已被预配置为用于检查 RAM 性能状况的选项，但第三方可以添加其自己的预引导可执行文件，以作为将在 Bootmgr 的引导菜单中显示的选项使用。

启动进程
在先前版本的 Windows 中，各种系统进程之间的关系并不直观。例如，在系统引导时，交互登录管理器 (%SystemRoot%\System32\Winlogon.exe) 会启动“本地安全机构子系统服务”(Lsass.exe) 和“服务控制管理器”(Services.exe)。此外，Windows 会使用一个名为 Session 的命名空间容器来隔离在不同登录会话中运行的进程。但在推出 Windows Vista 之前，登录到控制台的用户共享的是 Session 0（即，由系统进程使用的会话），这就造成了潜在的安全问题。例如，如果某个运行于 Session 0 中的编写拙劣的 Windows 服务在交互式控制台上显示一个用户界面，从而允许恶意软件通过粉碎攻击之类的方法来攻击窗口并有可能获得管理特权，就会引发此类安全问题。
为解决这些问题，若干个系统进程都针对 Windows Vista 进行了重新设计。会话管理器 (Smss.exe) 是在使用先前版本的 Windows 时在引导期间创建的第一个用户模式进程，但在 Windows Vista 上，会话管理器会启动自己的另一个实例来配置 Session 0，该会话将独自供系统进程专用。用于 Session 0 的会话管理器进程将启动“Windows 启动应用程序”(Wininit.exe)（一个用于 Session 0 的 Windows 子系统进程 (Csrss.exe)），然后退出。“Windows 启动应用程序”会通过启动“服务控制管理器”、“本地安全机构子系统”和一个用来管理计算机的终端服务器连接的新进程（即“本地会话管理器”(Lsm.exe)）持续运行。
当用户登录到系统上时，初始的会话管理器会创建其自己的一个新实例来配置新会话。新的 Smss.exe 进程会为这个新会话启动 Windows 子系统进程和 Winlogon 进程。让主会话管理器使用自己的副本初始化新会话并不会为客户端系统带来任何有利条件，但在充当终端服务器的 Windows Server“Longhorn”系统上，可以并行运行多个副本以提高多个用户的登录速度。
在这个新的体系结构下，各系统进程（包括 Windows 服务）在 Session 0 中进行了隔离。如果运行于 Session 0 中的某个 Windows 服务显示一个用户界面，则“交互服务检测”服务 (%SystemRoot%\System32\UI0Detect.exe) 会通过在用户的安全上下文中启动自己的实例并显示图 4 中所示的消息来通知所有登录的管理员。如果用户选择“显示消息”按钮，该服务会将桌面切换到 Windows 服务桌面，用户可在这里与服务的用户界面交互，然后再切换回自己的桌面。有关启动时会出现哪些情况的详细信息，请参阅边栏“查看启动进程关系”。
Figure 4 Service has displayed a window
Figure 4 Service has displayed a window
查看启动进程关系
可使用 Sysinternals (microsoft.com/technet/sysinternals) 提供的 Process Explorer 来查看 Windows Vista 的进程启动树。
屏幕快照包括“会话”列，您可通过 Process Explorer 的列对话框添加它。突出显示的进程是初始的 Smss.exe。位于它下面的是 Session 0 的 Csrss.exe 和 Wininit.exe，由于这两个进程的父进程（用于配置 Session 0 的 Smss.exe 实例）已经退出，因此它们左对齐。Wininit 的三个子进程分别是 Services.exe、Lsass.exe 和 Lsm.exe。
Process Explorer 将一组进程标识为在 Session 1 中运行，也就是我通过远程桌面连接登录到的会话。Process Explorer 用蓝色突出色来显示与自身使用相同帐户运行的进程。最后，将 Session 2 初始化，以为登录到控制台并创建新登录会话的用户做好准备。就是在该会话中，Winlogon 将运行并使用 LogonUI 要求新的控制台用户“按 Ctrl+Alt+DELETE 进行登录”，而且在该会话中，Logonui.exe 将要求用户提供其凭据。
Startup process and session information
Startup process and session information

凭据提供程序
即使是登录体系结构也在 Windows Vista 上发生了变化。在先前版本的 Windows 上，Winlogon 进程会加载在注册表中指定的“图形识别与认证”(GINA) DLL 来显示要求用户提供其凭据的登录 UI。遗憾的是，GINA 模式受到了许多限制，包括只能配置一个 GINA、第三方很难编写完整的 GINA，以及具有非标准用户界面的自定义 GINA 会改变 Windows 用户体验。
而 Windows Vista 使用了新的“凭据提供程序”体系结构来代替 GINA。Winlogon 会启动一个单独的进程，即“登录用户界面主机”(Logonui.exe)，该进程将加载在 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\Currentversion\Authentication\Credential Providers 中配置的凭据提供程序。Logonui 可以并行托管多个凭据提供程序；实际上，Windows Vista 随带了交互式 (Authui.dll) 提供程序和智能卡式 (Smart-cardcredentialprovider.dll) 提供程序。为确保统一的用户体验，LogonUI 会管理对最终用户显示的用户界面，但它还会允许凭据提供程序指定文本、图标和编辑控件之类的自定义元素。

延迟式自动启动服务
如果您曾经在 Windows 系统启动后立即登录到系统上，您或许在桌面被完全配置并且您可以与外壳和所启动的任何应用程序进行交互之前经历了一些延迟。在您登录时，“服务控制管理器”会启动多个被配置为自动启动服务并因此在引导时激活的 Windows 服务。许多服务都会执行与登录活动争用资源的 CPU 密集型初始化和磁盘密集型初始化。为解决此问题，Windows Vista 采用了一个被称为延迟式自动启动的新服务启动类型，如果服务不必在 Windows 引导后立即激活，便可使用该类型。
“服务控制管理器”会在自动启动服务完成启动后再启动配置为延迟式自动启动的服务，并将这些服务的初始线程优先级设置为 THREAD_PRIORITY_LOWEST。此优先级别会使线程执行的所有磁盘 I/O 都采用“非常低”I/O 优先级。在服务完成初始化后，“服务控制管理器”会将其优先级设置为普通。将延迟式启动、较低的 CPU 和内存优先级与后台磁盘优先级结合后，大大减少了与用户登录间的相互冲突。许多 Windows 服务（包括后台智能传输、Windows Update 客户端和 Windows Media® Center）都使用这个新启动类型来帮助提升引导后的登录性能。

关闭
困扰着 Windows 服务编写人员的一个问题是，在 Windows 关闭过程中，默认情况下他们最多只有二十秒钟的时间来执行清理。Windows Vista 之前的 Windows 版本一直不支持等待所有服务都退出的干净关闭，因为存在程序错误的服务可能会无限期地阻止关闭。有些服务（例如那些具有与网络相关的关闭操作或必须将大量数据保存到磁盘的服务）可能需要更多的时间，因此 Windows Vista 允许服务请求预关闭通知。
在 Windows Vista 关闭时，“服务控制管理器”会首先通知那些要求预关闭通知的服务。它将无限期地等待这些服务退出，但如果这些服务存在程序错误且没有响应查询，则“服务控制管理器”会在三分钟后放弃并继续前进。一旦所有这些服务都已退出或超时时间已满，“服务控制管理器”就会接着对剩余的服务执行旧有形式的服务关闭。组策略和 Windows Update 服务会在全新的 Windows Vista 安装中注册预关闭通知。
组策略和 Windows Update 服务还会使用另一个 Windows Vista 服务功能：关闭排序。各服务始终都可以指定启动依存性，“服务控制管理器”要服从这些依存性以按照满足这些依存性的顺序来启动服务，但在 Windows Vista 之前，各服务还无法指定关闭依存性。现在，注册预关闭通知的服务也可以将其自己插入到存储在 HKLM\System\CurrentControlSet\Control\PreshutdownOrder 的列表中，“服务控制管理器”将根据它们的相应顺序将其关闭。有关这些服务的详细信息，请参阅边栏“标识延迟式自动启动和预关闭服务”。

电源管理
睡眠和休眠属于其他形式的关闭，自 Windows 2000 将电源管理引入到 Windows NT® 系列的 Windows 操作系统后，驱动程序和应用程序方面的问题重重的电源管理一直都是令商务旅行人士苦恼的一件事情。当许多用户在踏上旅途前合上自己的便携式计算机外盖时，都期望计算机系统处于挂起或休眠状态，但没想到在到达目的地时，手提箱已经变得灼热，电池也被用尽，而且还丢失了数据。这是因为 Windows 始终都会询问设备驱动程序和应用程序是否允许更改电源状态，只要有一个驱动程序或应用程序未响应，就可能会阻止转换。
在 Windows Vista 中，内核的电源管理器仍会通知驱动程序和应用程序要更改电源状态以便它们可以为这些更改做好准备，但不会再请求许可。此外，电源管理器最多会留出 20 秒来等待应用程序对更改通知做出响应，而不是像在先前版本的 Windows 上那样等待 2 分钟。因此，Windows Vista 用户可以更加确信自己的系统会执行休眠和挂起。

预告
正如我先前所说的，这是由三部分组成的系列文章中的第二部分。第一部分介绍了 Windows Vista 内核在 I/O 和进程方面的改进。这一次，我分析了 Windows Vista 在内存管理、启动和关闭方面的增强。下一次，我将通过介绍内核在可靠性和安全性方面的改变来结束本系列文章。
标识延迟式自动启动和预关闭服务
内置的 SC 命令在 Windows Vista 得到了更新，以显示配置为延迟式自动启动服务的服务：
Using SC to display start type
Using SC to display start type
遗憾的是，SC 命令不会报告已请求预关闭通知的服务，但您可以使用 Sysinternals 提供的 PsService 实用程序来确保服务接受预关闭通知：
Viewing pre-shutdown status
Viewing pre-shutdown status

Mark Russinovich 是 Microsoft 的“平台和服务部”的一名技术人员。他是《Microsoft Windows Internals》（Microsoft Windows 内部结构）(Microsoft Press, 2004) 的合著者之一，并经常在 IT 和开发人员会议上演讲。在 Microsoft 最近收购了 Mark Russinovich 与其他人创办的 Winternals Software 之后，Mark Russinovich 也随之加入了 Microsoft。他还创办了 Sysinternals，在那里他发布了 Process Explorer、Filemon 和 Regmon 实用程序。


 编辑视角: 荣誉窗
编辑视角
荣誉窗
Joshua Trupin

我已走过三十年 的人生旅途，但从未获得过任何奖项。我的即开型彩票始终都是以失望而告终。我打开糖果包装机，它们会说“对不起，请重试”。我在足球的决赛赌博中选择了纽约队。
回头想一下这些可怜的记录，我迄今为止没有获得任何奖项也就不奇怪了。TechNet 杂志刚刚获得了由“技术交流协会”(STC) 授予的四个奖项。我们对于每个问题都投入了大量的精力，因此工作得到认可实在是太好了。我想花一点时间来感谢我们的员工和作者所做出的贡献。
2006 年 3 月/4 月刊中以拼图海报的形式为您提供了一个即清晰又可访问的 Active Directory 指南。它受到了大家的普遍欢迎 - 实际上，我们现在每天还会收到申请副本的请求！STC 为我们的海报授予了“优秀软件指南奖”。
还有两篇文章也取得了很好的成绩。2006 年 8 月由 Greg Taylor 编写的“在沙盒中使用 Exchange”和 2006 年 5/6 月由 Jesper Johansson 和 Steve Riley 共同编写的“解构常见的安全说法”获得了 STC 授予的“优异奖”。
TechNet 杂志本身赢得了“优秀学术和专业期刊奖”。我为这些认可而感到无比骄傲，团队中每个为此付出努力的人也都同样感到骄傲。
别担心，我们不会满足于这些荣誉而停滞不前的。在本期中，Mark Russinovich 探究了 Windows Vista 中内核更改的多个方面，会为参与桌面部署或支持服务的所有 IT 专业人士提供具有持久价值的重要信息。本月我们还为您带来了 SQL Server 的详细内容，它涵盖了从使用索引元数据到表分区及高可用性选项到群集技巧的信息。
我们 TechNet 杂志中心从不休息。（期限由您决定。）不过，我们的聚会地点偶尔会有一些变化。在 2007 年 6 月的 4-8 日期间，我们参加奥兰多市的 Microsoft TechoEd（请参阅 microsoft.com/teched）。顺便拜访一下 TechNet 杂志的获奖编辑和作者。我们计划要获得一些赠品和有趣的东西，因此，您可能永远也不会知道 - 我们也可让您成为一个成功者。-JOSHUA TRUPIN
衷心感谢以下 Microsoft 技术专家： Vijay Bharadwaj、Paul Bowden、Elden Christensen、Manish Karla、Michael Murgolo、Rajeev Nagar、John Peng、Mark Russinovich、Abraham Samuel、Mark Scurrell、Jim Truher、George Walters 和 Buck Woody。

Joshua Trupin


 工具箱: 面向 IT 专业人员的新产品
工具箱
面向 IT 专业人员的新产品
Greg Steen

管理网络同步
ViceVersa Pro
www.tgrmn.com

除非您的基础结构是由一个单元组成的，否则多半需要不断地在所处环境中四处移动文件。从日志文件到应用程序发行再到数据库备份，网络中一刻不停地进行着数据移动。了解需要复制哪些内容来保持不同环境的同步有重要意义。大多数 IT 专业人员不会考虑以手动方式在环境之间移动这些文件，不用说就知道这通常是一种糟糕的方法，因为它可能会导致同步效果不佳。
TGRMN Software 的 ViceVersa Pro 可让您轻松地自动执行和安排这些任务。该应用程序提供了四种不同的复制模式。“基础同步”用于进行源到目标的同步，而“高级同步”模式用于进行具有文件跟踪功能的双向文件和目录同步。另外两种模式是“备份”（对源进行镜像）和“合并”。
尽管“备份”模式将源镜像到目标，并向该目标添加文件或从中删除文件，但您也可以选择文件复制，只向目标复制文件。如果有会意外地删除内容的用户，这种方法可能会证明非常有用。顾名思义，双向同步执行的是双向的同步，并带有冲突和删除跟踪功能。如果在下一次同步之前，源和目标中的同一文件都发生了变化，将会记录该冲突，您可以手动解决此问题。“合并”方法类似于双向同步，但它不会对冲突或删除进行跟踪。
Protect against copy errors and data loss
Protect against copy errors and data loss
ViceVersa Pro 可以利用“备份”模式中集成的文件压缩和文件加密功能协助完成日志传送任务。该应用程序将源文件压缩成标准的 gzip 格式，然后便可对其进行密码保护。
该应用程序集成有一个不错的文件比较程序，但如果您更喜欢使用其他文件比较程序，也可以将 ViceVersa Pro 设置为使用外部文件比较程序。为支持全局环境，ViceVersa 可以处理 Unicode 字符和长度超过 256 个字符的路径。该应用程序还内置有对 Internet 时间同步的支持，因此如果您允许来自使用该应用程序的环境的出站请求，便可以将其设置为指向本地 NTP 单元或任何外部 NTP 服务器。
有时带宽利用可能会成为问题，尤其是当通信在 VPN 或 WAN 上流动时。在这种情况下，可以通过 ViceVersa 减少应用程序在执行每个同步任务时所使用的带宽大小。这也可以帮助确保从作为复制目标或来源的服务器获得生产级响应。或者，如果您愿意，也可以设置最大 CPU 利用率阈值，以同时帮助确保应用程序运行时服务器端的可用性。
ViceVersa Pro 通过使用安全复制算法来防止复制错误和数据丢失。该应用程序还会执行验证循环冗余校验来确保数据的完整性。您可以安排让同步任务按几乎任何间隔运行，包括以实时模式运行（在将文件添加到同步文件夹或从中删除文件时即进行同步）。
ViceVersa Pro 的另一个绝妙功能是，可以通过其 UI 在执行所有操作前进行预览。这有助于确保您所复制的便是您想要复制的内容，而不会发生系统目录中出现几个 G 的日志文件的情况。
总之，如果您在寻找价格低、功能全的同步工具，ViceVersa Pro 可能适合您的需要。
价格：单用户 59.95 美元。

管理您的系统磁盘
Partition Manager 8.0 Professional Edition
www.partition-manager.com/corporate/professional
如果您在寻找一个能够帮助您管理系统磁盘的应用程序，Paragon Software Group 的 Partition Manager 8.0 Professional Edition 绝对值得考虑。
这个基于 Windows® 的应用程序通过其直观的向导驱动式界面提供其核心功能，让您轻松地创建、删除硬盘分区，更改硬盘分区的可见性以及格式化硬盘分区，无论分区格式是 FAT16/32、NTFS、Ext2/3 还是 HPFS。您还可以调整现有分区的大小或将分区从一个磁盘移动到另一个磁盘。
再进一步，您可以在不同类型的文件系统之间进行转换，例如从 FAT16/32 转换为 NTFS，或从 Ext2 转换为 Ext3，以及进行逆向转换。跨磁盘复制分区时会自动将分区大小调整为磁盘的大小，这在扩展您系统上的数据驱动器时会很便利，因为不必手动复制所有文件。8.0 版的一个有用功能是能够进行“热大小调整”，该功能让您不必重新启动 Windows 就可以增加活动 NTFS 分区的大小。
Increase the size of an active NTFS partition — without rebooting
Increase the size of an active NTFS partition — without rebooting
Pro Edition 的另一个有用功能是让您可以在 Windows 2000、Windows XP 和 Windows Server® 2003 客户端上将动态磁盘转换为基本磁盘。Partition Manager 至少支持容量高达 500GB 的磁盘（Paragon 的测试极限，并不是实际的物理极限），还支持通过 FireWire 和 USB 接口连接的驱动器。Partition Manager 将支持 PATA (IDE)、SATA 和 SCSI 硬盘。
Partition Manager 让您可以创建恢复 CD（或 DVD 乃至软盘），以帮助您在发生灾难性磁盘故障后恢复系统。或者，如果您错误地删除了某个分区，而系统仍处于活动状态，则可以通过该界面来取消对该分区的删除操作。您甚至可以更改磁盘的簇大小以帮助对性能做细微调整。该应用程序还提供了“虚拟预执行”视图，让您可以在将磁盘更改实际提交到磁盘之前对其进行评估。
您也可以使用 Partition Manager 8.0 升级您系统上的硬盘驱动器。该应用程序让您可以将一个分区或整个硬盘映像复制到新磁盘上，并调整其大小以适合目标分区。
最后一点，Partition Manager 还包含嵌入式的磁盘碎片整理程序，可在驱动器使用量占总容量 99％ 的情况下对其进行碎片整理，不过还是希望您能够在驱动器达到这种状态前采取措施。
价格：Professional Edition 为 99.95 美元。

为 SQL 编辑器添加自动完成功能
SQL Prompt：面向 SQL 的智能感知工具
www.red-gate.com/products/SQL_Prompt/index.htm

想像所有数据库都以标准化形式、使用逻辑命名约定进行创建和构造是很吸引人的，但遗憾的是，我们都知道这是不可能的。在担任系统管理员或数据库管理员时，我发现自己在创建或运行查询时需要经常回头查看表结构，在担任开发人员时就更是如此。
尽管 SQL Server™ Management Studio for SQL Server 2005 在其界面的树视图部分集成了对象浏览器，已经给工作带来了极大的便利，但我仍觉得自己需要的是 Visual Studio® IDE 中提供的我已经熟悉得不能再熟悉的那个功能：IntelliSense®。幸运的是，Red-Gate Software 提供了 SQL Prompt，它为 Microsoft SQL Server 编辑器提供了自动完成功能。
该应用程序在任务栏中运行，当您开始在编辑器中工作时它会检测到这种情况。首先，SQL Prompt 会向您询问该数据库的连接信息，以便能够搜寻并缓存所有对象数据。这会提高性能并降低开销，但如果您在数据库实例上不断地修改对象，请记得要清除缓存（简单的两次单击操作）才能在提示列表上看到所做的更改。
除了 Visual Studio IDE、Enterprise Manager、Query Analyzer 和 SQL Server Management Studio 外，SQL Prompt 还直接与 UltraEdit 和 EditPlus 2 集成。在 UI 设置的“Options”（选项）选项卡上，您可以指出想要触发候选列表的 SQL 关键字（例如 FROM、JOIN、EXEC 等等）。在 SQL JOINS 上触发 SQL Prompt 甚至还会为您提供 ON 声明（假定您设置了引用或字段命名为相同的名称）。您还可以在此设置代码片段插入的文本快捷键。例如，该应用程序会将“ssf”后跟 Tab 键设置为拼写成“SELECT * FROM”。还可以让 SQL Prompt 在您键入 SQL 关键字时自动将其更改为大写字母。此功能和代码片段肯定能节省时间。
在 UI 设置的“Candidates”（候选）选项卡上，您可以设置是否希望 SQL Prompt 使用完全限定列名、带有所有者/架构信息的前缀、在您名称两边加上方括号，甚至设置想要在候选列表中显示的 SQL 函数的类型。如果您使用 SQL，可能会希望将此工具添加到您的百宝囊中。
在发行 3.0 版本之前 SQL Prompt 将一直免费，撰写本文时该版本为最终测试版本。3.0 版本的定价尚未发布
价格：免费。

计算页面大小
Atomic Web Page Size Calculator
www.info-pack.com/pagesize
最终用户带宽可能正在增加，而新技术也在帮助更快速地将 Web 内容提供给这些最终用户，但页面大小仍然很重要。对高通信量的站点来说，所有这些字节加起来会产生巨大的带宽成本。
Info-pack.com 的 Atomic Page Size Calculator 实用程序提供了一种简单的低成本方法来检查网站页面的页面权重。您所要做的只是键入大小有问题的页面的 URL，该程序会获取该页面，计算所有元素（包括任何从外部引用的图像 — 可能不会显示在您的主域 Web 日志中的项目）的权重。
Keep an eye on bandwidth cost
Keep an eye on bandwidth cost
该计算器支持页面、图像和 JavaScript 文件，所以您应该能够获得有关页面上大多数资源的数据。该应用程序还会在遇到 404“中断”资源时通知您。获取您的 URL 的权重信息后，即可单击“Report”（报告）按钮查看大小明细和预估的以各种连接速度下载该 URL 所需的时间。
如果该应用程序能够同时处理多个 URL 就更好了，但这也许会在未来发布的版本中得以实现。如果您在寻找一种快速的方法来检查您的开发团队页面的页面大小，那么这个应用程序可能符合您的需要。
价格：直销价 19.95 美元。

与工具箱专栏联系
如果您希望在本专栏中介绍某个非常喜欢的工具或实用程序，请发送电子邮件给作者，地址为 tntools@microsoft.com。
Greg Steen是一位技术专家和企业家，他酷爱钻研技术。他总是在寻找能便于 IT 专业人员进行操作和开发的新工具和新方法。


 实用工具特别推荐: SyncToy
实用工具特别推荐
SyncToy
Jay Munro
下载这篇文章的代码: SynctToy (971KB)

一件令人头痛的事 使 IT 专业人士、网络管理员和帮助台工作人员面临着同样的困扰，那就是接到计算机出现故障但又没有备份的用户打来的电话。即使最有头脑的人，要让他们将工作保存到网络而不仅仅是本地也是很困难的。SyncToy 可以帮上忙。
SyncToy 最初设计用来在照相机、计算机和外部驱动器之间同步数字照片，它是适用于几乎所有类型文件的、灵活而可靠的备份方式。SyncToy 运行于 Windows Vista™、Windows® XP 和 Windows Server® 2003 上，适用于本地、外部或 USB 驱动器，以及网络或工作组共享。
SyncToy 使用允许您指定左侧和右侧目录的成对文件夹体系结构。您可以根据需要创建任意多个文件夹对，并且只需按一次按钮就可以同步一个或所有文件夹对。要创建文件夹对，请逐步完成四步的快速向导，在其中键入或粘贴路径，或浏览至左右文件夹的位置。SyncToy 支持 UNC 文件命名方式（例如 \\MyServer\Joe_smith\backup\）以及本地驱动器映射和路径 (E:\doc_backup)。输入路径后，即可选择在比较文件夹时 SyncToy 将执行的五个操作之一。
“Sync”（同步） 沿两个方向复制新的和更新的文件。如果在一个文件夹中重命名或删除某个文件，此操作会复制到另一个文件夹。
“Echo”（回传） 只沿从左侧文件夹到右侧文件夹的方向，复制新的和更新的文件，以及执行删除和重命名操作。
“Subscribe”（订阅） 只沿从右向左的方向复制更新的文件，并且该文件必须已经存在于左侧文件夹中。对左侧所做的更改不会复制到右侧文件夹。
“Contribute”（贡献） 沿从左向右的方向复制新的和更新的文件，但忽略删除操作。
“Combine”（组合） 通过复制在一侧存在而另一侧不存在的文件来使多台机器保持同步。在任一侧删除或重命名的文件不受影响，也不会被复制。
选择此操作后，为文件夹对选择用户易于识别的名称，这样就完成了。SyncToy 目前不允许您在创建文件夹对后编辑路径或更改名称，因此如果路径或名称有误，请选择该文件夹对，单击删除并重新创建。您可以设置各种选项，其中包括将要包括哪些文件和子目录以及是否将被覆盖的文件保存到回收站（默认为保存）。
Synchronizing files with SyncToy
Synchronizing files with SyncToy

主 SyncToy 对话框将在左侧显示文件夹对以及所选文件夹对的路径、选项和操作。“All Folder Pairs”（所有文件夹对）选项用于列出所有文件夹对，您可以对选中的文件夹对执行“Preview”（预览）或“Run All”（运行全部）操作。不难想像，“预览”操作用于模拟实际执行同步的动作，但不做任何更改。如果打算安排无人参与的同步，这绝对值得去做。“预览”操作会显示一份报告，用以列出它已完成的操作。如果一切正常，请单击“Run”（运行）。
尽管帮助文件中提供了针对 Windows Vista 和 Windows XP 计划程序的非常好的预排，但计划功能并不是内置的。非技术用户可能需要一些帮助来对计划功能进行设置，但这样的设置只需一次，因为对文件夹对、操作和选项的所有更改都在 SyncToy 内部完成。
与所有 Power Toy 一样，SyncToy 尽管是依照与常规传送软件相同的标准而编写的，但 Microsoft 本身并不提供支持，而是通过社区论坛和文档来提供支持。因此一切都要靠您自己。帮助文件是为所有使用级别而编写的，对各种功能进行了相当详尽的介绍。其 UI 也足够直观，使您第一次接触就可以基本掌握。
下载 SyncToy 并为您的用户展示如何备份文件。这样就高枕无忧了。

Jay Munro 是 Microsoft Windows 用户协助组的撰稿人，是 Internet Explorer 方面的专家。之前，他曾是 PC Magazine labs 的项目主管。


 SQL 技术问答: 夏令时、服务器内存及更多内容
SQL 技术问答
夏令时、服务器内存及更多内容
编辑：Nancy Michell

夏令时
问：为遵守“2005 年能源政策法”，美国的夏令时 (DST) 制度即将做出更改，我是否应该随之更新 SQL Server™？
答：不用。此时不必为了支持对 DST 的更改而做出特定于 SQL Server 的任何更新。SQL Server 对于与时间相关的数据依赖的是基础操作系统，这意味着如果操作系统所报告的日期和时间正确，则 SQL Server 就将报告和使用同样的值。为符合即将对 DST 做出的更改，您必须按照 support.microsoft.com/kb/928388 所概括的内容来更新 Windows® 副本。在 Windows Vista™（该系统已包含了这些更改）之前的所有 Windows 操作系统（包括那些运行 SQL Server 的系统）上都必须执行此操作才能符合 DST 更改。（澳大利亚也要进行一些更改。请参阅 support.microsoft.com/kb/912475。）

使用 Windows Vista 进行连接
问：我已经安装了 Windows Vista，但是现在无法在系统上连接到 SQL Server 2005。我是一名本地管理员，过去能够顺利地进行此连接。这是什么原因？
答：如果没有安装 Service Pack 2 (SP2)，则在 Windows Vista 和 SQL Server 2005 中出现该行为乃意料中的事。Windows Vista 新增了一个安全模型（用户帐户控制），该模型实际上会在本地管理员组中捕获您的成员身份，并要求您验证需要管理特权的操作。如果单击“允许”按钮，则您的凭据（其中包含您的管理员令牌）会被一路发送到应用程序。如果使用的是 SQL Server Management Studio (SSMS)，则不会显示对话框，因为仅运行该工具并不需要管理权限。问题在于，默认情况下，SQL Server 2005 系统管理员角色包含来自操作系统的本地管理员组，这也是您的帐户在过去访问 SQL Server 时所使用的管理员组。既然 Windows Vista 并不发送该权限，因此您就不会获得访问权限。
应该指出，Windows Vista 不支持在未安装 SP2 的情况下运行 SQL Server 2005，并且该 SP2 具备一个可以为您添加帐户的工具。如果您仍在等待 SP2，修复方法非常简单。必须将您个人的 Windows 用户帐户添加到 SQL Server 中，就您而言，必须将其添加到系统管理员角色中。为此，只需右键单击 SQL Server Management Studio，然后选择“以管理员身份运行”。连接到您的 SQL Server，然后将您的 Windows 帐户添加到系统管理员角色。有关其工作原理的详细信息，请参阅 SQL Server 联机丛书。

关于视图的事务性复制
问：如果我有一个已发布视图并将其更新，我知道这将会复制该事务。但是，如果我更新此视图的基表，还会复制该事务吗？此外，如果我有一个已发布表并创建了一个它的视图，然后我更新了此视图而不是基表，这时会复制该事务吗？
答：假定该基表被配置为发布中的一个项目（也就是说，您也配置了该基表进行复制），则对该基表的任何更新都会被复制。
在您复制视图时，默认情况下要作为视图部分复制的所有内容是视图的架构部分或视图的支持代码，而不是视图的基础数据（除非是索引视图）。因此，即使没有复制，无论您何时更新视图（这种情况下，意味着以视图为目标来运行数据操作语言 (DML) 语句），实际上只是在更新基础表（而不是视图）中的数据。视图只不过是查询语句的逻辑存储区，不附带任何物理存储区（再次重申，除非您使用的是索引视图）。

服务器最大内存
问：我有一台运行 Windows Server® 2003 和 SQL Server 2000 的计算机，RAM 为 5GB。假定我使用 /3GB 开关增加我的用户模式虚拟地址空间，使用 /PAE 开关加载 Windows 内核的物理地址扩展 (PAE) 版本，并将“Address Windowing Extensions (AWE) Enabled”（启用地址窗口化扩展插件 (AWE)）设置为 1（并在内存中启用锁定页面）。在启用 AWE 后，服务器最大内存选项是会仅配置为数据缓存的大小还是所有缓冲区缓存（数据、进程及会话等）的大小？既然只有数据缓存可以使用 AWE 映射的内存，如果我将服务器最大内存配置为 4GB，那么，数据缓存是仅使用 1GB（AWE 所映射的那部分），还是可以使用这额外的 1GB 并继续与标准地址空间中其他所有内存消耗者共同使用或争用内存？
答：服务器最大内存会始终限制整个缓冲池的大小；不过，可以使用 AWE 映射内存的唯一消耗者是数据缓存，没有其他消耗者。
因此，对于您的第一个问题，即使启用了 AWE，服务器最大内存仍会限制整个缓冲池的大小，但非数据缓存消耗者在任何时候也不会使用 AWE 映射的内存。
对于第二个问题，数据缓存将使用 AWE 映射的内存外加缓冲池中 SQL Server 确定适合的其他任何内存；数据缓存不会仅限于使用 AWE 内存，它只不过碰巧是可以使用 AWE 内存的唯一消耗者。如果您不确定 /3GB 开关的作用，请参阅 Raymond Chen 的博客（英文）。

探查和性能
问：我在生产中对 SQL Server 2005 进行了镜像处理。当我在数据库计算机上启动 SQL Server Profiler 并向某文件写入跟踪数据时，我发现性能明显下降。这是为什么？
答：性能下降的原因取决于您运行探查器的位置。如果您在服务器上以交互方式运行它，则探查器 UI 会同时消耗服务器上的内存和 CPU，从而影响性能。
如果您在工作站上以交互方式运行探查器，则会在整个网络内移动事件信息。这会影响吞吐量。如果这是您用于镜像处理的同一网络，则在那里同样会产生影响。此外，如果您在网络共享上存储探查器输出，则会在整个网络内移动数据并对性能产生不良影响。
或许缓解所有这些问题的最佳方法是在运行待探查实例的服务器上以非交互方式运行探查器，然后再将输出传送到本地文件。尽管仍会消耗服务器资源，但此方法通常可以将影响降到最低。这远比（内存中）探查器跟踪有效。文件跟踪可以更有效地使用系统内存；它具有更大的缓冲区，可以将缓冲区中的数据更有效地刷新到磁盘。它同样也不依赖外部进程（如 SQL Server Profiler）。
最后，在探查器仍在进行探查时，跟踪数据被写入到磁盘文件。跟踪文件是共享的，以便其他人可以远程查看实时探查数据。如果您要以交互方式调用跟踪文件，则表示您已手动调用了探查器并要在屏幕上查看输出。可以在没有直观输出的情况下用编程方式创建跟踪文件，这也是您为什么应以非交互方式运行程序的原因。
除了本地目录，您还可以创建一个共享，这样其他人可以访问此共享中的文件，通常这是一种很好的做法。正如先前所提到的，您不希望将跟踪输出发送到远程共享上的文件，尤其是通过用于镜像处理的同一网络管道来访问的远程共享。
您还应该只选择进行调查所需的最小事件集。对于跟踪文件位置，您应选择系统上速度最快的驱动器（最好与 SQL Server 数据库驱动器和事务日志驱动器分开）。如果性能仍是显著降低，则将事件分割为两个或多个跟踪文件，每个跟踪文件都定向于一个不同的硬盘驱动器。即使跟踪文件定向于同一个硬盘驱动器，将事件分割仍然是有好处的，因为每个跟踪文件都有其自己的缓冲集区。有关详细信息，请参阅“SQL Server 联机丛书”中的 sp_trace_create 及其所有相关内容。

群集问题
问：我一直尝试在运行 Windows Server 2003 的群集上安装 SQL Server。可每次进行安装时总会收到一个错误，内容是“安装程序无法在群集节点上执行所需的操作。”而 sqlstp.log 的记录仅仅是：
Script file copied to '\\SERVER01\ADMIN$\SERVER01_MSSQLSERVER.iss' successfully.
Installing remote service (SERVER01)...
An error occured in the service create (SERVER01): 1069...
这是怎么回事？
答：造成这种失败有若干种原因。安装程序在所有选中节点上安装 Windows NT® Service 以远程管理每台计算机上的安装进程。因此，您必须认识到可能会遇到许多阻碍。
首先，您的域帐户用户可能具有拒绝“作为服务登录”权限的组策略。（切记，域策略会覆盖您的本地计算机策略。）请确保您使用的帐户没有这些限制。
其次，您执行安装程序所使用的计算机的已登录帐户在所有节点上都应具有管理员访问权限，原因如下：主安装进程需要对所有计算机的远程注册表访问权限；安装程序会将 cnvsvc.exe 复制到远程计算机的 Windows 目录下；或者安装程序使用标准的 Windows API（这些 API 仅使用已登录帐户的权限）来访问远程计算机。由于这些原因，默认情况下，您在所有节点上均应以管理员身份登录。

灾难恢复计划
问：我在考虑是使用数据库镜像（同步模式）还是日志传送来为我的 SAP 数据库实现灾难恢复 (DR) 策略。我的生产和 DR 站点将使用并非专用于镜像会话的 100Mb 宽带连接。各种镜像会话甚至其他 DR 服务器都将共享此连接。
如果因网络问题而造成无法将日志记录发送到镜像数据库，会进行重试吗？
当镜像会话挂起时，会有保留期吗？还有，除系统视图之外，是否还有任何我可以用来监视镜像流量和日志记录传输的日志记录信息？
答：我们先回答这个问题：什么是数据库镜像的重试逻辑？可以从两方面来考虑此问题：首先，如果出现短暂的网络问题，镜像会话状态是断开的。有一个持续 10 秒的默认的网络超时值，这表示日志记录无法从主体数据库发送到镜像数据库。这种情况下，主体将继续“公开”运行，事务将在客户端上提交。一旦网络问题解决，无需用户干预，镜像会话就会自动重试。它将尝试使用日志记录赶上去；镜像伙伴将首先进行同步，待它们赶上后，就将对状态进行同步。
其次，如果存在重做问题，则镜像会话状态为挂起。重做问题意味着镜像数据库无法在其数据库上提交日志记录。重做问题主要是由于未找到物理文件或磁盘空间不足引起。这种情况下，主体将继续公开运行，因此事务将在客户端上提交。当您在镜像服务器上手动修复重做问题后，镜像会话需要干预才能进行：
ALTER DATABASE <db> SET PARTNER RESUME;
关于保留期，答案是，无论镜像会话是断开还是挂起，日志记录会一直保留到会话还原，并且从会话挂起到会话恢复这段时间的所有记录都会在镜像上得到加固。本质上说，尽管镜像会话被断开或挂起，但主体上的日志不能被截断，因为那样做会破坏日志重做链。这表示主体的日志将没有限制地增大，直到会话还原。因此，事实上对于镜像会话并没有保留限制；唯一的真正约束条件是主体服务器用于存储日志的磁盘空间（因为日志不能被截断）。
最后，不存在您可以用来监视镜像的特定日志文件。为此，SQL Server 2005 提供了一个名为“数据库镜像监视器”的 GUI 工具，该工具允许系统管理员查看和更新状态，并根据几个关键的性能度量配置警告阈值。这样，在镜像不能正常执行时您就可以收到警报。对于数据库镜像的性能要考虑的主要问题是日志记录是否得到及时处理。有关监视数据库镜像的详细信息，可浏览 msdn2.microsoft.com/fr-fr/library/ms365781.aspx 上的“监视镜像状态”一文。

衷心感谢以下 Microsoft IT 专家，感谢他们与我们分享了技术与专业知识： Chad Boyd、Sandu Chirica、Alan Doby、Kaloian Manassiev、Luciano Moreira、Ivan Penkov、Sivagaminathan Rajarethinam、Deborah To、Patrick Woodward、Buck Woody、Stanley Yau 和 Warren Young。


 网络专家: DHCPv6 协议
网络专家
DHCPv6 协议
Joseph Davies

动态主机配置协议 (DHCP) 曾设计用来处理向计算机分配 IP 地址和其他网络信息，以便计算机可以在网络上自动通信。通过使用 IPv6 网络，实际上不需要 DHCP 来配置地址，但是有充分的理由来使用它。DHCP for IPv6 (DHCPv6) 可以（本文还包含指向英文网页的链接。）
向 IPv6 主机提供有状态的地址配置或无状态的配置设置。IPv6 主机可以使用多种方法来配置地址：
无状态地址自动配置 用于对链接本地地址和其他非链接本地地址两者进行配置，方法是与相邻路由器交换路由器请求和路由器公告消息。
有状态地址自动配置 通过使用如 DHCP 的配置协议，用来配置非链接本地地址。
IPv6 主机自动执行无状态地址自动配置，并在相邻路由器发送的路由器公告消息中使用基于以下标记的配置协议（如 DHCPv6）：
托管地址配置标记，也称为 M 标记。设置为 1 时，此标记指示主机使用配置协议来获取有状态地址。
其他有状态配置标记 ，也称为 O 标记。设置为 1 时，此标记指示主机使用配置协议来获取其他配置设置。
结合 M 和 O 标记的值可以产生以下组合：
M 和 O 标记均设置为 0。 此组合对应不具有 DHCPv6 基础结构的网络。主机使用非链接本地地址的路由器公告以及其他方法（如手动配置）来配置其他设置。
M 和 O 标记均设置为 1。 DHCPv6 用于这两种地址（链接本地地址和其他非链接本地地址）和其他配置设置。该组合称为 DHCPv6 有状态，其中 DHCPv6 将有状态地址分配给 IPv6 主机。
M 标记设置为 0，O 标记设置为 1。 DHCPv6 不用于分配地址，仅用来分配其他配置设置。相邻路由器配置为通告非链接本地地址前缀，IPv6 主机从中派生出无状态地址。此组合称为 DHCPv6 无状态：DHCPv6 不为 IPv6 主机分配有状态地址，但分配无状态配置设置。
M 标记设置为 1，O 标记设置为 0。 在此组合中，DHCPv6 用于地址配置，但不用于其他设置。因为 IPv6 主机通常需要使用其他设置（如域名系统 (DNS) 服务器的 IPv6 地址）进行配置，所以这是一种不太可能的组合。
类似于 DHCP for IPv4，DHCPv6 基础结构的组件由下列各项构成：请求配置的 DHCPv6 客户端、提供配置的 DHCPv6 服务器、以及 DHCPv6 中继代理（当客户端位于不具备 DHCPv6 服务器的子网上时，它在客户端和服务器之间传递信息）。

DHCPv6 消息
如同 DHCP for IPv4 一样，DHCPv6 也使用用户数据报协议 (UDP) 消息。DHCPv6 客户端在 UDP 端口 546 上侦听 DHCP 消息。DHCPv6 服务器和中继代理在 UDP 端口 547 上侦听 DHCPv6 消息。DHCPv6 消息的结构比 DHCP for IPv4 的结构简单得多，DHCP for IPv4 在 BOOTP 协议中包含原始数据以支持无盘工作站。图 1 显示了客户端和服务器之间发送的 DHCPv6 消息的结构。
Figure 1 DHCPv6 messages between client and server
Figure 1 DHCPv6 messages between client and server
1 位字节的“消息类型”字段指明 DHCPv6 消息的类型。3 位字节的“事务 ID”字段由客户端确定并用于对 DHCPv6 消息一起交换的消息进行分组。“事务 ID”字段之后的 DHCPv6 选项用于指明客户端和服务器的标识、地址以及其他配置设置。有关定义的 DHCPv6 选项的列表，请参阅“DHCPv6 RFC 资源”边栏中引用的 RFC 3315。
DHCPv6 选项的格式为类型长度值 (TLV) 格式。图 2 显示了 DHCPv6 选项的结构。
2 位字节的“选项代码”字段指明了特定的选项。2 位字节的“选项长度”字段指明了“选项数据”字段的长度，以字节为单位。“选项数据”字段包含选项的数据。
为中继代理和服务器之间交换的各种消息提供了单独的消息结构，以记录其他信息。图 3 显示了各种类型的消息的结构。
Figure 2 Structure of DHCPv6 options
Figure 2 Structure of DHCPv6 options
1 位字节的“跃点计数”字段指明了已接收消息的中继代理数。如果其超过了配置的最大跃点计数，正在接收的中继代理可以放弃该消息。16 位字节的“链接地址”字段包含分配给连接到客户端所在子网的接口的非链接本地地址。在“链接地址”字段中，服务器可以确定从中分配地址的合适的地址范围。16 位字节的“对等方地址”字段包含最初发送消息的客户端或之前中继该消息的中继代理的 IPv6 地址。“对等方地址”字段之外是包括“中继消息”选项的 DHCPv6 选项，“中继消息”选项包含将被中继的消息和其他选项。“中继消息”选项提供了将在客户端和服务器之间进行交换的消息的封装。
没有为 IPv6 定义的广播地址。因此，用于某些 DHCPv4 消息的受限广播地址已替换为使用 FF02::1:2 for DHCPv6 的 All_DHCP_Relay_Agents_and_Servers 地址。例如，尝试发现网络上 DHCPv6 服务器位置的 DHCPv6 客户端从其链接本地地址发送一个“要求”消息给 FF02::1:2。如果主机子网上存在 DHCPv6 服务器，它会接收此“要求”消息并发送合适的应答。更为典型的情况是，主机子网上的 DHCPv6 中继代理接收此“要求”消息并将其转发给 DHCPv6 服务器。
Figure 3 Structure of messages between relay and server
Figure 3 Structure of messages between relay and server

有状态消息交换
要获取 IPv6 地址和配置设置的 DHCPv6 有状态消息交换（接收路由器公告中的 M 和 O 标记均设置为 1 时）通常由以下消息组成：
  • 由客户端发送以定位服务器的“要求”消息。
  • 由服务器发送用以指明其可以提供地址和配置设置的“公告”消息。
  • 由客户端发送以请求特定服务器中的地址和配置设置的“请求”消息。
  • 由包含地址和配置设置的请求服务器发送的“应答”消息。
如果客户端和服务器之间存在中继代理，该中继代理会发送包含来自客户端的封装“要求”和“请求”消息的服务器“中继转发”消息。服务器发送包含为客户端封装的“公告”和“应答”消息的中继代理“中继应答”消息。有关 DHCPv6 消息的完整列表，请参阅图 4。
Figure 4 DHCPv6 消息 (Close Figure 4)

DHCPv6 消息 | 描述 | 等效的 DHCP for IPv4 消息
要求 | 由客户端发送以定位服务器。 | DHCPDiscover
公告 | 由服务器对“要求”消息进行响应时发送以指明可用性。 | DHCPOffer
请求 | 由客户端发送以请求来自特定服务器的地址或配置设置。 | DHCPRequest
确认 | 由客户端发送给所有服务器，以确定对于已连接的链接客户端的配置是否有效。 | DHCPRequest
更新 | 由客户端发送给特定服务器以延长分配地址的生存期并获取更新的配置设置。 | DHCPRequest
重新绑定 | 未接收到对“更新”消息的响应时由客户端发送给任何服务器。 | DHCPRequest
应答 | 对要求、请求、更新、重新绑定、信息请求、确认、发布或拒绝消息进行响应时由服务器发送给特定客户端。 | DHCPAck
发布 | 由客户端发送以指明客户端不再使用分配的地址。 | DHCPRelease
拒绝 | 由客户端发送给特定服务器以指明分配的地址已在使用中。 | DHCPDecline
重新配置 | 由服务器发送给客户端以指明该服务器具有新的或更新的配置设置。客户端随后发送“更新”或“信息请求”消息。 | N/A
信息请求 | 由客户端发送以请求配置设置（但不包括地址）。 | DHCPInform
中继转发 | 由中继代理发送以转发消息给服务器。中继转发包含封装为 DHCPv6 中继消息选项的客户端消息。 | N/A
中继应答 | 由服务器发送以通过中继代理发送消息给客户端。中继应答包含封装为 DHCPv6 中继消息选项的服务器消息。 | N/A

无状态消息交换
要仅获取配置设置的 DHCPv6 无状态消息交换（接收路由器公告中的 M 标记设置为 0，O 标记设置为 1 时）通常由以下消息组成：由 DHCPv6 客户端发送以请求来自服务器的配置设置的“信息请求”消息，以及由包含请求的配置设置的服务器发送的“应答”消息。
对于具有配置为向 IPv6 主机分配无状态地址前缀的路由器的 IPv6 网络，两消息 DHCPv6 交换可用于分配 DNS 服务器、DNS 域名以及其他未包括在路由器公告消息中的配置设置。

Windows 中的 DHCPv6 支持
Windows Vista™ 以及下一版本的 Windows Server®（代码名为“Longhorn”）包括 DHCPv6 客户端。依据接收的路由器公告消息中的 M 和 O 标记的值，DHCPv6 客户端尝试基于 DHCPv6 的配置。因此，要使用 DHCPv6，必须将 DHCPv6 服务器和中继代理配置为维护每个 IPv6 子网，然后将您的 IPv6 路由器配置为设置这两个标记为合适的值。如果某个给定子网存在多个公告路由器，应将它们配置为公告相同的无状态地址前缀和 M 与 O 标记的值。运行 Windows® XP 或 Windows Server 2003 的 IPv6 主机不包括 DHCPv6 客户端，因此会忽略接收的路由器公告中的 M 和 O 标记的值。
通过使用“netsh interface ipv6 set interface InterfaceName managedaddress=enabled”命令，可以将正在运行 Windows Vista 或 Windows Server“Longhorn”的 IPv6 路由器配置为在路由器公告中设置 M 标记为 1。类似地，通过使用“netsh interface ipv6 set interface InterfaceName otherstateful=enabled”命令可以在路由器公告中设置 O 标记为 1。
Beta 2 版本的 Windows Server“Longhorn”通过 DHCP 服务器服务来支持 DHCPv6 中继代理和 DHCPv6 无状态配置。可以将用于 DHCPv6 选项的 DHCP 服务器服务配置为分发到之前所述的两消息 DHCPv6 消息交换中的所有 DHCPv6 客户端。Microsoft 已计划在 Beta 3 版本的 Windows Server“Longhorn”中支持 DHCPv6 有状态操作（包括在 DHCP 管理单元中创建 IPv6 地址范围的功能）。
DHCPv6 RFC 资源
与 DHCPv6 相关的所选 RFC：
  • RFC 3315 Dynamic Host Configuration Protocol for IPv6 (DHCPv6)
  • RFC 2462 IPv6 Stateless Address Autoconfiguration
  • RFC 3633 IPv6 Prefix Options for DHCP version 6
  • RFC 3646 DNS Configuration Options for DHCPv6
  • RFC 3736 Stateless DHCP Service for IPv6

Joseph Davies 是 Microsoft 的一位技术撰稿人，从 1992 年起一直在培训和编写 Windows 网络主题。他曾为 Microsoft Press 编写了五本著作，并且是每月的在线 TechNet 网络专家专栏的作者。


 Windows Powershell: 变量的力量
Windows Powershell
变量的力量
Don Jones

如果使用基于 Windows 的脚本语言（如 VBScript 或 KiXtart），您会习惯于认为变量只不过是一种数据存储机制。Windows PowerShell 也具有变量，但这些变量要比早期脚本语言中的变量强大得多。Windows
PowerShell 变量实际上映射到 Microsoft® .NET Framework 中的基础类。在 Framework 中，变量是对象，这就意味着变量可以存储数据，也可以通过多种方法进行处理。实际上，正是由于 Windows PowerShell™ 中变量的强大功能，使得 Windows PowerShell 脚本语言不包含任何内部数据处理函数。它根本不需要这些函数，因为变量本身就已经提供了这种功能。

声明变量
尽管 Windows PowerShell 中的 New-Variable cmdlet 确实允许您声明变量并为其赋予初始值，但您并不一定要使用 cmdlet。作为一种替代方式，您只需通过为变量赋值即可即时地创建新的变量：
$var = "Hello"

在 Windows PowerShell 中，变量名始终以一个美元符号 ($) 开头，并可以混合使用字母、数字、符号、甚至空格（但如果使用空格，则需要将变量括在花括号中，例如 ${My Variable} = "Hello"）。此示例创建了一个名为 $var 的新变量，并为其赋予初始值 "Hello"。因为本例中的变量值是字符串，所以 Windows PowerShell 会使用 String 数据类型来存储该值。在 .NET Framework 中，这些字符串变量对应于 System.String 类。System.String 类或许拥有任何变量类型的大部分内置功能。假设我要查看 $var 值的全小写版本，则可以执行以下命令：
PS C:\> $var.ToLower()
hello
PS C:\>
ToLower 方法内置于 System.String 类中，可以使字符串的值以全小写形式来表示。但它并不更改变量 $var 的实际内容。要查看 System.String 类所有功能的完整列表，请像下面这样通过管道将一个字符串变量传递给 Get-Member cmdlet：
$var | get-member
图 1 显示了输出结果，其中包含许多用于处理字符串的方法。在 Windows PowerShell 中，VBScript 字符串处理函数所提供的几乎所有功能都改由字符串变量方法来提供。
Figure 1 A look at the System.String class output
Figure 1 A look at the System.String class output

在管理上下文中，字符串变量的许多功能比语言（如 VBScript）中的字符串函数要有用得多。假设您编写了一个用于从文件中读入通用命名约定 (UNC) 路径并对其进行操作的脚本。在尝试使用路径之前，您可能要验证所读入的每个路径是否为 UNC 路径。可通过 StartsWith 方法来确认字符串值是否以 UNC 所必需的反斜线字符开头：
PS C:\> $path = "\\Server\Share"
PS C:\> $path.StartsWith("\\")
True
PS C:\>
由于 StartsWith 返回 True 或 False 值，因此可将其用在逻辑构造中：
if ($path.StartsWith("\\")) {
 # code goes here
}
Windows PowerShell 甚至还提供了一种自动完成变量方法的形式，从而减少了必要的键入工作量。如果 $var 包含一个字符串，您可以键入
$var.
然后按 Tab，这样会弹出 $var 变量的第一个方法名。再按一次 Tab 键可移到下一个方法，而按 Shift+Tab 会调出前一个方法。您可以通过这种方式遍历所有可用的方法，直到找到您想要的那一个。

变量混淆
到目前为止，我的示例一直在让 Windows PowerShell 来确定变量的数据类型。将字符串赋给变量实质上会强制变量成为 System.String 类。而另一方面，将数字赋给变量通常会使变量成为 Integer（或者更确切地说，是成为 Int32，其可以存储特定范围的值）。例如，考虑以下脚本：
PS C:\> $int = 5
PS C:\> $int | get-member

  TypeName: System.Int32
这一被截断的输出说明 Windows PowerShell 将 $int 视为 Int32，Int32 有其自己的方法和属性集。而实际上，其方法和属性要比 String 类型的方法和属性少得多。
将 $int 作为 Int32 是因为其值没有括在引号中而且仅由数字组成。如果将其值括在引号中，则它会被视为 System.String。
让 Windows PowerShell 来决定要使用的数据类型并不是总能达到您所希望的效果。假设您要从文件读取变量值，而又希望这些值始终被视为字符串。但是，其中一些值可能只包含数字，这就增加了 Windows PowerShell 将其视为 Int32 或其他数字类型的可能性。这样可能会使脚本出现问题。如果 Windows PowerShell 不将值识别为字符串，则 System.String 类的所有方法都无法使用（而您的脚本可能要依赖于这些无法使用的方法中的一个）。
要解决这一问题，可以在第一次使用变量时声明其类型，从而强制 Windows PowerShell 将变量按该特定类型处理。示例如下：
[string]$var = 5
$var 通常应为 Int32 类型，但在此示例中我强制 Windows PowerShell 将 $var 作为 String，从而确保了我可以使用与 System.String 类相关的所有方法。变量类型声明也提供了一种便捷的代码自我文档化方式，因为现在哪种类型的数据要进入 $var 中是显而易见的。实际上，我已经养成了为所有变量声明特定类型的习惯，而完全不管是不是在 Windows PowerShell 中。这使得我脚本的行为更加可以预测，在许多情况下为我节省了大量的调试时间。
正如您所料，尽管强制声明变量未必不好，但也确实会产生影响。以下面的代码为例，代码中未声明变量的类型：
PS C:\> $me = 5
PS C:\> $me = "Don"
$me 变量最初为 Int32 类型，但在添加了值“Don”后 Windows PowerShell 将该变量改为 String 类型。如果变量尚未被明确地设置为某个特定的类型，Windows PowerShell 可以根据需要更改变量的类型。
在本例中，我使用 [int] 类型名将 $me 强制为 Int32 类型：
PS C:\> [int]$me = 5
PS C:\> $me = "Don"
Cannot convert value "Don" to type
"System.Int32". Error: "Input string
was not in a correct format."
At line:1 char:4
+ $me <<<< = "Don"
然后，当我尝试为其赋予字符串值时，会出现一条错误消息。因为我已明确地将 $me 强制为 Int32 类型，所以 Windows PowerShell 要将字符串“Don”转换为整型值。然而这样做是不可能的，而且将 $me 的类型更改为 String 也是不可能的。

许多类型
Windows PowerShell 中提供了很多的类型。实际上，您可以使用任何的 .NET Framework 类型（可用的类型有数百个）。但 Windows PowerShell 为常见的数据类型定义了许多快捷方式。图 2 只是部分列表，显示了 10 个最常用类型的快捷方式。有关完整列表，请参考 Windows PowerShell 文档。
Figure 2 常见类型快捷方式 (Close Figure 2)

快捷方式 | 数据类型
[datetime] | 日期或时间
[string] | 字符串
[char] | 单个字符
[double] | 双精度浮点数
[single] | 单精度浮点数
[int] | 32 位整数
[wmi] | Windows Management Instrumentation (WMI) 实例或集合
[adsi] | Active Directory 服务对象
[wmiclass] | WMI 类
[Boolean] | True 或 False 值
您还可以使用完整的 .NET Framework 类名来声明变量的类型，如下所示：
[System.Int32]$int = 5
您可以通过这一技术来使用存在于 .NET Framework 中但在 Windows PowerShell 中没有特定快捷方式的类型。

扩展类型
Windows PowerShell 最爽的地方或许就是它能够扩展这些变量类型的功能。在 Windows PowerShell 安装文件夹（通常在 %systemroot\system32\windowspowershell\v1.0 中，但在 64 位系统上您会发现此路径略有不同）中，您会找到名为 types.ps1xml 的文件。可以在记事本或 XML 编辑器（如 PrimalScript）中编辑此文件。默认情况下，尽管其中列出了许多其他类型，但并未列出 System.String 类。但我可以向 System.String 类型添加新的功能，只需将其扩展添加到 types.ps1xml 文件中即可实现。
进行更改后，需要关闭 Windows PowerShell，然后再重新打开。我还需要确保本地脚本执行策略允许执行未签名的脚本，因为我未对 types.ps1xml 进行签名：
Set-executionpolicy remotesigned
现在，只要重新启动 Windows PowerShell，我就可以使用新功能了，如下所示：
PS C:\> [string]$comp = "localhost"
PS C:\> $comp.canping
True
PS C:\>
正如您所看到的那样，我已经将 CanPing 属性添加到 System.String 类。这样会返回 True 或 False 值，来指示本地计算机能否 ping 字符串中所含的地址。在图 3 中，您会注意到 WMI 查询使用了一个特殊的变量 $this。$this 变量代表字符串中所含的当前值，也是字符串变量内容传递给 WMI 查询的方式。
Figure 3 扩展 System.String 类型 (Close Figure 3)
<Type>
  <Name>System.String</Name>
  <Members>
    <ScriptProperty>
      <Name>CanPing</Name>
      <GetScriptBlock>
      $wmi = get-wmiobject -query "SELECT *
FROM Win32_PingStatus WHERE Address = '$this'"
      if ($wmi.StatusCode -eq 0) {
        $true
      } else {
        $false
      }
      </GetScriptBlock>
    </ScriptProperty>
  </Members>
</Type>

让变量工作
Windows PowerShell 提供了灵活的、功能众多的变量类型，也提供了同样灵活的类型功能扩展系统。这些功能使您可以很容易地为您的脚本注入大量的功能。实际上，变量可以成为复杂脚本中的主要构建基块，在许多情况下提供着通常在更为复杂的函数中才能找到的高级功能。
在线了解更多信息
请加入我于每月第二个星期二举办的系列网络广播，在网络广播中我们将继续探究 Windows PowerShell 所提供的丰富脚本功能。请访问 microsoft.com/events/series/donjonesscripting.mspx 立即注册。

2007 年 2 月 20 日 Windows PowerShell：脚本速成课程
请加入此网络广播以了解 Windows PowerShell 中的变量、语言构造、脚本和函数。在这一小时的速成课程中，我们将演示 Windows PowerShell 中简单但很有效的脚本语言如何加快 Windows® 的管理自动化进程。我们还将探究一些能够尽可能简化 Windows PowerShell 脚本的第三方工具。

2007 年 3 月 20 日 Windows PowerShell：函数、筛选器和效率
学习在 Windows PowerShell 函数和筛选器中创建有效的、模块化的代码。我们将探讨 Windows PowerShell 中能够让您完全封装函数和筛选器的强大作用域规则，并将说明如何轻松地在项目之间重复使用这些函数和筛选器。我们还会以实例说明如何将自己自定义的函数添加到 Windows PowerShell 中的全局作用域，从而可以轻松地组建一个随时可用的有用工具函数库。

2007 年 4 月 17 日 Windows PowerShell 和 Windows Management Instrumentation
Windows PowerShell 可以访问 Windows Management Instrumentation（也称 WMI）的所有功能。我们不仅将介绍如何使用 Windows PowerShell 来访问 WMI，而且还会探讨如何通过 Windows PowerShell 管道传递 WMI 对象和集合。我们将探究如何在 Windows PowerShell 脚本中利用 WMI 属性和方法，并举例说明 WMI 中的基础安全和配置功能。

2007 年 5 月 22 日 Windows PowerShell：从 VBScript 转换
想要将您的脚本甚至是您的技能从 VBScript 转换到 Windows PowerShell 吗？请加入本网络广播进行学习。我们将探究 Windows PowerShell 如何包含 VBScript 的所有主要构造和功能，使您能够轻松地将 VBScript 技能转换到这一新的管理环境。我们将举例说明如何将 VBScript 中的工具转换为 Windows PowerShell 中原本的脚本语言。我们还将分析 Windows PowerShell 中的唯一结构，并为您展示如何开始使用 Windows PowerShell 脚本语言来提高工作的效力与效率。

2007 年 6 月 19 日 Windows PowerShell：深层次的扩展
在此网络广播中，我们将探究 Windows PowerShell 如何利用强大而又灵活的 .NET Framework 来处理数据，从而为您提供数百个内置函数来管理字符串、日期和其他数据类型。但您知道吗，您可以使用 Windows PowerShell 脚本扩展这些函数。我们会为您展示如何使字符串变量不仅包含计算机名称而且还可以告诉您计算机是否已经启动并运行。您将学习如何创建无需使用外部函数即可自动设置数据格式的日期和时间变量。请加入此会话，来了解如何在短短几分钟内将所有类型的新功能构建到 Windows PowerShell 中，从而使 Windows 管理更加快速和简单。

Don Jones是 SAPIEN Technologies 的项目和服务主管，也是《Windows PowerShell：TFM》(SAPIEN Press) 的合著者。请通过 www.ScriptingAnswers.com 与他联系。


 您好，脚本专家！: 回答您有关 OU 的问题......只挣 5 美分
您好，脚本专家！
回答您有关 OU 的问题......只挣 5 美分
Microsoft 脚本专家
下载这篇文章的代码: HeyScriptingGuy2007_03.exe (151KB)

问：如何才能显示一个允许我从 Active Directory 选择 OU 的对话框？

您要知道， 如果我们脚本专家在每次被问到这个问题时都能得到 5 分镍币，我们应该得到 -- 嗯，实际上，也只不过是 37.15 美元，不是吗？当然，但如果对于那些想要向我们咨询这个问题但只是从未抽出时间来真正提问的每个人收取 5 分镍币，我们会得到 -- 嗯，金额是多少并不重要。毕竟，我们做脚本专家并不是为了赚钱。我们做脚本专家是为了体验帮助别人更简单快捷地执行系统管理任务所获得的满足感。
注意。 是的，从表面上看，我们做脚本专家的确是为了赚钱。但之后经理告诉我们，“要知道，你们大家是可以做得很出色并赚到很多钱的，但仅是在你们乐于真正努力地工作并只创造出高质量作品的情况下。”也就是在那时，我们确定了金钱并不代表一切。
没有关系。意义在于，许多人都想知道如何显示允许他们选择 OU、然后再使用脚本连接到该 OU（通过扩展）的对话框。最近发来的一封电子邮件中提到，“您曾经介绍过如何显示允许我们选择文件夹的对话框，也介绍过如何显示允许我们选择文件的对话框。那为什么从未介绍过如何显示允许从 Active Directory® 选择 OU 的对话框呢？”
嗯，首先，我们的确曾经介绍过如何显示允许选择文件夹的对话框，就在此处您就可以找到相关示例。我们也向所有人介绍过如何显示允许他们选择文件的对话框，如果您不相信的话，可以查看 microsoft.com/technet/scriptcenter/resources/qanda/jan05/hey0128.mspx。这两篇文章都收到了好评，而且我们知道用户在自己的脚本中采用了这些方法。那么，为什么我们未向大家介绍如何显示允许从 Active Directory 选择 OU 的对话框呢？是 Microsoft 有某种密约还是有其他什么原因？
当然不是。（哎呀，稍等一下：这正是一项密约中所涉及的人要说的话，不是吗？）事实是，实际上有一个很好的理由可以解释为什么我们还没有介绍如何显示允许从 Active Directory 选择 OU 的对话框：因为根本不存在这样的对话框。就像棒球运动中常说的一句话，您无法击中看不到的东西。我们在编写脚本时也会常说，您无法显示不存在的东西。
因此，我们想这正是本月专栏要讨论的内容。下次见。
我就是这样不走运！据 TechNet 杂志的编辑所说，本月专栏的主题并不是这个。“那么，如果像那样的对话框不存在怎么办？”他们问到。“你们是脚本专家，看在上帝的份上。难道你们就不能创建一个用来处理此任务的对话框吗？我们原以为你们这些人在脚本编写方面可以做到任何事情。”
不用说，TechNet 杂志的编辑听到的消息显然不正确。在脚本编写方面可以做到任何事情？并不完全是这样。事实是，在涉及脚本编写时，我们脚本专家知道如何只做一件事情。
不过幸运的是，这一件事请就是创建允许您从 Active Directory 选择 OU 的对话框。看一看图 1。
这个脚本有点长是不是？但您知道人们常说的那句话：有失才有得。同样，您不可能不付出代价就构建一个自定义对话框（我们在这个专栏进行到一半时感觉有点饿，不得不停下来吃点快餐）。既然我们已经吃饱了，就让我们看看是否能够阐明其整个工作原理。
我们对这个脚本所要完成的任务是使用 Internet Explorer® 对象模型构建一个伪对话框。我们要创建一个 Internet Explorer 实例，从 Active Directory 获取一个 OU 集合，然后在 Internet Explorer 窗口的某列表框中显示这些 OU。我们会一直等到用户选择一个 OU，然后获取所选项的 ADsPath，退出该“对话框”，然后从此处继续往下操作。
注意。这会成为所创建过的最奇妙、最别致的对话框吗？如果“最奇妙和最别致”指的是甚至连“确定”和“取消”按钮都没有的骨架式风格，那么是的。但站在我们的立场，这里提到的概念只是为您提供基本框架。如果您想要美化或修改该对话框，就要靠您自己来完成了。
（朋友，如果我们在每次说过“对不起，这要靠您自己来完成”后就收取 5 分镍币，我们确实会变得非常富有。请将您捐赠的脚本代码发送至 TechNet 杂志转“脚本专家”。）
为使分配给我们的空间能够容纳下对我们方法的说明，我们不得不只略谈几个方面。例如，对以下代码块我们要说的只是，它将创建一个 350x400 像素的 Internet Explorer 实例，这样做会隐藏地址栏和状态栏之类的元素：
Set objExplorer = CreateObject( _
    "InternetExplorer.Application")

objExplorer.Navigate "about:blank"
objExplorer.ToolBar = 0
objExplorer.StatusBar = 0
objExplorer.Width= 350
objExplorer.Height = 400
objExplorer.Left = 400
objExplorer.Top = 400
objExplorer.Visible = 1
当然，这只是要为我们提供一个空白的 Internet Explorer 窗口。为了获取我们域中 OU 的列表，我们需要搜索 Active Directory。我们今天无法阐明有关 Active Directory 搜索的细节；有关这方面的信息，请看一下我们在脚本中心发布的由两部分组成的系列文章 (microsoft.com/technet/scriptcenter/resources/tales/sg0405.mspx)。我们能做的只是告诉您，这是我们用来检索域 fabrikam.com 中每个 OU 的 Name 和 ADsPath 的代码：
objCommand.CommandText = _
    "SELECT Name, ADsPath FROM 'LDAP://DC=fabrikam,DC=com' WHERE objectCategory=
    'organizationalUnit' ORDER BY Name"
哦，我们还按名称的字母顺序对 OU 进行了排序，只是为了让您更方便一些。这就是 ORDER BY Name 子句的作用。（没问题；毕竟，我们的使命是提供服务。）
当我们调用 Execute 方法时，将取回一个由在 fabrikam.com 中找到的所有 OU 组成的记录集。这意味着我们的下一步是在一个列表框中显示其中的每个 OU。
那么，我们如何去做呢？首先，我们需要在内存中构造整个列表框。对于初学者来说，这意味着使用这个代码行将 <SELECT> 标记存储在名为 strHTML 的变量中：
strHTML = "<select size = '20' name='OUList' style='width:300px'>"
我们要用这个代码行做什么呢？我们只需构造一个标准 HTML 列表框（使用 <SELECT> 标记执行的某个操作）。在此过程中，我们为该列表框赋予的 Name 是 OUList，并将其配置为一次显示 20 个项目，且宽度为 300 像素 (300px)。如果您非常了解 HTML，就会对上面的代码感到很熟悉。
接下来，我们需要将一些项目（即，一些 OU）添加到列表框中。令人感到十分凑巧的是，我们的记录集恰好由域中每个 OU 的相关信息组成。正因如此，我们可以设置一个 Do Until 循环来遍历整个数据集，获取信息，然后将每个 OU 添加到列表框中。在该循环内部，我们使用下列代码（再次重申，用于将项目添加到列表框的标准 HTML）添加每个 OU，从而对相应项目进行配置以使 OU Name 显示在列表中，如果在列表框中单击该 OU，会将 ADsPath 传递给脚本：
strHTML = strHTML & "<option value= " & _
    Chr(34) & objRecordSet.Fields
    ("AdsPath").Value & Chr(34)
strHTML = strHTML & ">" & objRecordSet.
    Fields("Name").Value
为什么要将 ADsPath 传递给脚本？道理很简单。假定我们改为传递 OU Name。例如，如果 Name 是 Finance，则就很难绑定到该 OU；毕竟“Finance”本身并不是一个有效的 ADSI 绑定字符串。但如果我们取回的是 ADsPath (LDAP://ou=Finance,dc=fabrikam,dc=com)，那么绑定只不过是小菜一碟。这是因为 ADsPath 正是我们需要查找并连接到 Active Directory 中的对象的属性。
顺便说一下，我们将在列表框中显示 Name，因为我们假定您的 OU 名称是唯一的。有时 OU 名称可能不是唯一的；例如，OU 的 North America\Research 和 Europe\Research 就使用相同的名称 (Research)。如果您的 OU 具有重复的名称，您最好在列表框中改用另一个属性（比方说，ADsPath 或 distinguishedName）。这是一个您必须自己做决定的事情。（好耶！又得到 5 分镍币！）
顺便说一下，要注意到我们仍在内存中构造列表框：添加到该列表框中的各项将被添加到变量 strHTML。下面是这个计划的全部内容：我们将列表框的整个代码保存到变量 strHTML 中，然后使用该变量的值将列表框实际添加到我们的 Internet Explorer 的实例中。
在添加了所有 OU 之后，我们使用此代码行表示列表框的末尾：
strHTML = strHTML & "</select>"
此时，strHTML 中包含了创建列表框（最好是创建一个其中每一项都代表 Active Directory 中一个 OU 的列表框）所需的所有 HTML 标记。这意味着我们现在可以将 strHTML 值赋予我们 Internet Explorer 文档的 InnerHTML 属性：
objExplorer.Document.Body.InnerHTML = strHTML
反过来，这也为我们提供了一个显示域中所有 OU 的列表框。
请等一下，别走；我们还没有完全结束呢。例如，我们还需要一些允许我们将脚本一直暂停到用户从对话框中选择了 OU 为止的代码；如果没有这些代码，则无论用户是否进行了选择，列表框都会显示出来而脚本将继续运行。呀！为防止这种情况发生，我们使用以下代码块：
Do While objExplorer.Document.Body.All.OUList.Value = ""
    Wscript.Sleep 300
Loop
我们此时要做的就是反复检查我们列表框（您可能想起来了，我们将其命名为 OUList，有位脚本专家给他女儿起了一个相同的名字）的 Value。如果 Value 是空字符串（表示未选择任何项目），我们只需将脚本暂停 300 毫秒，然后循环一次，接着再次进行检查。此过程将持续到：a) 永远；b) 直到用户在列表框中选择某个项目；或者 c) 直到用户关闭 Internet Explorer 窗口。
让我们假定用户确实在列表框中选择了一个 OU。在这种情况下，我们获取 Value（您可能记得，就是 OU 的 ADsPath），并将其赋予名为 strTargetOU 的变量：
strTargetOU = objExplorer.Document.Body.All.OUList.Value
然后，我们按如下所示使用 Quit 方法退出 Internet Explorer 实例：
objExplorer.Quit
至此，我们差不多就结束了。我们首先检查并确定 strTargetOU 是否为空字符串：
If strTargetOU = "" Then
    Wscript.Quit
End If
如果是，则表示用户未选择 OU 就关闭了 Internet Explorer；接着，我们使用 Wscript.Quit 方法来终止脚本。（我们做出的假设是，如果用户未挑选 OU，则他或她不是真正对运行该脚本感兴趣。）但如果 strTargetOU 不等于空字符串，则我们将回显该变量的值：
Wscript.Echo strTargetOU
当然，在真正的脚本中，您可能会继续前进并绑定到 Active Directory 中的该 OU，而不仅仅是回显 ADsPath。但您知道他们在说什么 -- 或者至少知道我们在说什么：这要靠您自己了。
正如我们所提到过的，也正如您在图 2 中所看到的，它不是所创建过的最奇妙的对话框，但它很起作用，而且与必须键入一个 AdsPath（假定您连 AdsPath 都知道）相比，这是一种更简便的方式。另外，它也使您有机会根据需要对代码（及对话框）进行自定义。
Figure 2 A simple script dialog
Figure 2 A simple script dialog

现在，要是我们对曾经编写的有关显示允许从 Active Directory 选择 OU 的对话框的每个专栏都能得到 5 分镍币该多好啊！这毕竟会为我们带来 5 美分的收入，比 TechNet 杂志通常支付给我们的要多一些。
提醒您，我们并不是在抱怨。

Microsoft 脚本专家是为 Microsoft 效劳的，也就是说受雇于 Microsoft。在没有棒球等各种娱乐活动时，他们偶尔也会经营一下 TechNet 脚本中心。查找所需的信息，网址为：www.scriptingguys.com。


 Exchange 技术问答: 事务日志、Cmdlet、远程管理及更多内容
Exchange 技术问答
事务日志、Cmdlet、远程管理及更多内容
KC Lemson and Nino Bilic

问：你们为什么在 Microsoft® Exchange Server 2007 中将事务日志大小降到了 1MB？
答：噢，在 Exchange 2007 中，我们增加了这项称为日志传送的酷炫技术，这项技术也是被称为本地连续复制 (LCR) 和群集连续复制 (CCR) 这两个新功能的强大后盾。当在存储组 (SG) 级启用此技术时，会将 SG 事务日志从运行的生产 SG 复制到副本 SG。然后，副本 SG 上的事务日志会重播到数据库中，以使数据库保持最新。这么解释此过程显然简单了些，但您可以获得一个大概印象。
难题在于，除非事务日志在生产 SG 上关闭，否则无法将其发送给副本 SG。换句话说，直到 e00 日志（第一个存储组中的当前日志）填满、关闭和重命名之后才能将其发送。另外，还有一些设置可以控制在此过程中可以有多少个后台日志，因为在可用性与性能之间要有一个权衡抉择。（在 Exchange 管理外壳中运行 Set-MailboxServer cmdlet，即可以使用这些设置。帮助文件提供了关于所需确切参数的详细信息。）
长话短说，为了将超出存储组两个副本间同步范围的数据量降到最低，我们降低了事务日志大小。假设在生产数据库硬盘驱动器崩溃并且您需要采取行动从副本启动 SG 时，这就开始起作用了。事务日志越小，就越有可能减少丢失的数据。
这并不表示我们可以保证您只丢失 1MB 数据。举例来说，既然这些好像是事务日志，因此有可能丢失的 1MB 日志文件中会含有用于提交 5MB 数据的 COMMIT 事务。在这种情况下，副本中就会缺失这 5MB 的数据 - 即使您可能已收到含有数据其本身的五个日志，但含有少量数据和那个必需的 COMMIT 事务的最后一个日志也是必不可少的。
最终还是由您自己根据业务需求来衡量是否可以接受这个风险。与花费数小时才能完成的还原工作相比，只需几分钟时间来恢复少量丢失的数据也许是完全可接受的。我们将来肯定还会讨论更多关于这个主题的内容！

问：既然事务日志变短了，我们会不会很快就将事务日志“用完”？
答：在说到 Exchange 时，“用完”指的是事务日志名称。在 Exchange Server 2003 中，事务日志按如下方式命名：
E nn fffff.log
其中 nn 是在各 SG 间不断改变的前缀。由于 nn 在 SG 的生命周期中保持不变，因此，可为存储组创建的以唯一方式命名的事务日志的数量基本上为“fffff”值，大概是一百万个日志（记录自我：将小手指放在嘴角开始狂笑）。我们有一些客户在繁忙的邮件服务器上已经达到了此限制。
从 Exchange Server 2000 开始，我们提供了一个在接近此限制时会在应用程序日志中显示一个事件的修补程序。（有关此修补程序的详细信息，请参阅 support.microsoft.com/kb/896001。）
OK，我们有点扯远了，那么 Exchange 2007 中到底有哪些变化？我们刚刚说过，事务日志大小从 5MB（早期版本 Exchange 中的大小）降到了 1MB (Exchange 2007 中的大小)。这是不是意味着只需五分之一的数据就能用完唯一事务日志名称？
答案是否定的。在 Exchange 2007 中，事务日志名称的格式如下：
E nn ffffffff.log
请注意，现在我们可使用“ffffffff”个日志，而不只是早期版本 Exchange 中“fffff”个日志。由于内部的“可扩展存储引擎”(ESE) 数学运算，Exchange 2007 无法创建所有“ffffffff”个日志。它会改为创建“7fffffec”个日志，这个数目仍然相当大。（如果您不相信我们，可将此数字从十六进制转换为十进制！）
在现实生活中，这意味着什么？在现实生活中，这意味着使用 Exchange 2007 可以创建的日志数大约比早期版本 Exchange 多出 2048 倍。现在，让我们将五分之一大小的日志作为因素计入在内：
2048 / 5 = 409.6
正如您所看见的，新的日志命名模式所处理的数据要比旧模式处理的多出 409 倍！而且请切记，是每个 SG 而不是每个服务器，因为每个存储组都将有一个不同的日志文件前缀（如 e00、e01 等等）。我们是否提过在 Enterprise 版本的 Exchange 2007 中，可为每个服务器创建 50 个 SG？我们认为，不太可能有人随时很快就用完事务日志！

问：如何远程管理 Exchange 2007 服务器？
答：有几种方法可选择用来远程管理 Exchange 2007 服务器。一开始，可以将 Exchange 2007 管理工具安装到独立于 Exchange 服务器的另外一台计算机上，只要该计算机运行的是 32 位或 64 位版本的 Windows Server® 2003 Service Pack 1 (SP1)（或更高版本）、Windows Server 2003 R2 或 Windows XP SP2（或更高版本）。请注意，在生产环境中不支持将任何服务器角色（客户端访问、邮箱、边缘等等）安装在 32 位硬件上，但对于将管理工具安装在 32 位计算机上这种情况是支持的。还请注意，从 Exchange Server 2007 的最初发行版开始，Windows Vista™ 上不支持管理工具。
在一些组织中，IT 部门只创建一台服务器来用作管理服务器，从而将各种产品的所有必需管理工具都安装在这台服务器上。然后，需要使用这些工具的管理员会通过终端服务器访问管理服务器，以便他们可以执行远程管理。在其他设置中，用户将管理工具安装在他们自己的台式机上。
您可以从控制台或外壳对组织中的各服务器执行操作。在控制台中，服务器是可见的，因此您可以对其中任何一台服务器执行远程操作。在外壳中，如果您想要将操作范围限定于单个服务器，则许多任务都支持像 -server 之类的筛选标志。但对于必须本地运行的命令（如 Get-NetworkConnectionInfo 命令）有几个例外情况。

问：在上期专栏中，您告诉了我们对于 Exchange 2007 服务器建议使用多大的 RAM。那么页面文件大小又如何呢？
答：为防止 Exchange 2007 服务器上可能出现的虚拟内存不足错误，我们建议使用以下指导原则来确定系统页面文件大小。
如果服务器的 RAM 大小少于 8GB，则页面文件大小的最小值应为 RAM 大小加上 10MB。因此，如果服务器的 RAM 大小为 4GB，则页面文件大小的最小值应为 4GB 加上 10MB。但建议的页面文件大小为 RAM 大小乘以 1.5。因此，如果服务器的 RAM 大小为 4GB，则页面文件大小应为 6GB。
此外，对于 RAM 大小高于 8GB 的服务器，则以相同方法计算页面文件大小的最小值，即 RAM 大小加上 10MB。但对于建议页面文件大小的确定方式是不同的。基本上，在 RAM 大小高于 8GB 的服务器上，只有在因使用较小的页面文件而收到虚拟内存不足错误时才应将页面文件大小增加到 RAM 大小的 1.5 倍。
请注意，在本例中提到的错误与虚拟内存碎片（Exchange 2000 和 Exchange 2003 服务器存在的问题）无关。这些错误是系统所记录的实际弹出错误，指出要增加页面文件大小或者页面文件过小。

问：我想学习如何使用 Exchange 管理外壳，但我以前从未使用过命令行来管理系统。我应该从哪里开始着手？
答：首先，我们从一些基础知识开始着手。Exchange 管理外壳中的每个 cmdlet 都包含一个动词和一个名词。Exchange 管理外壳仅使用少数几个动词：“Get”用于检索对象属性；“Set”用于更改对象属性；“New”用于创建新对象，等等。Exchange 管理外壳使用的名词包含许多所有 Exchange 管理员都已熟悉的概念（例如，Mailbox、Contact、Queue 和 OWAVirtualDirectory）。其中也有一些 Exchange Server 2007 新增的概念（例如，TransportRule、StorageGroupCopy 和 UMAutoAttendant）。因此在实践中，基本 cmdlet 看起来有些类似于 New-Mailbox（用于创建邮箱）或 Get-Mailbox（用于检索组织中的邮箱列表）。
但大多数 cmdlet 都需要更多关于您设法要完成的确切任务的具体细节。例如，如果您要更改某邮箱的某属性，您就需要指定是哪个邮箱和哪个属性。这通过参数来完成。如果 New-Sandwich 是一个 cmdlet，则参数可能是 -NumberOfTomatoSlices 4、-Bread Wheat 以及用于更详细指定您想要什么质地的三明治的其他多种类似选项 (-FlufferNutter $true-ahhhh yum)。
一旦您了解了动词-名词拆分，您就可以使用 Exchange 管理控制台 (EMC) 向导中呈现的命令行来熟悉哪些 cmdlet 执行哪些操作。在 EMC 中，从头至尾运行该向导以创建新用户。在向导结尾，完成操作后，您会看到向导在引擎罩下运行的一个命令副本（请参见图 1）。按 Ctrl+C 将复制该对话框的内容，这样，您就可以将 cmdlet 粘贴到记事本中，然后整理出您自己的简单批处理文件。
Figure 1 Command-line exposure for creating a new user
Figure 1 Command-line exposure for creating a new user
如果您想练习使用 cmdlet，但不想造成任何不利影响，则有一个很棒的窍门，就是将 -WhatIf 放在命令结尾。如果照此方法执行，在不实际更改系统的情况下，系统就将告诉您，通过运行该命令它会执行什么操作。
一旦您熟悉了 *-Mailbox 之类的基本 cmdlet，就可能想要尝试其他一些命令。要查找更多 cmdlet，请使用 Get-ExCommand cmdlet。此命令将显示所有 Exchange 管理 cmdlet 的列表。（在运行 Exchange 管理外壳期间，您必须执行此任务。只是打开 Microsoft 命令外壳或 Windows PowerShell™ 不会让您执行这个或其他任何 Exchange 管理任务。）该列表很庞大，因此请使用通配符匹配查找您需要的命令。例如，Get-ExCommand *database* 将返回名称中含有“database”的所有 Exchange cmdlet 的列表，例如 Dismount-Database、Move-DatabasePath 和 New-MailboxDatabase。
如果您认为自己知道要查找的名词的一部分，则可以遍历各选项。例如，您可以键入 Get-Mailbox，然后按 Tab 键遍历任务名称的名词中带有 mailbox 的所有 cmdlet。您甚至可以将通配符匹配与 tab 键完成功能结合使用。例如，您可以键入 Get-*Mailbox*，然后按 tab 键遍历可能的选项。
另一个简单窍门是使用提示。每个 cmdlet 都有一组在默认情况下必需的参数，即执行操作所必需的基本参数。“Get”cmdlet（如 Get-Mailbox）不需要任何参数。默认情况下，Get-Mailbox 将返回整个组织中所有邮箱的列表。（默认情况下，此列表的大小最大值为 1000。若要更改此最大值，可以键入 Get-Mailbox -ResultSize 2000 之类的其他值。）用于创建或修改对象的 Cmdlet（如 Set 或 New）会提示您输入有关要创建或修改哪个对象的必需信息。例如，如果运行 New-Mailbox，外壳会逐一提示您输入密码、数据库、名称等等。
若要查找有关给定 cmdlet 的可能参数的详细信息，请使用帮助系统：Help Move-DatabasePath。（您也可以仅键入 Move-DatabasePath、Move-DatabasePath -? 等等 - 我们尽量将基本部分包含进去。）该帮助系统将提供 cmdlet 的说明和参数列表及每个参数的用途。使用 Get-Help cmdlet 也可以访问帮助系统。（只键入 Get-Help 是不是感觉很好？难道您不想让您认识的一些人也能在现实世界中运行该 cmdlet 吗？）
发现 cmdlet 可能参数的另一个方法是对参数名称使用 tab 键完成功能。键入 Get-Mailbox -S，然后按 Tab，您将会遍历以 S 开头的所有可能参数。
当然，还有内容详尽的帮助系统。如果您在脱机状态下未找到要查找的内容，不要忘了查阅 go.microsoft.com/fwlink/?linkid=79421 上定期更新的帮助内容和 go.microsoft.com/fwlink/?linkid=64647 上的快速参考指南。
所有这些信息将让您掌握 Exchange 管理控制台的入门知识，但这些信息只是对命令行强大功能和灵活性的粗略介绍。我们会在将来的专栏中涉及更多内容。

问：在 Exchange Server 2003 ESM（Exchange 系统管理器）中，我们很容易就可以看到所有用户的邮箱大小。但在 Exchange 2007 中，好像没有这项功能了。在 Exchange 2007 中，有其他方法可以做到这点吗？
答：在新的 Exchange 2007 控制台中，您可以在邮箱本身的常规属性页上看到邮箱大小。如果想要查看多个用户的邮箱大小，则必须使用 Exchange 管理外壳。
如果运行 Get-MailboxStatistics，则可以获得整个服务器的数据（而不只是特定数据库的数据）。默认情况下，不显示邮箱大小，您必须运行带有 Format-List 的 cmdlet 才能获得邮箱大小：
get-mailboxstatistics | format-list
如果选择想要查看的内容并将其输送到表格，可能会更好。在本例中，我们获得邮箱的显示名称、项目总大小、已删除项目总大小以及邮箱所在的数据库：
Get-MailboxStatistics | Select-Object
displayname,totalitemsize,
totaldeleteditemsize,databasename
| Format-Table
根据您要查找的信息，可以使用不同的输出格式。在本例中，Format-Table 就比 Format-List 更好用

KC Lemson是 Exchange Server 团队的首席程序经理。她的 ATM PIN 号是 0000 — 不要将它都用在一个地方。
Nino Bilic是一位技术主管，他利用空闲时间开设了技术咨询信箱，以 1,000 美元的薪水为微软做技术顾问。


 安全观察: 部署 EFS：第 2 部分
安全观察
部署 EFS：第 2 部分
John Morello

可以认为任何加密文件系统 (EFS) 部署实质上都可以分为两个部分：侧重于证书管理和恢复代理的后端设计部分，以及面向用户的 EFS 部署部分。上个月，我谈完了后端部分，并且论述了数据和密钥恢复选项
以及用于为客户端置备证书的选项。在此，我将着重谈谈最终用户将会看到的那些部署方面，例如对 Windows® 资源管理器的增强、选择哪些文件系统位置进行加密以及如何使用组策略管理加密选项。

确定 EFS 是否已在使用
一旦您已决定部署 EFS，第一步就要确定 EFS 当前在贵组织内的使用状态。回想一下，EFS 可在独立模式下使用，在该模式下，最终用户独自负责创建和备份其加密密钥。在贵组织中可能已有高级用户以这种方式使用过 EFS，所以，为了确保平稳过渡到管理更好的部署，确定这些用户具体是哪些人非常重要。
任何给定用户在计算机上首次运用 EFS 时，Windows 均会在 HKEY_CURRENT_USER 中创建以下注册表条目：
HKCU\Software\Microsoft\Windows NT\CurrentVersion\EFS\CurrentKeys\CertificateHash
可使用 Microsoft® Systems Management Server (SMS)、Active Directory 登录脚本或其他工具检查整个企业范围的计算机上是否存在此条目。如果对于某位用户存在此注册表值，则表明该用户曾在某时使用过 EFS。然而，此值不一定表示某人当前正在使用 EFS 或是已对该人的任何数据进行了加密。因此，在发现某计算机存在此注册表项时，应更仔细地对其进行检查以确定是否有任何文件当前已加密。
使用 cipher.exe 并传递 /U 和 /N 开关将指示密码器报告计算机上文件和目录的加密状态。例如，如果要确定系统上是否有任何数据已加密，可运行以下命令：
cipher /U /N
可将注册表检查和密码器报告合并到一个脚本中，以检查是否存在此注册表值，如果存在，便在计算机上生成当前加密文件的报告。一旦生成这些报告，您就会知道哪些用户有哪些文件进行了加密，并且随时可以将其迁移到集中管理的设计。
为了在部署期间控制组策略的应用程序，可在域中创建两个安全组，将正在使用 EFS 的计算机与未在使用 EFS 的计算机分开，这样做很有用。例如，“SG-ComputersUsingEFS”组将包含已在使用 EFS 的那些计算机的计算机帐户，而“SG-ComputersNotUsingEFS”组将包含其他所有当前未在使用 EFS 的计算机。

通过组策略控制 EFS 的使用
知道了何人正在使用 EFS、何人未在使用 EFS 后，客户端部署的下一步就是禁止当前未在使用 EFS 的任何人独立使用 EFS。这一步在部署过程中很重要，因为与迁移正在使用自签名证书的用户相比，首次正确地启用和配置 EFS 要更加容易。只有在确定了哪些用户已在使用 EFS 之后，才能执行这一步，因为通过组策略禁用 EFS 后，用户将不能再访问任何当前加密的文件。可在组策略对象编辑器中的“计算机配置\Windows 设置\安全设置\公钥策略\加密文件系统”下找到此策略。
如前所述，只应在当前未在使用 EFS 的计算机上启用此值，以免干扰已在使用 EFS 的任何用户的工作。因此，在我给出的示例中，禁用了 EFS 的组策略对象 (GPO) 上的访问控制列表 (ACL) 只允许“SG-ComputersNotUsingEFS”组应用组策略。

加密还是不加密
确定了哪些系统当前正在使用 EFS 并在您管理的部署之前在其他所有计算机都禁用了 EFS 后，下一步就要确定将要在该被管部署中对哪些项进行加密。根据客户端系统的管理方式，此过程可能很简单，也可能相当复杂。
在考虑要对哪些文件进行加密时，首先要考虑用户在他们的系统上是否为管理员。如果用户是本地管理员，您其实只能指导并鼓励他们加密敏感数据，但不能以管理的方式真正对其进行控制。其原因很简单：本地管理员可在文件系统的任意位置创建文件和目录。因此，尽管您可以对诸如 My Documents 之类的公共位置进行加密，但如果用户在另一位置创建新的目录并将敏感信息保存在该处，便很难确保此数据也受到保护。当最终用户不是本地管理员时，除了其他所有的安全受益之外，还会使得 EFS 更加易于管理和安全。
关于 EFS 需要记住的下一条关键信息是，它是按用户加密的。换句话说，由给定用户加密的任何项只能由同一用户进行解密，而其他用户都做不到，包括 SYSTEM 帐户（当然也有例外，那就是数据恢复代理，亦称 DRA）。这意味着，如果使用某位用户的密钥对某个可执行文件或 DLL 进行了加密，而系统上的另一位主体尝试对其访问，则该主体会收到拒绝访问错误。因此，如果文件或目录包含系统上多个主体可能需要访问的可执行代码，一般不应对其进行加密。Windows 将阻止对 %SystemRoot% 中的文件或具有 SYSTEM 属性的文件进行加密，但软件供应商为 SYSTEM 帐户可能运行的服务将二进制文件安装到 %ProgramFiles% 中的情况也并不罕见。例如，便携机供应商在安装作为服务运行的电源和设备管理软件时，就常常会这样做。
考虑到这两个事实，那么实现 EFS 加密目标的最佳实践是什么呢？答案要回到正常情况下是如何对用户的系统进行配置的。在具有标准化操作系统映像的管理完善的环境中，您很可能会基于映像中预定的数据存储位置自动为用户执行加密过程。例如，如果用户只能在 %userprofile%\My Documents 中保存数据，您可能仅需加密该目录。（注意：如果使用文件夹重定向，则在加密 My Documents 时要小心。如果将 My Documents 重定向到某个服务器，该服务器需为“已为委派信任”并且具有访问用户配置文件的权限。在这种情况下，只对“脱机文件”缓存加密通常更加容易，我们以后将在本专栏对此进行讨论。）
在管理较差的环境中（用户可能在文件系统上写入许多位置），不妨采取这样的做法，先对少数几个公共存储位置自动进行加密，然后教会用户如何自行加密其他位置。无论环境如何，都务必要在目录级而不是文件级进行加密。这样做可确保该目录中创建的所有文件（包括临时文件）始终都会被加密。

推荐的加密位置
一般而言，通常应对这些位置进行加密：%userprofile%\My Documents、%userprofile%\Application Data\Microsoft\Outlook（假定您使用 Microsoft Office Outlook® 作为消息传送客户端）和 %userprofile%\Desktop。对 My Documents 加密会保护 Windows XP 中保存文件的默认位置。保护 Outlook 目录可确保本地缓存的敏感电子邮件也会被加密；这对于在 Outlook 2003 默认的“缓存模式”下运行的用户尤为重要。最后，桌面常被用作某种临时目录，用户可能会在此放置他们当前处理的文档或频繁讲述的演示文稿。当然，在贵组织中，您可能已修改了其中某些目录的默认位置，因此，无论在贵组织中通常都使用什么位置，在您做出 EFS 加密选择时都应将其反映出来。
除了这些一般性的最佳实践外，还应确定是否有应用程序可能在备选位置保存敏感信息。例如，一些应用程序会在 %ProgramFiles%\AppName 中而不是在用户的配置文件中保存用户信息。确定您的客户端系统上是否存在这样的应用程序很重要，这样才能正确地保护它们所利用的一切数据。在最好的情况下，可将应用程序配置成在另一个用户配置文件特定的路径中保存更改（例如 My Documents），此时，无需再进一步做任何 EFS 更改。在最坏的情况下，如果应用程序要求将数据存储在它的 Program Files 目录中，可使用 EFS 仅对 Program Files 的那个特定子目录进行加密。
最后，如果您打算加密临时目录（%TMP% 和 %TEMP%），请小心从事。许多应用程序安装程序都使用此目录来展开安装软件包的内容，然后对展开的文件执行文件移动操作，以便将它们放置到 %ProgramFiles% 中的适当路径中。由于在同一个卷上移动的文件会保留其原始属性，所以在将该文件放置到 %ProgramFiles% 中之后，它将继续被加密。如果尝试使用此文件的用户不是运行安装程序的那位用户，则会拒绝该用户对其进行访问。通常，此类问题不会以明确的应用程序错误消息表明自身的根本原因。因此，我一般建议仅在管理完善的环境下（最终用户不自行安装应用程序）才对 %TMP% 加密。

保护脱机文件
“脱机文件”是 Windows 2000 及更高版本中的一个功能，借此用户可以为文件服务器上存储的数据建立本地副本。然后，利用“脱机文件”，用户可在与服务器断开连接的情况下处理此数据，并在下次连接后通过同步操作将更改返回给服务器。然而，“脱机文件”并不只是一个包含服务器中文件副本的简单目录。相反，它是一个在系统范围内共享并在 SYSTEM 帐户的环境中运行的数据库。在 Windows XP 中，可使用 EFS 保护此数据库。根据前面对 EFS 按用户加密的讨论，想一想这意味着什么。特定于用户的 EFS 怎么能对多个安全主体使用的数据库进行加密呢？
当运行 Windows XP 的用户在 Windows 资源管理器中选择加密脱机文件的选项时，所利用的加密例程与该用户可能启动的其他加密操作所使用的例程是不同的。不是利用用户个人的 EFS 密钥对来执行该过程（这会阻止 SYSTEM 访问这些文件），而是由 SYSTEM 帐户自己生成用于 EFS 的密钥对，并使用该密钥对加密客户端缓存。这就产生了一个非常严重的安全隐患，如果有人随后得到代码以 SYSTEM 身份执行，他便可以解密缓存中的数据。如果某台便携机被盗，攻击者轻而易举就可以重置管理员密码并以管理员身份登录。一旦以管理员身份运行，强制代码以 SYSTEM 身份运行然后访问缓存，也同样简单。（请注意，这种行为在 Windows Vista™ 中有所改变。Windows Vista 现在使用单个用户的密钥来加密用户特定的脱机文件。）
为防止此类攻击，可将便携机配置成脱机存储安全帐户管理 (SAM) 数据库的加密密钥，既可在引导时将其作为密码输入（模式 2），亦可在引导过程中使用软盘（模式 3）。两种方法可能都不令人满意，因为它们需要在引导过程中与计算机进行不可自动化的交互，从而造成系统自动化管理方面的潜在问题。然而，如果要在具有脱机文件的计算机上使用 EFS，使用这些选项之一保护缓存中的数据至关重要。通常，我建议使用引导时密码（请参见图 1）。原因是，如果某位用户的便携机包被盗，那么极有可能软盘已插在便携机中，即便不是如此，软盘也几乎肯定会和便携机一起放在包中。
Figure 1 Automate EFS operations
Figure 1 Automate EFS operations

启用 EFS
确定了要在您的客户端上对什么进行加密后，下一步就要执行实际的加密操作了。可通过登录脚本或其他进程完成此过程，它们将在客户端上执行某个脚本以调用 cipher.exe 来执行加密操作。如在前面的示例中所讨论的，如果在您的环境中存在独立的 EFS 用户，建议首先迁移这些用户。
如在本专栏 2007 年 2 月的分期连载中所讨论的 (microsoft.com/technet/technetmag/issues/2007/02/SecurityWatch)，要迁移这些独立用户，第一步就要为客户端系统置备被管证书。在所有系统都为这些被管证书登记注册后，就应使用 /U 开关运行密码器，以新的加密和恢复代理密钥更新所有现有文件。此时，SG-ComputersUsingEFS 组中的所有系统都会以管理的方式利用 EFS，包括集中颁发（并且可能已存档）的密钥以及集中管理的恢复代理。
要为当前没有使用 EFS 的用户部署 EFS，必须首先删除阻止使用 EFS 的组策略。在完成该操作并且用户为被管证书登记注册后，便可运行登录脚本来加密先前讨论的密钥路径。以下是一个简化的脚本示例：
cipher /e /s /a "%userprofile%\My Documents"

cipher /e /s /a "%userprofile%\Application Data\Microsoft\Outlook"

cipher /e /s /a "%userprofile%\Desktop"
在所需目录上启用 EFS 后，应考虑设置另外两个选项来提高 EFS 部署的安全性，这两个选项都与从内存中页式调度到磁盘的数据有关。请注意，对于其中每一种方案，恶意方可能恢复的数据量都仅限于在合法使用期间加载到内存中或页式调度到磁盘上的数据量。换句话说，如果用户的机器上有受 EFS 保护的敏感数据且用户未曾查看过这些数据（因而未加载到内存中），则此数据便不会受到这些攻击病媒的威胁。
页面文件被 Windows 用作虚拟内存（在其他操作系统中常称作交换空间），其中包含内存中以明文写入硬盘的原始数据片段。这会将受 EFS 保护的系统上的敏感数据置于危险境地，因为如果攻击者能够恢复页面文件，他们就有可能利用辨析工具从此文件中提取合用的数据。由于在 Windows XP 中无法对页面文件本身进行加密（请注意，在 Windows Vista 中可以对其加密），所以最佳选择就是强制 Windows 在关闭时将其清除。这可以通过组策略来实现，如图 2 所示。
Figure 2 Setting group policy
Figure 2 Setting group policy
此设置的缺点是大大增加了关机所需的时间，尤其是对于具有大量内存的机器。（默认情况下，页面文件的大小与计算机中的物理内存量直接相关。）这是因为 Windows 必须在关闭前在磁盘上物理写入每一页，方可清除页面文件。
当电源管理模式设置为启用休眠时，Windows 中的休眠文件包含机器物理内存的转储。休眠发生时，Windows 会将物理内存的完整内容写入磁盘中的休眠文件，从而当用户准备要继续工作时，可将计算机还原到与休眠前完全相同的状态。与页面文件一样，在 Windows XP 中也无法对休眠文件进行加密。然而，与页面文件不同的是，不能直接通过 GPO 禁用休眠。而是应该使用脚本调用带有 /HIBERNATE 开关的 powercfg.exe 来禁用（或重新启用）休眠。
完成初始加密和配置任务后，在部署中应考虑擦除磁盘上的闲散空间。此过程可能非常耗时且具有破坏性，但在高安全性环境中具有安全上的好处。闲散空间是一个磁盘区域，其中虽未存放系统上当前活动的数据，但由于硬盘的操作方式，其中可能包含先前使用过的文件中的敏感信息片段。换句话说，由于文件删除操作实际上并不覆写文件在磁盘上占据的所有扇区（仅会从文件表中删除相应的指针），所以被删除的数据可能继续存在于磁盘上。在这种情况下，便可能通过可以读取此闲散空间并从中重新组装文件的辨析工具来恢复该数据。要安全地擦除这些残留数据，请运行带有 /W 开关的密码器。这样可以强制密码器反复覆写磁盘上标记为可用的所有扇区。只要将来针对敏感数据的所有文件操作都在受 EFS 保护的目录中进行，便无需定期运行此命令。

为 EFS 增强 Windows 资源管理器
默认情况下，文件和目录的加密和解密选项兴许埋藏在 Windows 资源管理器的高级属性菜单中。可以选择通过将“加密”和“解密”置于 Windows 资源管理器的上下文菜单（右键单击时出现）来增强对 EFS 的最终用户体验。此外，还可对 Windows 资源管理器进行配置，使其以与其他文件不同的颜色（绿色）显示受 EFS 保护的文件和目录。这可能对用户很有帮助，他们不必深入到高级属性菜单中即可知道某个文件或目录是否已加密。最后，您可能想要添加一个选项来定义哪些用户有权访问该文件或目录。要启用这些选项，请通过调用 reg.exe 的脚本在注册表中进行以下更改：

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced]

"EncryptionContextMenu"=dword:00000001

"ShowCompColor"=dword:00000001

[HKEY_CLASSES_ROOT\*\Shell\Encrypt To User...\Command]
@="rundll32 efsadu.dll,AddUserToObject %1"

Windows Vista 中的 EFS
在 Windows Vista 中对 EFS 进行了许多重要的增强，这使其变得更加易于部署和管理，同时也增加了安全性。这些增强中最主要的就是完全支持在智能卡上存储 EFS 密钥。对于已在利用智能卡登录的组织来说，此功能尤其有价值，因为也能使用同一个卡来存储用户的 EFS 密钥。对智能卡的这一支持也扩展到了恢复代理证书，这样就可以将敏感的 DRA 证书也存储在卡上。
Windows Vista 还支持对以前在 Windows XP 中不可能或不易做到的项进行加密。Windows Vista 可以加密页面文件，无需再设置“清理虚拟内存页面文件”选项。在 Windows Vista 中，对脱机文件的整个设计进行了重新整改。除大大提高了性能和稳定性（以及界面通常对用户更加友好）以外，客户端缓存现在是按用户进行的，也就是说可在不使用 SYSKEY 模式 2 或 3 的情况下安全地加密缓存。这两项改进消除了现今基于 Windows XP 的环境所存在的主要部署障碍。
最后，在 Windows Vista 中，管理员不必利用单独的脚本即可通过组策略直接配置 Documents 文件夹的加密。图 3 说明了 Windows Vista 中通过 GPO 提供的新 EFS 属性。
Figure 3 EFS properties in Windows Vista
Figure 3 EFS properties in Windows Vista

总结
EFS 为 Windows 管理员提供了一个用于保护移动数据的高度安全的选项。EFS 可伸缩且易于管理，并且提供了灵活的数据恢复机制。在 Windows Vista 中进一步对 EFS 进行了改进，使其功能得到了增强，不仅更加易于管理和部署，而且支持基于智能卡的密钥存储。对于要保护数据的组织来说，即使对于计算机真正丢失的情况，EFS 也提供了一个强大的解决方案，它已成为您现有 Windows 平台的一个组成部分。
何处去找更多信息
有关本专栏所讨论主题的详细信息，请参阅以下资源：
  • 2007 年 2 月的安全观察专栏
  • Encrypting File System in Windows XP and Windows Server 2003（英文）
  • 如何使用 SysKey 实用程序来保护 Windows 安全帐户管理器数据库

John Morello 以最优异的学业成绩毕业于 LSU，在 Microsoft 的六年工作期间担任过许多不同的职务。作为高级顾问，他曾经为财富 100 强企业以及联邦民用和国防客户设计安全性解决方案。目前，他是 Windows Server 组的项目经理，负责安全性和远程访问技术方面的工作。


 桌面文件: PsTools 入门
桌面文件
PsTools 入门
Wes Miller
下载这篇文章的代码: MillerTheDesktopFiles2007_03.exe (150KB)

到去年为止，我一直在 Winternals Software 公司工作。Winternals 公司于 2006 年 7 月被 Microsoft 收购，公司专门经销商业软件。但其创始人 Mark Russinovich 和 Bryce Cogswell 还拥有一家名为 Sysinternals 的独立公司，该公司以免费软件形式发行了许多工具。在这些免费软件应用程序中，有些
甚至已经发展成为 Winternals Administrator's Pak 中的组件。Mark 和 Bryce 还超出典型免费软件许可条款的范围将这些工具的许可授予那些寻求使用这些工具的公司。
在 Sysinternals 工具库中（没错，它是一整套的工具），PsTools 套件是我所喜爱的工具之一，该工具位于 microsoft.com/technet/sysinternals/utilities/pstools.mspx。Mark 将这些工具以 UNIX 进程列表实用程序 ps 命名。正如这些工具的下载网站上所述，有些内置的 Windows® 命令行实用程序与这些工具有着相同的功能。但除了重要的独特功能外，两者的主要区别在于 PsTools 套件既可本地使用，又可远程使用（当然，前提是必须正确配置远程系统）。更为可喜的是，其远程运行过程几乎与本地运行过程一样简单。
PsTools 套件包含几个单独的应用程序，如图 1 所列。如同其他 Sysinternals 实用程序一样，没有安装程序，而只有应用程序。只需将它们放在所选路径的某个文件夹中就万事大吉了！
Figure 1 PsTools 实用程序 (Close Figure 1)

工具 | 说明
PsExec | 用于在任何用户环境中执行进程。
PsFile | 显示系统上远程打开的文件。
PsGetSid | 显示计算机或用户的 Windows 安全标识符 (SID)。
PsInfo | 列出有关 Windows 系统的信息。
PsKill | 按照名称或进程 ID 终止进程。
PsList | 列出有关运行中进程的详细信息。
PsLoggedOn | 显示所有本地登录的用户以及所有远程资源共享用户。
PsLogList | 转储 Windows 事件日志记录。
PsPasswd | 更改用户帐户的密码。
PsService | 查看和控制 Windows 服务。
PsShutdown | 关闭、重新启动、休眠或挂起计算机。
PsSuspend | 挂起运行中的进程。
现在，您可能注意到，这一功能的某些环节（特别是 PsLogList）可以通过 Windows Management Instrumentation (WMI) 和一些复杂的脚本来实现。但 PsTools 套件的特点在于，每个实用程序都旨在完成某一项特定的任务，简单而又容易。
我将逐个工具加以介绍，并通过简单的示例来说明工具的用途及用法。但首先，我要简要介绍一下这些工具的工作原理以及要使这些工具能够工作必须满足哪些前提条件。

工具的工作原理
这些工具通常都以相同的方式工作。在主机系统上启动所需的 PsTool。尽管许多工具都可以在本地运行，但您会发现这些工具在针对一个远程系统运行时或在同时针对多个系统运行时最能发挥其效用。（PsExec 是我所想到的一个特例，在下面的示例中您就会看到。）实用程序使用您所提供的命令行参数，来将其自身复制到远程系统的管理共享，这一管理共享与远程系统上的 %SystemRoot%（Windows 目录）相同。
一旦复制到远程目录中，该应用程序即会自行启动并将安装 Windows 服务。该 Windows 服务会使用启动时您所提供的凭据来执行您所请求的任务。如果该特定工具需要客户端系统上的某个最终用户 UI，该服务会在用户上下文中启动附加的二进制文件。（请记住，服务不能抛出直接交互的 UI。）该服务再将任何必要的信息返回给启动它的控制台。最后，该服务会自行卸载。
您可以看到，这些工具是强大而独立的。尽管它们会涉及多个二进制文件和多个进程，但作为用户的您却不必担忧，您只需运行所需的工具，它就会完成任务。
要使用这些工具，必须满足以下前提条件：具有 Windows NT®、Windows 2000、Windows XP 或 Windows Server® 2003（支持 Windows 的 x86 或 x64 版本，但不支持 Itanium 版本）；在运行这些工具的远程系统上启用默认的管理网络共享 (admin$)；在远程系统上打开用于文件和打印机共享的端口（无论使用的是 Windows 防火墙、Internet 连接防火墙，还是其他防火墙产品）。

PsExec
PsExec 是这些工具中我个人所偏爱的工具，个中原因或许并不是您想象的那样。在两年中，我有大半的时间一直以“非管理员用户”的身份运行我的系统，也就是说，我以“用户”身份而非“管理员”身份运行。而我有一个用来以本地管理员身份运行应用程序的简短脚本，称为 run.vbs（以用户身份也可以同样轻松地运行这些应用程序）。图 2 显示了该脚本的内容（也可在 TechNet 杂志网站的 technetmagazine.com/code07.aspx 获得该脚本）。该脚本假定 PsTools 在 Path 系统变量中。
Figure 2 Run.vbs (Close Figure 2)
SET WshShell = WScript.CreateObject("WScript.Shell")
SET WshSysEnv = WshShell.Environment("SYSTEM")
SET FSO = CreateObject("Scripting.FileSystemObject")
IF WScript.Arguments.Count <> 0 Then
    FOR EACH arg IN WScript.Arguments
        iArgCount = iArgCount + 1
        strCmdArg = (arg)
        strCmdArray = Split(strCmdArg, " ", 2, 1)
        IF iArgCount = 1 THEN
        strExe = strCmdArray(0)
        ELSEIF iArgCount = 2 THEN
        strRun = strCmdArray(0)
        ELSE
        strParams = strParams&" "&strCmdArray(0)
        END IF
    NEXT
END IF
'/t:0A && title ***** Admin *****
        strExt = LCase(Right(strExe, 3))

IF strExt <> "exe" AND strExt <> "bat" AND strExt <> "cmd" THEN
WshShell.Run "psexec.exe -d -i -e -u COMPUTERNAME\USER -p PASSWORD  cmd /c start "&strExe&"
    "&strRun&" "&strParams, 0, FALSE
ELSE
WshShell.Run "psexec.exe -d -i -e -u COMPUTERNAME\USER -p PASSWORD "&strExe&" "&strRun&"
    "&strParams, 0, FALSE
END IF

    SET WshShell = NOTHING
    SET WshSysEnv = NOTHING
    SET FSO = NOTHING
当我们在 Winternals 开发 Protection Manager 产品时，Mark 添加了一个功能：即允许受限用户提供更高权限的凭据来在本地启动 PsExec。这个简单的脚本就是将 PsExec 用于这一功能的一个示例。当然，PsExec 更常用来远程运行，例如用来运行一个不提供自身远程、脚本或自动处理框架的实用程序。PsExec 的两个主要功能包括将所要执行的二进制文件复制到远程系统以及规定处理器关联。PsExec 的另一个有用功能是可以通过提供脚本文件来针对许多计算机同时进行脚本处理。
以下是简化的 PsExec 用法：
PsExec \\computer -u username -p password command
通过图 3 所示的开关可以扩展这一用法。
Figure 3 PsExec 开关 (Close Figure 3)

开关 | 说明
-a | 指定处理器关联。
-c | 指示是否应将应用程序复制到远程系统。
-d | 指定在退出前不等待进程结束（不等待成功或失败的消息）。
-e | 使用指定帐户的配置文件加载。
-f | 即使远程系统上存在该应用程序也进行复制。确保运行当前（或特定）版本。
-i | 在远程系统上以交互方式运行应用程序（如果想要某位最终用户看到该应用程序的任何用户界面，则必须采用这种模式）。
-l | 以受限用户身份运行应用程序。删除管理员组令牌并且将进程权限降至用户组成员的权限级别。便于以最低权限运行高风险、面向 Internet 的应用程序。
-n | 指定在与远程计算机连接失败之前等待的超时时间。
-priority | 按指定的优先级运行进程。即，指定 Windows 应为给定任务所设的进程优先级（-low、-belownormal、-abovenormal、-high 或 –realtime）。
-s | 使用系统帐户运行进程（便于运行 Regedit 来访问权限内的注册表项）。
-v | 只在所复制应用程序为更高版本时才复制该应用程序。
-x | 在 Winlogon 安全桌面上显示 UI（仅适用于本地系统）。
@file | 针对文本文件中所指定的所有计算机运行该命令。

PsFile
与 PsExec 相比，PsFile 是更加有所侧重的工具。具体来说，它用于检查谁在远程系统上打开了哪些文件。如果要在重新引导服务器前跟踪哪些文件处于打开状态，此工具会很有用。如果需要查明哪位用户锁定了该文件（或许是为防止进行编辑或删除），可以手动进行通知，或者通过 PsFile 将文件关闭。PsFile 的用法如下：
PsFile \\computer -u username -p password
此命令只会返回远程系统上所打开的文件句柄的列表。除了标准用法之外，还可以向语句中添加 Id 参数，以指定要查找相关信息或者要关闭的文件的名称。其中还可以包括 -c 开关，表示将关闭 Id 参数所标识的文件。

PsGetSid
PsGetSid 也是一个很有侧重的工具。它会告诉您计算机、用户或组的安全标识符 (SID)。如果要确保域中的系统不存在重复的 SID 或者只想在某个系统上验证所有用户或组的 SID，此工具会很有用。PsGetSid 的用法如下：
PsGetSid \\computer -u username -p password account
请注意，account 可以指定用户、计算机或组。尽管 PSGetSid 的语法非常简单，但它仍提供了一些附加的参数。SID 参数用于指定具有指定 SID 的帐户（组或计算机），@file 用于针对指定文本文件中所列的所有计算机运行该命令。

PsInfo
PsInfo 用于提供详细的系统信息。显然，这在针对多个远程系统编制脚本时最为有用，其输出内容会发送到某个文件（例如 Excel® 和数据库可以识别的 CSV 文件）。请注意，此实用程序的运行依据一个不同于大多数其他工具的重要概念：在默认情况下，如果不提供计算机名称，PsInfo 会尝试查询域中的所有计算机。PsInfo 的用法如下：
PsInfo  \\computer -u username -p password
虽然其语法简单，但也提供了几个附加的开关和参数，如图 4 所示
Figure 4 PsInfo 开关 (Close Figure 4)

开关 | 说明
-h | 显示所有 Windows 更新。
-s | 显示系统上安装的所有应用程序。
-d | 显示系统的磁盘/卷信息。
-c | 以 CSV 格式输出结果。
-t | 为 CSV 文件指定不同于默认值 \t 的分隔符。
Filter | 仅显示与给定筛选器匹配的数据。
@file | 针对指定文本文件中所列的所有计算机运行该命令。

PsKill
PsKill 是用于在本地或远程系统上终止进程的有效实用程序。其用法非常简单，并且可以快速终止某个进程及其所有子进程。PsKill 的用法与上面介绍的其他工具非常类似：
PsKill -t \\computer -u username -p password process id | process name
请注意，您可以指定要终止的进程 id 或进程名。

PsList
我是一个 Process Explorer 迷，所以不大使用 PsList（有关详细信息，请参阅 microsoft.com/technet/sysinternals/ProcessesAndThreads/ProcessExplorer.mspx。）但由于 Process Explorer 没有远程版本，所以 PsList 理所当然成为 Process Explorer 一些主要功能的替代品。实际上，PsList 是在远程系统上运行的一个或多个进程的列表。
PsList 的用法相当简单：
PsList \\computer -u username -p password account
与其他工具一样，account 参数可以指定用户、计算机或组。其他参数如图 5 所示。
Figure 5 PsList 开关 (Close Figure 5)

开关 | 说明
-t | 显示线程级别的信息。
-m | 提供每个进程的详细内存信息。
-x | 显示进程、内存和线程信息。
-t | 按进程树显示信息（考虑 Process Explorer 树视图）。
-s [n] | 在指定的秒数内，以任务管理器模式运行。这样可以相对实时地显示远程进程信息。
-r [n] | 指定在以任务管理器模式运行时的刷新频率（以秒计）。
name | 显示以指定名称开始的进程的相关信息。
-e | 与进程名称完全匹配。
pid | 显示特定进程的相关信息。
PsList 功能强大且简单易用，您无需使用远程桌面即可相当详细地查看远程进程。在需要（例如您有必须进行控制的进程）时，可以利用此信息移到 PsKill 或 PsSuspend。

PsLoggedOn
PsLoggedOn 背后的前提假定是不言自明的。它用于显示登录到系统中的用户。这可以是本地（交互式）登录或网络共享登录。与 PsFile 相似，此功能对于确定谁在使用您所需要维护的服务器是很有用的。其用法很简单：
PsLoggedOn \\computer
PsLoggedOn 只有两个可选参数：-l 只显示本地登录（而不包括网络登录），-x 不包括登录时间。
PsLoggedOn 尽管是一个基础性的工具，但它允许您轻松地查看到通常必须要通过搜索几个 Windows 位置才能获得的信息。

PsLogList
作为最常用的实用程序之一，PsLogList 工具用于将 Windows 事件日志转储为文本格式，这些转储的日志进而再用于日志读取器、数据库或其他任何需要的地方。其主要的优点是将事件日志项从二进制格式转换为易于转换或自动处理的格式。（当然，在 Windows Vista™ 中，事件日志原本就有 XML 格式，但那不是我们今天要谈的话题。）
PsLogList 的用法：
PsLogList \\computer -u username -p password
此实用程序还提供了许多附加的开关和参数，如图 6 所示。
Figure 6 PsLogList 开关 (Close Figure 6)

开关 | 说明
-a | 仅转储所指定日期之后的事件。
-b | 仅转储所指定日期之前的事件。
-c | 在转储所需输出后永久清除事件日志。
-d | 仅显示指定天数内的信息。
-e | 排除具有指定事件 ID 的事件（最多可指定 10 个事件 ID）。
-f | 筛选事件类型（警告、错误等）。
-g | 将事件日志导出为 EVT 文件，需要指定 -c 开关。
-h | 仅显示指定小时数内的信息。
-l | 从指定的 EVT 文件转储。
-m | 仅显示指定分钟数内的信息。
-n | 仅显示最近的 n 个事件。
-o | 仅显示来自给定源的事件（例如，-o SRService 表示显示 Windows System Restore Service 的事件）。
-q | 排除来自给定源的事件（与 -o 相反）。
-r | 颠倒转储顺序，即按从最早到最近的顺序转储日志事件。
-s | 以分隔的字段在每行列出记录。
-t | 为 -s 输出内容指定除逗号之外的分隔符。
-w | 等待新事件，以实时地填充并转储。这仅适用于本地系统。
-x | 从事件日志转储扩展数据。
eventlog | 指定要转储的事件日志。System 为默认值。
@file | 针对文本文件中所指定的所有计算机运行该命令。
Windows 事件日志可以是一个强大得惊人的诊断工具，但其奥妙在于使数据变得可以操作或合并。PsLogList 就是这神奇的力量。

PsPasswd
PsPasswd 是一个简单而又强大的实用程序，其功能在某种程度上与 Net.exe 相同，但它还考虑到了远程可用性和自动化。
PsPasswd  \\computer -u username -p password Username Newpassword
您可以指定计算机，也可以不指定计算机（针对本地系统运行），如果将计算机指定为 \\*，则实用程序将针对当前域中的所有计算机运行。PsPasswd 只提供了一个附加参数：@file 用于针对文本文件中所指定的所有计算机运行该命令。同样，它也是一个非常基础性的实用程序，却能提供极强大的多系统功能。

PsService
通过 PsService，您可以在网络的一个或多个系统上查看和编辑“服务控制管理器”(SCM) 信息，甚至可以在网络的各系统上搜索给定的服务名。
PsService \\computer -u username -p password command options
运行 PsService 时，command 代表图 7 中所列的任一命令，options 用于指定应用于给定命令的属性。如果不提供附加命令开关，PsService 会完整地转储 SCM 信息。
Figure 7 PsService 命令 (Close Figure 7)

命令 | 说明
query | 查询给定的服务。
config | 查询给定服务的配置信息。
setconfig | 设置给定服务的配置信息。
start | 启动服务。
stop | 停止服务。
restart | 重新启动（停止并启动）服务。
pause | 暂停服务。
cont | 恢复暂停的服务。
depend | 显示哪些服务依赖于给定的服务。
find | 在网络中查找服务的给定实例。
security | 为给定服务提供安全信息。
因此，您可以将 PsService 视为 Services.msc 功能强大的远程命令行版本，只是它还允许您跨网络查找服务（可能具有潜在的危险）以及设置配置信息。

PsShutdown
PsShutdown 是我所喜爱的另一个工具。尽管有人会说 Microsoft 中包含一个用于处理此任务的工具 (shutdown.exe)，但它们的确是不同的。两者的主要区别在于 PsShutdown 具有以下功能：它可以关闭或重新启动远程系统。此外，它还可以将系统置为待机或休眠模式（而不仅仅是关闭或重新启动）、锁定系统以及在通过消息提示用户一段给定的时间后重新启动系统。
PsShutdown \\computer -u username -p password
PsShutdown 也提供了许多附加参数，如图 8 所示。
Figure 8 PsShutdown 开关 (Close Figure 8)

开关 | 说明
-a | 中止 PsShutdown 所启动的、已在进行的关机进程。
-c | 允许交互式用户中止关机进程。
-d | 挂起计算机（待机）。
-e | 关机原因代码。建议到 Microsoft 网站查找相关帮助，因为同时使用 PsShutdown 和内置的 shutdown.exe 时可能会发生问题。有关详细信息，请参阅 msdn2.microsoft.com/en-us/library/aa376885.aspx。
-f | 强制终止所有运行中的应用程序。
-h | 使计算机休眠。
-k | 关闭计算机的电源（如果不支持关闭电源，则重新启动计算机）。
-l | 锁定计算机。
-m | 指定要向任何交互式用户显示的消息。
-n | 指定在与远程计算机连接失败之前等待的超时时间。
-o | 注销控制台用户。
-r | 重新启动计算机。
-s | 关机，但不关闭电源。
-t | 运行关机命令之前的倒计时时间，或者距关机的具体时间。
-v | 在关机前为用户显示消息。
@file | 针对文本文件中所指定的所有计算机运行该命令。
您会发现，PsShutdown 是一个非常强大、可配置性强的系统电源控制实用程序，而且使用起来相对比较简单。

PsSuspend
如果要暂时控制一个恶意进程，或者只是要在不丢失任何数据或应用程序上下文的情况下暂时中止对 CPU 占用率高的进程，PsSuspend 将不失为一个便利的实用程序。
PsSuspend \\computer -u username -p password process ID
可以通过 PsList 获得进程 ID。挂起某个进程后，可使用 -r 开关进行恢复。请注意，Process Explorer 也会实现挂起和恢复某个进程的概念，但只是在本地。而 PsSuspend 可以通过远程及本地方式挂起和恢复进程，这显然比 Process Explorer 中内置的实现方式更具优势。

结束语
我发现 PsTools 套件是一个价值不可估量的实用程序集。连同其余的 Sysinternals 实用程序（即，Regmon、Filemon、Process Explorer 以及现在的 Process Monitor）一起，PsTools 是我在所有系统上重新安装 Windows 时第一批放入路径变量中的程序。如果您以前不了解 PsTools，我希望本专栏能让您对它有所熟悉；如果您以前就知道 PsTools，我也希望本专栏能让您对这些工具有进一步的了解。当然，我要多谢 Mark Russinovich 这些年来一直免费提供这些工具，并帮我完成本专栏的内容。

Wes Miller 是德克萨斯州奥斯汀市 Pluck 公司 (www.pluck.com) 的一位开发经理。在这之前，Wes 在位于奥斯汀市的 Winternals Software 公司任职，并曾在微软担任 Windows 程序经理和产品经理。Wes 的联系方式如下：technet@getwired.com。


 Microsoft.com 技术内幕: 数据库镜像入门
Microsoft.com 技术内幕
数据库镜像入门
Saleem Hakani

如果数据库脱机，您很可能会陷入困境，是这样吧？但是，如果您使用的是 SQL Server 2005 SP1，那么通过数据库镜像功能可以避免灾难的发生。这一具有高可用性的新技术可以让您维护数据库的热备用，
以便在生产数据库服务器因故无法使用时使用。数据库镜像功能通过将数据库的事务日志记录从主服务器传送到辅助服务器（用作热备用）来工作。通过数据库镜像，数据更改会首先记录到事务日志中，然后才会对实际数据页进行任何更改，就像 SQL Server™ 更新一直所采取的工作方式一样。日志记录首先被置于内存中主体数据库的日志缓冲区内，然后再保留到磁盘上。这些事务日志会复制到镜像服务器的数据库中并在那里重播。这样，主体数据库的更改便复制到镜像数据库。请注意，只有客户端连接才能访问主体数据库。当主体数据库收到客户端所请求的更改时，主体服务器会将这些活动的更改发送到镜像服务器，而镜像不会作出任何决定。在启用了数据库镜像的情况下，当主体数据库出现故障时，镜像数据库会变为可用。

数据库镜像的结构
数据库镜像适用于支持 SQL Server 2005 的所有标准硬件，它会确保在数据库发生故障时不丢失任何数据。在主数据库服务器上所处理的当前事务始终会更新到镜像数据库中。图 1 说明了这一过程的数据流。
如果主体服务器确实出现故障，您尽可放心，镜像服务器拥有主体数据库截至上一次所提交事务时的准确时点副本。因此，镜像随时准备接替主体服务器的角色。
Figure 1 Data replication to the mirror
Figure 1 Data replication to the mirror
在数据库镜像拓扑中，如果您想要启用从主体服务器到镜像服务器或从镜像服务器到主体服务器的自动故障转移，则需要一个称为见证方的第三个服务器。见证服务器可以是能支持 SQL Server 2005 的任何计算机。

操作模式
您所要使用的数据库镜像拓扑取决于所选择的事务安全和操作模式。数据库镜像所支持的操作模式包括高安全（带或者不带自动故障转移）和高性能模式。
高安全（带自动故障转移）：这种模式以针对镜像数据库的同步数据传输和自动故障转移来维持最高的数据库可用性。如果主体数据库与镜像服务器之间具有快速而又非常可靠的通信，而且您需要单个数据库的自动故障转移，使用这种操作模式最为理想。在此方案中，主体数据库会等到收到来自镜像服务器的消息（说明镜像服务器已将事务日志强化到磁盘）后再提交事务。
高安全（不带自动故障转移）：这种模式以同步数据传输来维持最高的数据库可用性，但它不具有自动故障转移到镜像数据库的功能。在此模式下，如果镜像服务器实例变得无法使用，主体服务器实例会继续工作，但将不能进行数据镜像。如果主体服务器出现故障，数据库镜像会暂停，但您可以手动强制服务进行故障转移。
高性能：在这种操作模式下，数据的传输是异步的。主体服务器不会像前两个模式那样等待来自镜像服务器的确认。镜像服务器会尽力与主体服务器保持一致，但在任何时刻都不能保证来自主体服务器的所有最新事务会在镜像服务器的事务日志中得到强化。如果主体服务器出现故障，数据库镜像会暂停，但您可以手动强制服务进行故障转移。

奠定基础
如果采用以下最佳实践方法来奠定一个坚固的基础，那么建立数据库镜像就是一个非常简单的过程：
服务器版本 仔细检查主体服务器和镜像服务器是否在相同版本的 SQL Server 2005 上运行；您可以使用 Standard 或 Enterprise 版本。
见证服务器可用性 如果打算使用带自动故障转移的高安全模式，应确保见证服务器可用且装有 SQL Server 2005（任何版本）。见证服务器可以在支持 SQL Server 2005 的任何可靠的计算机系统上运行。
镜像 确保镜像服务器实例与主体服务器实例具有同样的作业、登录、SQL Server Integration Service (SSIS) 程序包、磁盘分区、文件位置和服务器配置。完全按照主体服务器那样配置镜像服务器会使镜像服务器以与主体服务器相同的方式进行工作。
完全恢复 所有参与数据库镜像的数据库都设置为 FULL 恢复模式，这一点很重要。
Master 和 TempDB 验证镜像拓扑中的所有服务器实例使用相同的 Master 和 TempDB 排列规则和代码页。不同的排列规则和代码页可能会在数据库镜像建立期间引发问题。
备份 如果所要镜像的数据库规模很大，则必须首先执行数据库的完整备份，然后使用 NORECOVERY 选项在镜像服务器实例上将其还原。
提前规划 确定所有的服务器名、端口号、安全帐户以及这些信息在数据库上的驻留和记录位置。请参阅“数据库镜像最佳实践”边栏，以获得检查表。
在基础奠定好之后，您就可以在所处环境中建立数据库镜像了。
数据库镜像最佳实践
  1. 使用具有相同 CPU、内存、存储器和网络容量的伙伴服务器。
  2. 确保两个伙伴具有相同的 SQL Server 和 OS 版本、服务包和更新。
  3. 将 SQL Server 安装在主体服务器实例和镜像服务器实例的相同目录和驱动器结构上。
  4. 如果性能是一个重要的考量点，则考虑使用专用网络接口卡来分散负载。
  5. 像服务器伙伴一样，确保主体服务器实例和镜像服务器实例拥有相同的 CPU、内存、存储器和网络容量。通过确保两个服务器拥有相同的目录结构、相同的磁盘分区方案和 SQL Server 配置，可以避免在向镜像伙伴进行故障转移期间或之后对它们进行更改。
  6. 确保所有应用程序都能连接并执行所有必要的操作，确保主体服务器实例上所有活动的 SQL Server 登录（及其权限）也同样存在于镜像服务器实例上。可以对 SQL Server 2005 Integration Services 使用传输登录名任务来完成这项工作。
  7. 将 SQL Server 代理作业、警报、SSIS 程序包、支持数据库、链接的服务器定义、备份设备、维护计划、数据库邮件配置文件等等从主体服务器复制到镜像服务器。
  8. 建立一个过程，以便当主体服务器上发生任何修改（例如更改硬件、软件、SQL Server 设置或任何数据库对象）时，这些修改都会自动在镜像服务器实例上重复，或者自动复制并传输到镜像服务器实例上。
  9. 在实际投入使用前，执行多次故障转移测试。

建立镜像
让我们使用带自动故障转移的高安全操作模式来建立数据库镜像。（正如前面所说，这意味着需要见证服务器实例）。对于此处的示例，我将使用图 2 所示的服务器和数据库名称，其中还指定了每个服务器的角色。
请记住，由于在镜像配置期间未决事务日志会从主体服务器复制到镜像服务器，从而会影响性能，因此最好在非高峰期执行数据库镜像的初始配置。
镜像的建立包括三个步骤：在参与的服务器上创建端点、执行主体数据库的备份和还原，以及在所有参与的服务器上启用镜像会话。
在建立数据库镜像会话之前，必须先建立数据库镜像中所有参与服务器之间的通信机制。为此，应在所有服务器上创建端点。在 ServerA 和 ServerB 上运行以下语句：
Create Endpoint Mirroring_Endpoint
State= Started as TCP (Listener_Port=5001)
For Database_Mirroring (Role=Partner);
对于 ServerC（将用作见证服务器），将 (Role=Partner) 更改为 (Role=Witness)，并运行此语句。这样会控制每个实例所侦听的 TCP 端口。
接下来，依次执行主体服务器 DBM_Demo 数据库的完整数据库备份和日志备份，然后使用 NORECOVERY 选项在镜像服务器实例上将其还原。（使用 NORECOVERY 可确保镜像数据库处于还原状态，以便能够应用事务日志。）
以下是执行 ServerA（主体服务器实例）DBM_Demo 数据库完整数据库备份的 T-SQL 语句：
Backup Database DBM_Demo to DISK='E:\MSSQL\Bak\DBM_Demo_FULL.bak';
如果在执行完整数据库备份后数据库又有任何更改，则可能必须执行数据库的日志备份；否则，不必执行日志备份。
必要时，可以使用以下 T-SQL 语句来执行 ServerA 的 DBM_Demo 数据库的日志备份：
Backup Log DBM_Demo to Disk='E:\MSSQL\Bak\DBM_Demo_Log.bak';
执行全部的备份后，将备份文件移动到 ServerB 或共享位置，以便您可以在 ServerB 上将这些备份还原。完成后，您还应还原自最后一次执行 ServerA 完整数据库备份后所执行的任何事务日志备份。
可以使用以下 T-SQL 语句来通过 NORECOVERY 选项在 ServerB 上还原完整数据库备份和日志备份：
--Restore full database backup on the mirror --server instance
Restore Database DBM_Demo from Disk='E:\MSSQL\Bak\DBM_Demo_FULL.bak' with NORECOVERY;
最后，使用以下 T-SQL 语句来通过 NORECOVERY 选项在镜像服务器上还原日志备份：
Restore Log DBM_Demo from Disk='E:\MSSQL\Bak\DBM_Demo_Log.bak' with NORECOVERY;
还原完所有的备份后，就可以执行最后一步：在所有参与的服务器上启用数据库镜像会话。
建立数据库镜像会话时，需要每个服务器实例的服务器网络地址。此地址必须通过提供系统地址和实例所侦听的端口号来识别实例。服务器网络地址的语法如下所示：
TCP://<System-address>:<port>
<System-address>：是完全限定域名或 IP 地址；可以通过在本地计算机上从命令提示符处执行 IPCONFIG 来获得此信息。
<Port> 在创建端点时建立。
可以按下面所示在 ServerB 上启动数据库镜像会话：
Alter Database DBM_Demo
Set Partner= 'TCP://ServerA.com:5001';
然后通过运行以下 T-SQL 在 ServerA 上启动会话：
Alter Database DBM_Demo
Set Partner='TCP://ServerB.com:5001';
接下来，按下面所示在 ServerC（见证服务器）上启用镜像会话：
Alter Database DBM_Demo
Set Witness='TCP://ServerC.com:5001';
数据库镜像现在已经就绪，可以在您的环境中运行了。在 DBM_Demo 数据库上已经添加或修改的任何数据库对象都将被传输到 ServerB 副本。但是，如果 ServerA 的数据库变得无法使用，则会发生故障转移，将镜像数据库的角色改为主体角色。
既然数据库镜像已经启动并运行，一旦生产数据库出现故障，您始终都会有一个热备用可供使用。

Saleem Hakani 是 Microsoft 的高级数据库工程师，拥有超过 14 年的数据库系统经验。他创办了 Microsoft SQL 社区并担任主管，并负责整个 Windows Live 组织内 SQL Server 标准和自动化的交付工作。Saleem 持有 MCTS、MCDBA 和 MCSA 证书。您可以通过 Saleem@sqlcommunity.net 与他联系。


 现场注意事项: 医生来了
现场注意事项
医生来了
Matt Hester

不久前 我突然想到一名 IT 专业人员的角色有点类似于医生。在我第一次使用 Windows Vista™ 时瞬间产生了这一体会。当时，我遇到了网络连接问题。在经过数小时的 ping、ipconfig，甚至是数次重新启动后，我看到了一个新单词，它引起了我们 IT 专业人员所固有的好奇心：“诊断”。在使用操作系统时，我通常会看到诸如“修复”、“状态”、“任务管理器”和“发送错误报告”之类的单词和短语，这可帮助我导航操作系统的次要部分。但看到“诊断”一词后，我便开始考虑这两种职业的相似点。
首先，医生和 IT 专业人员两者都有资格考试和认证，通过这些考试和认证，我们就有资格在自己名字的后面加上某些缩写词。这些缩写词标志着我们的专业化，而且印在墙面或名片上还可能会给人留下深刻的印象。
和医生一样，IT 专业人员也有相当多的我们用来解决问题的资源。医生可与同事商量，同时可在专业期刊和 Internet 上搜索相关信息。我们 IT 专业人员也可通过与同事商量以及在 Internet 或技术出版物（当然主要是 TechNet 杂志喽！）搜索相关信息，来帮助我们解决问题。
而且，医生和 IT 专业人员都依赖于可信任的工具。医生有听诊器之类一些靠得住的仪器；我们有 ping。这两类工具均可使我们能够侦听所发生的事情。
最后一个共同点实质上是社交方面。当医生和 IT 专业人员参与社会活动时，我们经常会发现隐藏自己的专业身份非常重要。唉，在近期的一次聚会上，有人出于某种原因说出了我所做的工作，突然我就变得非常受欢迎了。但受欢迎是要付出代价的：我同时也失去了一个有魅力的人的身份。对所有聚会来宾来说，我只是一个“PC 专家”，更确切地说，是令人厌恶的现场支持专家：“您好，PC 专家，今天上午我的计算机为什么崩溃了？”“您好，PC 专家，Any 键在哪儿？”、“您好，PC 专家，您能修复我的 Mac 吗？”，最糟糕的是我看到房间里有一位医生一边在不停地跳舞，一边咧着嘴向我会心地笑着。
让我们言归正传，回到我遇到的连接问题吧：大约经过长达三个小时的连接尝试失败之后，我单击了新发现的“诊断”和“修复”按钮。当我看到下面显示的回复屏幕时，可以想象我的震惊和尴尬：
我目瞪口呆，面部灼热，我环顾四周以确定没人在注视我。然后我回到 PC 附近，插入电缆。从那个令人兴奋的日子之后，我更深入地使用了 Windows Vista，并广泛地使用了诊断功能。这一诊断功能为我节省了大量在查找许多问题根本原因上所花费的时间。它还有效集成了“问题报告和解决方案”控制面板，这使我们能够在线搜索其他信息。在您解决问题之前，请查找所需的信息。
那天晚上我深夜才渐渐入睡，我为自己成功识别和解决了网络连接问题而深感愉快，我竟然想到：这是否意味着我可以考虑花更多时间来打高尔夫球和驾驶别致的新款跑车？

Matt Hester 是 Microsoft 跨美 (Microsoft Across America) 团队的 TechNet 演示者。欲了解有关这些演示的更多信息，可访问 technetevents.com/mhester。您也可以访问他的博客 blogs.technet.com/matthewms。


 Windows 机密: 靠愚蠢显得聪明
Windows 机密
靠愚蠢显得聪明
Raymond Chen

当您 将 CD 插入计算机时，Windows® 可能会问您它将要做什么 - 即使您上次告诉过它做什么。为什么 Windows 会忘记您的自动播放设置呢？唔，它不是忘记了，而只是重新与您核实一下。
在 Windows XP 处理 CD 自动播放的方式上存在两个问题。第一，许多用户发现，在安装一个支持 CD 自动播放的程序之后，他们不知道如何使该程序成为他们新的默认的 CD 自动播放程序。如果他们以前选择了一个程序并且勾选了“始终执行此操作”，则没有可轻易发现的方法来取消选中这个始终执行标志，使 CD 自动播放对话框再次出现，并给用户机会选择新程序作为默认设置。
第二，某些行为不良的程序，为了将它们自己设置为默认的 CD 自动播放处理程序，在安装后暗中攻击未作文档记录的 CD 自动播放设置，从而覆盖了用户的选择。
简而言之，对于用户来说，这两个问题是：“我刚刚安装了此程序，我想让它成为我的 CD 自动播放程序。”和它的相反说法“我刚刚安装了此程序，我不想让它成为我的 CD 自动播放程序。”
Windows Vista treats autoplay settings globally
Windows Vista treats autoplay settings globally
Windows XP Service Pack 2 (SP2) 引进了新的 CD 自动播放行为，以试图解决这些问题。当 Windows 看到有新的 CD 自动播放处理程序可用时，现在会再显示一次 CD 自动播放对话框。这样，您就可以选择这个刚安装的新程序，或者在这个刚安装的新程序完全不合理地将自己设置为新的默认播放器的情况下取消选择这个新程序。
升级到 Windows XP SP2 之后，当您第一次插入 CD 时，同样会为您显示这个 CD 自动播放对话框。这是一个“迟来总比不来好”的对话框，面向的是在升级到 Windows XP SP2 之前安装的任何处理程序。
在过去，OS 只需关心如何帮助开发人员与功能对接即可。但在当前的软件前景下，它还要担心如何阻止试图滥用该接口的编程人员。
还可能出现另一种情况，那就是 Windows 忘记了您的自动播放设置。当您插入一个可移除式存储设备（如 USB 驱动器）时，即使您已经选择了一个选项并且勾选了“始终执行此操作”，系统可能还是会提示您。这不是因为 Windows 忘记了您的设置，而是因为它记得太好了。
当您为某个可移除式存储设备选择一个选项时，Windows 记住了该设备的设置。如果您插入一个不同的设备，您会得到一个新提示，因为您没有指定想要这个不同的设备采用什么行为方式。这种设计的目的是，这样您就可以指定，数码相机将默认用于“查看图片”，而音乐播放器将默认用于“播放音乐”。（如果设备没有序列号，则将其插入另一个 USB 端口会将其视为另一个设备。）
为应对由 Windows XP 设计造成的混乱，在 Windows Vista™ 中已更改了该行为。现在，自动播放设置被视为全局设置而不是“按设备”设置。如果您指定“不执行任何操作”，则无论您插入哪个设备，也无论您将设备插入什么位置，Windows Vista 都不会执行任何操作。有时让操作系统显得更聪明的方法就是让它变得更愚蠢。

Raymond Chen的网站“The Old New Thing”（英文）(blogs.msdn.com/oldnewthing) 和同名书籍（Addison-Wesley，2007）论述了 Windows 历史和 Win32 编程。在他的业余时间，众所周知，他的论文被评为七级。