当过滤器仅包含一个字母 + 通配符时,slapd 慢到 return 结果

slapd slow to return results when filter consists of only one letter + wildcard

当在具有子字符串索引的字段上使用一个字母的过滤器和通配符 (*) 时,我的 OpenLDAP 实例 return 记录速度很慢。当过滤器有多个字母时,搜索速度非常快。我的问题是为什么?

场景为OpenLDAP v.2.4.44,LMDB后端,OS X,130万条记录,索引设置为:

olcDbIndex: objectClass eq
olcDbIndex: cn eq,sub

如果搜索过滤器为'(cn=s*)',查询时间较长,而如果过滤器为'(cn= sm*)' 速度非常快。 运行 slapindex 没有区别。

这里有一些数字,使用time:

$time ldapsearch -H ldap:/// -D cn=Manager,dc=xyz,dc=com -w secret -b dc=xyz,dc=com -s sub -z 5 '(cn=s*)' cn sn

-- some ldap records --

real    0m0.474s
user    0m0.001s
sys     0m0.002s

更改为 '(cn=sm*)' 得到:

real    0m0.012s
user    0m0.001s
sys     0m0.003s

'(cn=smi*)':

real    0m0.012s
user    0m0.001s
sys     0m0.003s

事实上,一旦过滤器具有多个字母和通配符,这些时间就会保持不变。

进一步调查后,我发现按字母顺序向下搜索会导致时间逐渐变长,但仅限于那个字母搜索。

使用 '(cn=a*)' 得到:

real    0m0.012s
user    0m0.001s
sys     0m0.003s

'(cn=b*)':

real    0m0.046s
user    0m0.001s
sys     0m0.004s

此时可以看到user和sys总是很快的,所以我以后就省略它们,只显示实时时间。因此:

'(cn=a*)' --> 0m0.012s
'(cn=b*)' --> 0m0.046s
'(cn=c*)' --> 0m0.105s
'(cn=d*)' --> 0m0.149s
'(cn=e*)' --> 0m0.174s
...
'(cn=m*)' --> 0m0.342s
...
'(cn=s*)' --> 0m0.523s
...
'(cn=z*)' --> 0m0.606s

但是如果这些搜索中的任何一个被赋予一个额外的字母,它就会恢复到快速(0.012s 是平均值)。所以出于某种原因,slapd 需要很长时间才能找到第一个字母,但前提是使用了一个字母。

经过进一步调查,过滤未索引的 'sn' 属性(例如 'sn=s*')产生与索引 'cn' 属性相同的时间,当使用一个字母 + 通配符过滤器。因此,slapd 在过滤一个字母 + 通配符时似乎忽略了 'cn' 属性上的子字符串索引。但是,添加第二个字母会使 'cn' 过滤器变快,而 'sn' 过滤器仍然很慢,这是可以预料的。

为什么会发生这种情况,如何解决?

发现此行为是设计使然。 OpenLDAP 有一个全局设置,用于控制子字符串索引的最小长度。在子字符串索引中索引的默认最小字符数是 2。

如果你使用在线配置(OLC),你会在man slapd-config中找到设置的描述:

olcIndexSubstrIfMinlen: <integer>
   Specify the minimum length for subinitial and subfinal indices. An attribute value must have at least this many characters in order to be processed by the indexing functions. The default is 2.

或者,如果你使用传统的配置方法,你会在man中找到它的描述 slapd.conf:

index_substr_if_minlen: <integer>
   Specify the minimum length for subinitial and subfinal indices. An attribute value must have at least this many characters in order to be processed by the indexing functions. The default is 2.

man 页面另外说:

Options described in this section apply to all backends, unless specifically overridden in a backend definition.

有谁知道是否可以在属性级别覆盖此行为?如果可以在比全局或每个后端更细粒度的级别指定子字符串的最小和最大长度,那就太好了。