基于 Windows 的域控制器上的分布式链接跟踪

本文介绍如何使用 Windows 中的分布式链接跟踪服务跟踪跨 NTFS 格式卷和服务器创建和移动链接文件。

原始 KB 数: 312403

可以使用分布式链接跟踪服务器服务和分布式链接跟踪客户端服务跟踪指向 NTFS 格式分区上的文件的链接。 分布式链接跟踪用于在 NTFS 卷上对文件的链接(例如 shell 快捷方式和 OLE 链接)进行跟踪。 如果该文件已重命名、移到同一计算机上的另一个卷、移动到另一台计算机或在其他类似方案中移动,Windows 将使用分布式链接跟踪查找该文件。 访问已移动的链接时,分布式链接跟踪将找到该链接;你不知道文件已移动,或者分布式链接跟踪用于查找已移动的文件。

分布式链接跟踪由客户端服务和服务器服务组成。 分布式链接跟踪服务器服务仅在基于 Windows Server 的域控制器上运行。 它将信息存储在 Active Directory 中,并提供服务来帮助分布式链接跟踪客户端服务。 分布式链接跟踪客户端服务在所有基于 Windows 2000 且Microsoft基于 Windows XP 的计算机上运行,包括工作组环境中的计算机或不在工作组中的计算机。 它提供与分布式链接跟踪服务器的唯一交互。

分布式链接跟踪客户端偶尔会向分布式链接跟踪服务器服务提供有关文件链接的信息,分布式链接跟踪服务器服务存储在 Active Directory 中。 当无法解析 shell 快捷方式或 OLE 链接时,分布式链接跟踪客户端还可以查询分布式链接跟踪服务器服务以获取该信息。 分布式链接跟踪客户端将提示分布式链接跟踪服务器每隔 30 天更新链接。 分布式链接跟踪服务器服务清理了 90 天内未更新的对象

当链接引用的文件移动到另一卷(在同一台计算机或另一台计算机上),分布式链接跟踪客户端会通知分布式链接跟踪服务器,该服务器在 Active Directory 中创建 linkTrackOMTEntry 对象。 在 Active Directory 中为域中的每个 NTFS 卷创建一个 linkTrackVolEntry 对象。

注意

在 Windows Server 2008 及更新的 Windows 中,分布式链接跟踪服务器服务不再包含在 Windows 中。 因此,你可以安全地从 Active Directory 中删除对象。

分布式链接跟踪对象在托管计算机帐户的域中的所有域控制器和林中的所有全局目录服务器之间复制。 分布式链接跟踪服务器服务在以下可分辨名称路径中创建对象:

CN=FileLinks,CN=System,DC= 域名 的 Active Directory 容器

分布式链接跟踪对象存在于 CN=FileLinks,CN=System 文件夹下的以下两个表中:

  • CN=ObjectMoveTable,CN=FileLinks,CN=System,DC= 域名

此对象存储有关已在域中移动的链接文件的信息。

  • CN=VolumeTable,CN=FileLinks,CN=System,DC= 域名

    此对象存储域中每个 NTFS 卷的相关信息。

分布式链接跟踪对象单独消耗少量空间,但允许在 Active Directory 中累积大量空间时,它们可能会消耗大量空间。

如果禁用分布式链接跟踪并从 Active Directory 中删除分布式链接跟踪对象,则可能会发生以下行为:

  • Active Directory 数据库大小可能会减少(在对象被标记为待删除并进行垃圾回收之后,以及执行脱机碎片整理过程之后,会出现这种情况)。
  • 域控制器之间的复制流量可能会减少。

在 Windows 2000、Windows XP 和 Windows Server 2003 中,分布式链接跟踪客户端服务的起始值设置为“自动”。 在基于 Windows 2000 的服务器上,分布式链接跟踪服务器服务默认手动启动。 但是,如果使用Dcpromo.exe将服务器提升到域,则分布式链接跟踪服务器服务配置为自动启动。

