IIWAB 【ES】警惕ES的通配符查询【转摘】 - IIWAB

【ES】警惕ES的通配符查询【转摘】

IIWAB 1年前 ⋅ 11953 阅读

警惕ES的通配符查询

为了加速通配符和正则表达式的匹配速度,Lucene4.0开始会将输入的字符串模式构建成一个DFA (Deterministic Finite Automaton),带有通配符的pattern构造出来的DFA可能会很复杂,开销很大。

Lucene构造DFA的实现
看了一下Lucene的里相关的代码,构建过程大致如下:

  1. org.apache.lucene.search.WildcardQuery里的toAutomaton方法,遍历输入的通配符pattern,将每个字符变成一个自动机(automaton),然后将每个字符的自动机链接起来生成一个新的自动机。
  2. 此时生成的状态机是不确定状态机,也就是Non-deterministic Finite Automaton(NFA)。
  3. org.apache.lucene.util.automaton.Operations类里的determinize方法则会将NFA转换为DFA。 代码注释里说这个过程的时间复杂度最差情况下是状态数量的指数级别!为防止产生的状态过多,消耗过多的内存和CPU,类里面对最大状态数量做了限制。
/**
   * Determinizes the given automaton.
   * <p>
   * Worst case complexity: exponential in number of states.
   * @param maxDeterminizedStates Maximum number of states created when
   *   determinizing.  Higher numbers allow this operation to consume more
   *   memory but allow more complex automatons.  Use
   *   DEFAULT_MAX_DETERMINIZED_STATES as a decent default if you don't know
   *   how many to allow.
   * @throws TooComplexToDeterminizeException if determinizing a creates an
   *   automaton with more than maxDeterminizedStates
   */
  public static Automaton determinize(Automaton a, int maxDeterminizedStates)

NFA在输入一个条件的情况下,可以从一个状态转移到多种状态,而DFA只会有一个确定的状态可以转移,因此DFA在字符串匹配时速度更快。 DFA虽然搜索的时候快,但是构造方面的时间复杂度可能比较高,特别是带有首部通配符+长字符串的时候。

总结: wildcard query应杜绝使用通配符打头,实在不得已要这么做,就一定需要限制用户输入的字符串长度。 最好换一种实现方式,通过在index time做文章,选用合适的分词器,比如nGram tokenizer预处理数据,然后使用更廉价的term query来实现同等的模糊搜索功能。 对于部分输入即提示的应用场景,可以考虑优先使用completion suggester, phrase/term suggeter一类性能更好,模糊程度略差的方式查询,待suggester没有匹配结果的时候,再fall back到更模糊但性能较差的wildcard, regex, fuzzy一类的查询。


全部评论: 0

    我有话说: