【问题标题】:How do I speed up data retrieval from .NET AD within ColdFusion?如何在 ColdFusion 中加快从 .NET AD 的数据检索速度?
【发布时间】:2008-09-17 15:58:18
【问题描述】:

如何优化以下代码,目前该代码需要 2 多分钟才能从超过 10 万条记录的池中检索和循环 800 多条记录,每条记录返回 6 个字段(每个附加字段大约增加 20 秒):

<cfset dllPath="C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.DirectoryServices.dll" />
<cfset LDAPPath="LDAP://" & arguments.searchPath />
<cfset theLookUp=CreateObject(".NET","System.DirectoryServices.DirectoryEntry", dllPath).init(LDAPPath) />
<cfset theSearch=CreateObject(".NET","System.DirectoryServices.DirectorySearcher", dllPath).init(theLookUp) />
<cfset theSearch.Set_Filter(arguments.theFilter) />
<cfset theObject = theSearch.FindAll() />

<cfloop index="row" from="#startRow#" to="#endRow#">
   <cfset QueryAddRow(theQuery) />
   <cfloop list="#columnList#" index="col">
     <cfloop from="0" to="#theObject.Get_Item(row).Get_Properties().Get_Item(col).Get_Count()-1#" index="item">
       <cftry>
         <cfset theQuery[col][theQuery.recordCount]=ListAppend(theQuery[col][theQuery.recordCount],theObject.Get_Item(row).Get_Properties().Get_Item(col).Get_Item(item),"|") />
         <cfcatch type="any">
         </cfcatch>
        </cftry>
      </cfloop>
    </cfloop>
  </cfloop>