对于基于 Windows Server 2003 的服务器,分布式链接跟踪服务器服务默认处于禁用状态。 使用Dcpromo.exe将服务器提升到域时,分布式链接跟踪服务器服务未配置为自动启动。 将基于 Windows 2000 的域控制器升级到 Windows Server 2003 时,在升级期间也会禁用分布式链接跟踪服务器服务。 如果你是管理员,并且想要使用分布式链接跟踪服务器服务,则必须使用组策略,或者必须手动将服务设置为自动启动。 此外,运行 Windows Server 2003 或 Windows XP SP1 的计算机上的分布式链接跟踪客户端服务不会默认尝试使用分布式链接跟踪服务器服务。 如果要将这些计算机配置为利用分布式链接跟踪服务器服务,请启用“允许分布式链接跟踪”客户端使用域资源策略设置。 为此,请在组策略中打开计算机配置/管理模板/系统节点。

Microsoft建议在基于 Windows 2000 的服务器上将以下设置与分布式链接跟踪配合使用:

  1. 在所有域控制器上关闭分布式链接跟踪服务器服务(这是所有基于 Windows Server 2003 的服务器的默认配置)。

    由于复制开销以及 FileLinks 表在 Active Directory 中占用的空间,Microsoft 建议在 Active Directory 域控制器上关闭分布式链接跟踪服务器服务。 若要停止服务,请使用以下任一方法:

    • 在“服务”管理单元(Services.msc 或 compmgmt.msc),双击“分布式链接跟踪服务器”服务,然后在“启动类型”框中单击“已禁用”。

    • 在组策略的计算机配置/Windows 设置/系统服务节点中定义启动值。

    • 定义托管所有 Windows 2000 域控制器的组织单位上的策略设置。

    复制策略后重启域控制器,以便应用策略。 如果未重启域控制器,则必须在每个域控制器上手动停止该服务。

  2. 从 Active Directory 域控制器中删除分布式链接跟踪对象。

    有关如何删除分布式链接跟踪对象的详细信息,请参阅本文的“如何删除分布式链接跟踪对象”部分。 建议在禁用分布式链接跟踪服务器服务后删除对象。

    注意

    在完成以下操作之前,域控制器上的目录信息树(DIT)大小不会减少。

    1. 对象将从目录服务中删除。

      注意

      已删除的对象存储在“已删除的对象”容器中,直到墓碑生存期过期。 删除标记生存期的默认值为 60 天。 最小值为两天。 默认情况下,新的目录林与 Windows Server 2003 Service Pack 1 或更高版本的 Windows Server 2003 一同安装时的默认值为180天。

      除非您具有强大的 Active Directory 复制监控,我们建议您使用 180 天这个值。 不要减少此值以处理 DIT 大小问题。 如果数据库大小有问题,请联系Microsoft客户支持服务。

    2. 垃圾回收已完成。

    3. 使用Ntdsutil.exe在 Dsrepair 模式下对 Ntds.dit 文件进行碎片整理。

停止分布式链接跟踪服务器服务后手动删除分布式链接跟踪对象并不重要,除非必须尽快回收这些对象使用的磁盘空间。 分布式链接跟踪客户端将提示分布式链接跟踪服务器每隔 30 天更新链接。 分布式链接跟踪服务器服务清理了 90 天内尚未更新的对象。

运行 Dltpurge.vbs VBScript 时,分布式链接跟踪服务器服务使用的所有 Active Directory 对象将从运行脚本的域中删除。 必须在林中每个域的一个域控制器上运行该脚本。 运行 Dltpurge.vbs:

  1. 从Microsoft产品支持获取 Dltpurge.vbs 脚本。

  2. 停止 Dltpurge.vbs 针对的域中所有域控制器上的分布式链接跟踪服务器服务。

  3. 使用管理员权限登录到域控制器的控制台或正在被 Dltpurge.vbs 作为目标的域中的成员计算机。

  4. 使用以下语法从命令行运行 Dltpurge.vbs:

    cscript dltpurge.vbs -s myserver -d dc=mydomain,dc=mycompany,dc=com  
    

    在此命令行中:

    • -s 是要删除分布式链接跟踪对象的域控制器的 DNS 主机名。
    • -d 是用于删除分布式链接跟踪对象的域的专用名称路径。
  5. 在对象被墓碑化和垃圾回收后,对 Ntds.dit 文件执行脱机碎片整理过程。 有关垃圾回收过程的详细信息,请单击以下文章编号以查看Microsoft知识库中的文章:

    198793 Active Directory 数据库垃圾回收过程

示例客户体验

