使用通配符的Active directory查询性能较差



我正在C#中编写一个方法,该方法应该查询Active Directory,并查找显示名称格式为{displayName}的所有用户和组(包含前导和尾随通配符的通配符搜索),该方法将用于自动完成字段。

问题是我编写的方法的性能非常差,尝试查询AD需要30秒到一分钟的时间,具体取决于查询字符串。

我的组织的AD非常大,但如果花这么长时间,自动完成字段将毫无意义。

这是我现在使用的代码:

// Intialize the results list.
result.queryResult = new List<Classses.ADSearchObject>();
// Set up domain context.
PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain, Constants.adQueryUser, Constants.adQueryPassword);
// Set up a directory searcher.
DirectorySearcher dSearcher = new DirectorySearcher();
// Define a SearchCollection to store the results.
SearchResultsCollection searchCol;
// Define returned result paging for performance.
dSearcher.PageSize = 1000;
// Define the properties to retrieve
dSearcher.PropertiesToLoad.Add("sAMAccountName");
dSearcher.PropertiesToLoad.Add("displayName");
// Define the filter for users.
dSearcher.Filter = $"(|(&(displayName = {result.querystring}*)(objectCategory=person))(&(displayName=*{result.querystring})(objectCategory=person)))";
// Search based in filter and save the results.
searchCol = dSearcher.FindAll();
// Add the results to the returned object 
foreach (SearchResult searchResult in searchCol)
{
DirectoryEntry de = searchResult.GetDirectoryEntry();
// Code to get data from the results...
}
// Define the filter for groups.
dSearcher.Filter = $"(|(&(displayName={result.querystring}*)(objectCategory=person))(&(displayName=*{result.querystring})(objectCategory=person)))";
// Search based in filter and save the results.
searchCol = dSearcher.FindAll();
// Add the results to the returned object 
foreach (SearchResult searchResult in searchCol)
{
DirectoryEntry de = searchResult.GetDirectoryEntry();
// Code to get data from the results...
}

目前,搜索被划分为用户和组,以便于区分它们,但如果它能显著提高性能,我会将它们统一为一个搜索。

编辑:正如用户rene所建议的,我使用Stopwatch来检查FindAll所需的时间,还检查了foreach循环所需的长度。

我发现FindAll调用大约需要100ms(非常快),即使在使用AD索引的前导通配符(不是)进行搜索时也是如此。

显然,耗时最长的呼叫是我的foreach循环,大约需要40秒(40000ms)。

我正在用foreach循环中的代码块更新这个问题,因为我还没有弄清楚如何提高它的性能:

// --- I started a stopwatch here
foreach (SearchResult searchResult in searchCol)
{
// --- I stopped the stopwatch here and noticed it takes about 30,000ms
result.code = 0;
DirectoryEntry de = searchResult.GetDirectoryEntry();
ADSearchObject adObj = new ADSearchObject();
adObj.code = 0;
if (de.Properties.Contains("displayName")
{
adObj.displayName = de.Properties["displayName"].Value.ToString();
}
adObj.type = "user";
result.queryResults.Add(adObj);
}

请注意我在更新的代码中从哪里开始和停止"秒表",我不知道为什么开始循环需要这么长时间。

当然,子字符串匹配比唯一值的相等匹配代价更高。此外,也不奇怪,大部分的运行时间都落在了迭代器块中,根据您的分析,它总共消耗了40秒。

如果你确信仅仅通过设置迭代器就会出现性能的大幅下降,我不是——这是因为你选择了时间点。

StartClock("foreach");
foreach (SearchResult searchResult in searchCol)
{
// use an empty block to speed things up or
StopClock("foreach");
// whatever
RestartClock("foreach");
}
StopClock("foreach");
LogClock("foreach");

如果你注意我已经评论过的一个最佳实践,我预计(对于大的条目数量)会有巨大的性能提升:向服务器发送一个请求,在搜索结果中收到你需要的所有信息,而不要为每个条目发送另一个请求。而对GetDirectoryEntry()的单个调用将只消耗<1ms,大量的条目将使您的代码对应用程序自动完成功能毫无用处。

感谢@rene为该过滤表达式提供了一个正常形式。我不知道Active Directory中的过滤器优化,所以我会选择

(&(objectCategory=person)(displayName=*{result.querystring}*))

最新更新