【问题讨论】:

    标签: .net coldfusion active-directory ldap


    【解决方案1】:

    内部循环的项目列表有多大?

    如果有大量项目,切换到数组可能会更快。

    我已经按照 x0n 的建议实现了这个...

    <cfset dllPath="C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\System.DirectoryServices.dll" />
    <cfset LDAPPath="LDAP://" & arguments.searchPath />
    <cfset theLookUp=CreateObject(".NET","System.DirectoryServices.DirectoryEntry", dllPath).init(LDAPPath) />
    <cfset theSearch=CreateObject(".NET","System.DirectoryServices.DirectorySearcher", dllPath).init(theLookUp) />
    <cfset theSearch.Set_Filter(arguments.theFilter) />
    <cfset theObject = theSearch.FindAll() />
    
    <cfloop index="row" from="#startRow#" to="#endRow#">
    
        <cfset Props = theObject.get_item(row).get_properties() />
    
        <cfset QueryAddRow(theQuery) />
    
        <cfloop list="#columnList#" index="col">
    
            <cfset CurrentCol = Props.getItem(col) />
    
            <cfset ItemArray = ArrayNew(1)/>
            <cfloop from="0" to="#CurrentCol.getcount() - 1#" index="item">
                <cftry>
                    <cfset ArrayAppend( ItemArray , CurrentCol.Get_Item(item) )/>
                    <cfcatch type="any">
                    </cfcatch>
                </cftry>
            </cfloop>
            <cfset theQuery[col][theQuery.recordCount] = ArrayToList( ItemArray , '|' )/>
    
        </cfloop>
    
    </cfloop>
    

    【讨论】:

    • 好的,所以我对此进行了测试...我发现运行时间减少了 30%...这似乎很有意义,因为我们有效地减少了三分之一对单数项目的要求。我绝对对您能想到的任何其他优化感兴趣...
    【解决方案2】:

    很久没接触CF了,不过可以用伪代码给点提示。一方面,这种表达方式极其低效:

    #theObject.Get_Item(row).Get_Properties().Get_Item(col).Get_Count()-1#

    以第一部分为例,Get_Item(row) - 您的代码使 CF 为 #columnList# 循环的每次迭代检索行及其属性;最重要的是,您在 columnlist 的每次迭代中都执行 TWICE(一次 for 循环,再次用于内部 cfset)。如果您考虑一下,它只需要为外部循环的每次迭代(从#sfstart# 到#cfend)检索行。所以,在伪代码中这样做:

    开始和结束之间的每一行

    cfset props = #theobject.get_item(row).get_properties()#

    #columnlist# 中的每个列

    cfset currentcol = #props.getitem(col)#

    cfset count = #currentcol.getcount() - 1#

    foreach 项目从 0 到 #count#

    cfset #currentcol.getItem(item)# 等...

    有意义吗?每次进入循环时,将在该范围(或子范围)中重用的对象缓存在一个变量中。这意味着您在列循环的每次迭代中只抓取一次列对象。外部作用域中定义的所有变量都可以在内部作用域中使用,正如您在上面所做的那样。我知道从前几行剪切和粘贴很诱人,但不要。到头来只会伤害你。

    希望这会有所帮助,

    欧辛

    【讨论】:

    • 计数变量/缓存不是必需的 - CF 将缓存 cfloop 的 from/to 值。 (虽然不是 cfscript 中的循环语句)
    • 我本来是这么想的,但我试图证明一个观点。
    【解决方案3】:

    此外,在每个循环中使用 cftry 块可能会大大减慢速度。除非您期望个别行失败(并且您需要从那一点继续),否则我建议为整个过程使用单个 try/catch 块。 Try/catch 是一项昂贵的操作。

    【讨论】:

    • 是的,但在这种情况下,我预计数据会出现错误或丢失......所以我不得不忍受这种打击。问题:鉴于其他建议,我假设我可以通过在最低范围检查 itemCount 来绕过它
    • 再一次,我不能谈论 CF,但在任何其他具有 try/catch 和异常的语言中,昂贵的不是 try/catch,而是异常的创建。这是因为异常包含堆栈跟踪 - 也就是说,在此之前调用的所有方法的列表。
    【解决方案4】:

    我认为您应该停止在循环内进行如此多的评估,而是使用变量来保存计数、指向 col 对象的指针并保存管道分隔符字符串,直到您准备好提交查询对象。如果我正确地完成了重构,如果您使用以下代码,您应该会注意到改进:

    <cfloop index="row" from="#startRow#" to="#endRow#">
    <cfset QueryAddRow(theQuery) />
    <cfloop list="#columnList#" index="col">
        <cfset PipedVals = "">
        <cfset theItem = theObject.Get_Item(row).Get_Properties().Get_Item(col)>
        <cfset ColCount = theItem.Get_Count()-1>
        <cfloop from="0" to="#ColCount#" index="item">
            <cftry>
            <cfset PipedVals = ListAppend(PipedVals,theItem.Get_Item(item),"|")>
            <cfcatch type="any"></cfcatch>
            </cftry>
        </cfloop>
        <cfset QuerySetCell(theQuery,col) = PipedVals>
    </cfloop>
    

    【讨论】:

    • 好的,让我稍微消化一下...通过对象的调用不是类似于数组,而是一组链式调用? [拍脑门] 难怪这么重……我要去运行这段代码……大约一个小时后我会带回一些结果
    • 我认为这是正确的,因为您正在实例化一个 .NET 对象,您应该能够设置一个变量以指向更下游的对象,从而减少您必须挖掘的次数通过第一个对象,特别是如果您要查找的只是对象的数量。
    • 您是否与我略有不同的解决方案进行了比较?我怀疑你会看到很多性能差异,但是获得一些关于将迭代存储在数组和字符串中的差异的指标会很有趣。
    • 结果大致相同...只有两个多值字段(memberOf 和 proxyAddresses),因此负载似乎分布得更好...我怀疑如果我刚刚检索到多值,它会更快(我最近看到数组在随机搜索值时只有性能提升)
    • 啊,那么您可能会通过在 ColCount 变量上使用开关并仅在值为 NEQ 0 时执行循环来获得更多优化。否则,只需执行 QuerySetCell 而不先转到字符串通过循环。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-07
    • 1970-01-01
    • 1970-01-01
    • 2020-02-28
    • 2011-02-09
    相关资源
    最近更新 更多