本部分介绍的最坏情况说明了在大型生产域中删除大量分布式链接跟踪对象时需要考虑的一些问题。

Trey Research 是一家虚构的财富 500 强公司,全球拥有超过 40,000 名员工。它部署了一个包含空根域的 Active Directory 林,以及用于映射世界各主要地理区域(例如北美、亚洲、欧洲等)的子域。 林中最大的域包含大约 35,000 个用户帐户和相同数量的计算机帐户。

Ntds.dit 文件放置在 18 GB RAID 阵列上。 自 Windows 2000 初始部署以来,全局目录文件已增加到 17 GB。

Trey Research 希望在未来 10 天内部署 Windows Server 2003,但在启动升级之前,数据库分区上至少需要 1.5 GB 的可用磁盘空间。 他们需要这么多磁盘空间,因为众所周知,Adprep.exe 会根据先前安装的修补程序和服务包,添加三到五个继承的访问控制项。 以下条件导致大型全局目录大小或磁盘空间不足:

  • 条件 1:Trey Research 是 Windows 2000 的早期采用者,他们从首选硬件供应商处收到的最大硬盘在 RAID 阵列中配置时为 9 GB 或 18 GB。 当前的存储设备的大小是以前的两倍,但成本减半。

  • 条件 2:未在委托给林中每个域的 Active Directory 集成 DNS 区域上启用 DNS 清理。

  • 条件 3:允许域用户在域中创建计算机帐户。 管理员没有定期过程来标识和删除孤立的计算机帐户。

  • 条件 4:随着时间的推移,管理员、Service Pack 和补丁程序在根命名上下文(NC)头(cn=schema、cn=configuration、cn= domain)以及在 Active Directory 中托管数千个对象的其他容器上定义了安全描述符。 此外,在同一分区上启用了审核功能。 在 Active Directory 中设置权限并启用对对象的审核时,数据库的大小将增加。 为基于 Windows Server 2003 的域控制器准备 Windows 2000 林和域的工具 (Adprep) 还会添加继承的访问控制项(ACE);因此,Trey Research 在升级域之前需要释放磁盘空间。

  • 条件 5:Trey Research 未定期在 Dsrepair 模式下执行 Ntds.dit 文件的脱机碎片整理过程。

  • 条件 6:查看最大域中的 CN=FileLinks、CN=System,DC= 域名 容器时,它显示超过 700,000 个分布式链接跟踪对象。 每个分布式链接跟踪对象上的安全描述符大约为 2 KB(KB)。 对这些条件进行了评估,分析它们对 17 GB .dit 文件的贡献。

  • 条件 1:Trey Research 决定不部署新驱动器,因为成本以及这样做所需的时间。 此外,它们只需要暂时的磁盘空间,因为它们期望 Active Directory 数据库在升级到 Windows Server 2003 后收缩,并且单实例存储(SIS)过程已完成(SIS 实现了在 Active Directory 数据库中更有效地存储权限)。

  • 条件 2 和 3:Trey Research 决定这些条件是最佳做法:然而,即使 Trey Research 实施了它们,他们也不会取得所需的结果。 他们决定启用 DNS 清理,因为它很容易实现。

  • 条件 4:Trey Research 意识到,如果他们重新定义安全描述符和系统访问控制列表(SACL),他们将能达到预期结果,但他们决定,该过程的实施将非常耗时,直到他们能够在反映生产环境的实验室场景中全面测试尺寸缩减、复制开销,以及最重要的程序/管理兼容性。

    由于 Trey Research 部署了 Windows 2000 SP2 以及一些热修复程序,因此他们预计由 Adprep 添加到域 NC 对象的增量继承权限项可能仅增加约 300 兆字节 (MB)。 他们可以在用于测试生产林升级的实验室环境中验证这一行为。

  • 条件 5:Trey Research 意识到,如果他们执行脱机碎片整理过程,它们可能无法恢复 Ntds.dit 文件中的“空格”。 事实上,Trey Research 管理员在完成脱机碎片整理过程后立即注意到数据库大小增加。 发生此行为的原因是 Windows 2000 数据库引擎效率低下;此引擎在 Windows Server 2003 中得到了增强。

  • 条件 6:Trey Research 同意,显而易见的操作方式是在林中的每个域的域控制器上,从 CN=FileLinks,CN=System,DC=域名容器中执行简单的批量删除,清除所有分布式链接跟踪对象。 但是,他们意识到,如果这样做,直到对象被标记为可删除并完成垃圾回收,并且在该域的每个域控制器上执行离线碎片整理过程之前,不会释放额外的磁盘空间。 虽然墓碑生存期值可以设置为低至两天,但 Trey Research 林中的多个域控制器在等待硬件和软件更新时处于脱机状态。 如果在端到端复制完成之前对象被墓碑化,删除的对象可能会被重新激活,或者林中全局目录服务器之间可能会报告数据不一致的情况。 为了立即提供救济,Trey Research 执行了以下过程:

  1. 他们删除了分布式链接跟踪架构类对象的默认安全描述符,并将其替换为单个安全主体(用户帐户)。
  2. 他们编写了一个 VBScript 程序,该程序删除了所有现有的安全描述符,然后将它们替换为针对单个安全主体的显式访问控制项(ACE)。
  3. 它们以 10,000 个单位增量删除分布式链接跟踪对象,每个对象删除之间的延迟为三小时。
  4. 删除所有分布式链接跟踪对象后,在域中的每个域控制器上执行脱机碎片整理过程。 Trey Research 删除描述符并执行碎片整理过程时,数据库在域中的所有域控制器上恢复了大约 1.5 GB 的磁盘空间。 此空间量足以轻松运行 Adprep 工具,并将所有基于 Windows 2000 的域控制器和全局目录升级到 Windows Server 2003。

