如何在 ColdFusion 中加速从 .NET AD 检索数据?
-
01-07-2019 - |
题
如何优化以下代码,目前需要 2 分钟以上的时间从超过 100K 记录的池中检索和循环 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>
解决方案
内循环的项目列表有多大?
切换到数组 可能 如果有大量项目,速度会更快。
我已经按照 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>
其他提示
好久没接触CF了,不过可以用伪代码给出一些提示。一方面,这个表达式的效率极低:
#theObject.Get_Item(row).Get_Properties().Get_Item(col).Get_Count()-1#
以第一部分为例,Get_Item(row) - 您的代码会导致 CF 为 #columnList# 循环的每次迭代检索行及其属性;最重要的是,每次列列表迭代您都会执行两次(一次 for 循环,另一次针对内部 cfset)。如果您考虑一下,它只需要检索外循环每次迭代的行(从#sfstart#到#cfend)。因此,在伪代码中执行以下操作:
对于开始和结束之间的每一行
cfset props = #theobject.get_item(row).get_properties()#
对于#columnlist#中的每个列
cfset currentcol = #props.getitem(col)#
cfset 计数 = #currentcol.getcount() - 1#
foreach 项目从 0 到 #count#
cfset #currentcol.getItem(item)# 等...
合理?每次进入循环时,都会将在该作用域(或子作用域)中重用的对象缓存在变量中。这意味着每次列循环迭代时您只获取一次列对象。外部作用域中定义的所有变量都可以在内部作用域中使用,正如您在上面所做的操作中看到的那样。我知道从以前的行中剪切和粘贴很诱人,但不要这样做。最后只会伤害你。
希望这可以帮助,
奥辛
此外,在每个循环中使用 cftry 块可能会大大减慢速度。除非您预计个别行会失败(并且您需要从该点继续),否则我建议整个过程使用单个 try/catch 块。Try/catch 是一项昂贵的操作。
我认为您希望停止在循环内进行如此多的评估,而是使用变量来保存计数、指向 col 对象的指针并保存管道 delim 字符串,直到您准备好提交查询对象为止。如果我正确地完成了重构,那么使用以下代码您应该会注意到改进:
<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>