System.DirectoryServices Search Performance – Part 2

NOTE: This post is part of a series.

Writing Efficient LDAP Filters

Writing efficient LDAP filters sometimes feels like more art than science.  Mostly that’s because the tools to make it a science are not always well known or understood.  We’ll tackle measuring the performance of your queries later in this series, but first let’s go over some basics of good filter design.

Use an Indexed Attribute

Whenever possible include an indexed attribute in your search.  Having an attribute that is indexed will quickly reduce the number of objects to search when filtering the non-indexed attributes.

Generally speaking only a single index will be used per query (more complicated queries may use multiple indexes).  The index that has the most uniqueness will be used.  So for example if you had two attributes, EmployeeID and EmployeeType, that are both indexed EmployeeID is probably more likely to be used as the indexed attribute because it will have less collisions than EmployeeType does.  Basically the LDAP server will try to do what it thinks is the most efficient.

Avoid Substring Searches

The default index is designed to handle wildcard characters at the end of a string relatively well.  For example doing a search for:

(givenName=steve*)

will give us reasonable performance.  However placing wildcard characters at the beginning or in the middle of strings causes significant performance issues.  For example:

(givenName=ste*en)

will produce undesirable performance issues.  Generally speaking writing a filter of:

(|

    (givenName=steve*)

    (givenName=stephen)

)

will produce better performance results.  Starting in Windows Server 2003 you can create medial or tuple index on attributes.  However these index’s are not as efficient as regular index’s.  We will cover creating index’s and testing the performance of your index’s in a future post in this series.

Avoid Using the NOT (!) Operator

The query processor will treat attributes you do not have permission to read and attributes with no value as matches when you use the NOT operator.  This can result in unnecessary objects being returned from you query.  Also the NOT operator prevents the use of indices on those attributes.

Avoid ANR Searches

Ambiguous Name Resolution (ANR) is a great feature introduced in Windows Server 2003 that helps us find a user when we know very little about them.  For example the LDAP filter:

(anr=doe)

Will be executed as:

(|

   (displayName=doe*)

   (givenName=doe*)

   (legacyExchangeDN=doe*)

   (msDS-AdditionalSamAccountName=doe*)

   (physicalDeliveryOfficeName=doe*)

   (proxyAddresses=doe*)

   (name=doe*)

   (sAMAccountName=doe*)

   (sn=doe*)

)

As you can see the anr searches 9 attributes (by default) for us.  Fortunately those attributes are usually indexed so simple ANR searches are not to bad.  However if you execute a lot of ANR searches in a short period of time, or you do multiple ANR searches in the same query (|(anr=doe)(anr=john)(anr=jane)) you can see how the problem can quickly get out of hand.

Avoid Bitwise Comparisons

Some attributes in Active Directory are a representation of bitwise flags.  The most common bitwise attribute is UserAccountControl.  Bit 0x2 in UserAccountControl signifies if the account is enabled or disabled.  So the filter:

(userAccountControl:1.2.840.113556.1.4.803:=2)

returns all accounts that are disabled.  (Replacing the 803 with 804 would return all accounts that are enabled)

A bitwise comparison of an attribute prevents the use of an index for that attribute.  When using a bitwise comparison try to include an index attribute in your query also.

Advertisements