Trey Research 将操作系统升级到 Windows Server 2003 后,当 Windows Server 2003 中的单实例存储功能将数据库大小减少到约 8 GB 时,释放了更多的磁盘空间(必须执行脱机碎片整理过程才能获取这些结果)。 TSL 间隔过期后,恢复了更多空间,分布式链接跟踪对象被进行垃圾回收,并且执行了脱机碎片整理过程。

Trey Research 将基于 Windows 2000 的新副本域控制器提升为域的成员,并将计算机帐户放置在不同于他们通常使用的组织单位中。 在两天内,基于 Windows 2000 的域控制器上存在大约 8,000 个分布式链接跟踪对象。 Trey Research 停止了分布式链接跟踪或创建了一个策略来停止服务,然后将该策略链接到托管基于 Windows 2000 的域控制器的组织单位。 最后,Trey Research 使用 Dltpurge.vbs 标记要删除的剩余分布式链接跟踪对象。

DLT 对象删除剖析

DLT 对象本身包含几个属性,在 Active Directory 中使用的空间很少。 将对象标记为删除(逻辑删除)时,除跟踪对象直至从 Active Directory 彻底清除所需的属性外,所有不必要的属性都会被删除。

对于链接跟踪对象,标记要删除的对象只相当于删除的两个属性:dscorepropagationdata 和 objectcategory。 删除这两个属性会导致初始节省 34 个字节。 但是,标记链接跟踪对象以删除的过程还会通过添加一个 IS_DELETED 属性(4 个字节),并通过变更 RDN 和“公用名”属性来更新该对象,这导致每个属性增加大约 80 个字节。 此外,“复制元数据”属性还会增长约 50 字节,以反映对此对象执行的更新。 因此,通过标记链接跟踪对象进行删除,该对象最终将增长约 200 字节。 NTDS.DIT 不会表现出减小大小,直到删除的对象变为墓碑状态、进行垃圾回收,并完成脱机碎片整理。

注意

如果按照本文的建议关闭服务,则不会发生自动清理。

Dltpurge.vbs 的文本版本

若要使用此脚本,

  1. 复制本文中的 <“开始复制此处> ”标记和 <“结束复制此处> ”标记之间的所有文本,然后将文本粘贴到 ASCII 文本编辑器文件(例如Microsoft记事本文件)。
  2. 将文件另存为“Dltpurge.vbs”。 3 完成如何删除分布式链接跟踪对象中所述 的过程
<Start Copy Here>
'==============================================================================
'==============================================================================
'
' Copyright (C) 2001 by Microsoft Corporation.  All rights reserved.
'
' This script deletes all Active Directory objects used by the
' Distributed Link Tracking Server service.
'
' It is assumed that the DLT Server service has been disabled,
' and you wish to recover the DIT space these objects occupy.
'
' Usage:   cscript DltPurge.vbs <options>
' Options: -s ServerName
'          -d distinguishedname dc=mydomain,dc=mycompany,dc=com
'          -b BatchSize  BatchDelayMinutes
'          -t (optional test mode)
'
' The objects are deleted in batches - BatchSize objects are deleted,
' then there is a BatchDelayMinutes delay before the next batch.
'
'==============================================================================
'==============================================================================

Option Explicit

'
' Globals, also local to main.
'
Dim oProvider
Dim oTarget
Dim sServer
Dim sDomain
Dim bTest

Dim BatchSize
Dim BatchDelayMinutes

'
' Set defaults
'

BatchSize = 1000
BatchDelayMinutes = 15
bTest = False

'==============================================================================
'
'   ProcessArgs
'
'   Parse the command-line arguments.  Results are set in global variables
'   (oProvider, oTarget, sServer, sDomain, BatchSize, and BatchDelayMinutes).
'
'==============================================================================


public function ProcessArgs

    Dim iCount
    Dim oArgs

    on error resume next

    '
    ' Get the command-line arguments
    '
    
    Set oArgs = WScript.Arguments

    if oArgs.Count > 0 then

        '
        ' We have command-line arguments.  Loop through them.
        '

        iCount = 0
        ProcessArgs = 0

        do while iCount < oArgs.Count

            select case oArgs.Item(iCount)

                '
                ' Server name argument
                '
                
                case "-s"

                    if( iCount + 1 >= oArgs.Count ) then
                        Syntax
                        ProcessArgs = -1
                        exit do
                    end if

                    sServer = oArgs.Item(iCount+1)
                    if Len(sServer) > 0 then sServer = sServer & "/"
                    iCount = iCount + 2

                '
                ' Enable testing option
                '
                
                case "-t"

                    iCount = iCount + 1
                    bTest  = True

                '
                ' Domain name option
                '
                
                case "-d"

                    if( iCount + 1 >= oArgs.Count ) then
                        Syntax
                        ProcessArgs = -1
                        Exit Do
                    end if

                    sDomain = oArgs.Item(iCount+1)
                    iCount = iCount + 2

                '
                ' Batching option (batch size, batch delay)
                '

                case "-b"

                    if( iCount + 2 >= oArgs.Count ) then
                        Syntax
                        ProcessArgs = -1
                        exit do
                    end if

                    Err.Clear
                    
                    BatchSize = CInt( oArgs.Item(iCount+1) )
                    BatchDelayMinutes = CInt( oArgs.Item(iCount+2) )
                    
                    if( Err.Number <> 0 ) then 
                        wscript.echo "Invalid value for -b argument" & vbCrLf
                        Syntax
                        ProcessArgs = -1
                        exit do
                    end if
                    
                    iCount = iCount + 3

                '
                ' Help option
                '
                
                case "-?"
                    Syntax
                    ProcessArgs = -1
                    exit do

                '
                ' Invalid argument
                '
                
                case else
                
                    ' Display the syntax and return an error

                    wscript.echo "Unknown argument: " & oArgs.Item(iCount) & vbCrLf
                    Syntax
                    ProcessArgs = -1
                    Exit Do
                    
            end select
      loop

    else
    
        '
        ' There were no command-line arguments, display the syntax
        ' and return an error.
        '

        Syntax
        ProcessArgs = -1

    end if

    Set oArgs = Nothing

end function ' ProcessArgs

'==============================================================================
'
'   Syntax
'
'   Show the command-line syntax
'
'==============================================================================

public function Syntax

    wscript.echo    vbCrLf & _
                    "Purpose:   Delete Active Directory objects from Distributed Link Tracking" & vbCrLf & _
                    "           Server service (Assumes that DLT Server has been disabled" & vbCrLf & _
                    "           on all DCs)" & vbCrLf & _
                    vbCrLf & _
                    "Usage:     " & wscript.scriptname & " <arguments>" & vbCrLf & _
                    vbCrLf & _
                    "Arguments: -s Server" & vbCrLf & _
                    "           -d FullyQualifiedDomain" & vbCrLf & _
                    "           -b BatchSize BatchDelayMinutes (default to 1000 and 15)" & vbCrLf & _
                    "           -t (optional test mode, nothing is deleted)" & vbCrLf & _
                    vbCrLf & _
                    "Note:      Objects are deleted in batches, with a delay between each" & vbCrLf & _
                    "           batch.  The size of the batch defaults to 1000 objects, and" & vbCrLf & _
                    "           the length of the delay defaults to 15 minutes.  But these" & vbCrLf & _
                    "           values can be overridden using the -b option." & vbCrLf & _
                    vbCrLf & _
                    "Example:   " & wscript.scriptname & "  -s  myserver  -d distinguishedname dc=mydomain,dc=mycompany,dc=com "

end function    ' Syntax



'==============================================================================
'
'   PurgeContainer
'
'   Delete all objects of the specified class in the specified container.
'   This subroutine is called once for the volume table and once for
'   the object move table.
'
'==============================================================================

sub PurgeContainer(ByRef oParent, ByVal strClass)

    dim oChild
    dim iBatch
    dim iTotal

    On Error Resume Next

    iTotal = 0
    iBatch = 0

    ' Loop through the children of this container

    For Each oChild in oParent

        ' 
        ' Is this a DLT object?
        '

        
        if oChild.Class = strClass Then

            '
            ' Yes, this is a DLT object, it may be deleted
            '
            
            iTotal = iTotal + 1
            iBatch = iBatch + 1

            '
            ' Delete the object
            '
            
            if bTest then
                wscript.echo "Object that would be deleted: " & oChild.adspath
            else
                oParent.Delete oChild.Class, oChild.Name
            end if

            '
            ' If this is the end of a batch, delay to let replication
            ' catch up.
            '
            
            if iBatch = BatchSize then
            
                iBatch = 0
                
                wscript.stdout.writeline "" ' ignored by wscript
                wscript.echo "Deleted " & BatchSize & " objects"
                wscript.echo "Pausing to allow processing (will restart at " & DateAdd("n", BatchDelayMinutes, Time) & ")"
                
                wscript.sleep BatchDelayMinutes * 60 * 1000
                wscript.echo "Continuing ..."
                
            end if
            
        else
        
            ' oChild.Class didn't match strClass
            wscript.echo "Ignoring unexpected class: " & oChild.Class
            
        end if

        oChild = NULL

    Next


    wscript.echo "Deleted a total of " & iTotal & " objects"

end sub ' PurgeContainer


'==============================================================================
'
' Main
'
'==============================================================================

if (ProcessArgs=-1) then wscript.quit

on error resume next

'
' Explain what's about to happen
'

wscript.stdout.writeline "" ' ignored by wscript
wscript.echo "This script will purge all objects from the Active Directory" & vbCrLf & _
             "used by the Distributed Link Tracking Server service (trksvr)." & vbCrLf & _
             "It is assumed that this service has already been disabled on" & vbCrLf & _
             "all DCs in the domain."

'
' When running in cscript, pause to give an opportunity to break out
' (These 3 lines are for cscript and ignored by wscript.)
'

wscript.stdout.writeline ""
wscript.stdout.writeline "Press Enter to continue ..."
wscript.stdin.readline

'
' Get an ADSI object
'

Set oProvider = GetObject("LDAP:")

'
' Purge the System/FileLinks/ObjectMoveTable
'

wscript.stdout.writeline "" ' ignored by wscript
wscript.echo "Purging ObjectMoveTable"

Set oTarget = oProvider.OpenDSObject( "LDAP://" & sServer  & "cn=ObjectMoveTable,CN=FileLinks,CN=System," & sDomain ,_
                                      vbNullString, vbNullString, _
                                      1) ' ADS_SECURE_AUTHENTICATION

call PurgeContainer( oTarget, "linkTrackOMTEntry" )
oTarget = NULL

'
' Purge the System/FileLinks/VolumeTable
'

wscript.stdout.writeline "" ' ignored by wscript
wscript.echo "Purging VolumeTable"

Set oTarget = oProvider.OpenDSObject("LDAP://" & sServer  & "cn=VolumeTable,CN=FileLinks,CN=System," & sDomain  ,_
                                     vbNullString, vbNullString, _
                                     1) ' ADS_SECURE_AUTHENTICATION
call PurgeContainer( oTarget, "linkTrackVolEntry" )
oTarget = NULL

oProvider = NULL
<END Copy Here>