Java 自动完成服务器端实现

Java 自动完成服务器端实现,java,memory,autocomplete,performance,Java,Memory,Autocomplete,Performance,为html输入框中的自动完成功能实现服务器端组件的快速有效方法是什么 我正在编写一个服务,在我们的web界面的主搜索框中自动完成用户查询,完成的内容显示在一个支持ajax的下拉列表中。我们运行查询所针对的数据只是我们的系统知道的一个大概念表,它与维基百科页面标题集大致匹配。对于这项服务,速度显然是最重要的,因为网页的响应性对用户体验很重要 当前的实现只是将所有概念加载到排序集中的内存中,并对用户击键执行简单的日志(n)查找。然后,尾集用于提供最接近匹配之外的其他匹配。这个解决方案的问题是它不能扩

为html输入框中的自动完成功能实现服务器端组件的快速有效方法是什么

我正在编写一个服务,在我们的web界面的主搜索框中自动完成用户查询,完成的内容显示在一个支持ajax的下拉列表中。我们运行查询所针对的数据只是我们的系统知道的一个大概念表,它与维基百科页面标题集大致匹配。对于这项服务,速度显然是最重要的,因为网页的响应性对用户体验很重要

当前的实现只是将所有概念加载到排序集中的内存中,并对用户击键执行简单的日志(n)查找。然后,尾集用于提供最接近匹配之外的其他匹配。这个解决方案的问题是它不能扩展。它目前运行时遇到了VM堆空间限制(我已经设置了-Xmx2g,这大约是我们在32位机器上可以推动的最大值),这阻止了我们扩展概念表或添加更多功能。在内存更多的机器上切换到64位虚拟机并不是一个立即的选择

我一直在犹豫是否开始开发基于磁盘的解决方案,因为我担心磁盘寻道时间会降低性能。是否有可能的解决方案可以让我更好地扩展,或者完全在内存中,或者使用一些快速的磁盘备份实现

编辑:

@甘道夫:对于我们的用例来说,重要的是自动完成是全面的,而不仅仅是对用户的额外帮助。至于我们正在完成的内容,它是一个概念类型对的列表。例如,可能的条目有[(“微软”、“软件公司”)、(“杰夫·阿特伍德”、“程序员”)、(“StackOverflow.com”、“网站”)]。一旦用户从自动完成列表中选择一个项目,我们就使用Lucene进行完整搜索,但我还不确定Lucene是否能很好地用于自动完成本身

@格伦:这里没有使用数据库。当我谈论表时,我指的是数据的结构化表示


@Jason Day:我对这个问题的最初实现是使用,但是由于需要大量的对象引用,使用它的内存膨胀实际上比排序集更糟糕。我将阅读三元搜索树,看看它是否有用。

对于这么大的一个集合,我会尝试使用Lucene索引之类的方法来查找所需的术语,并设置一个计时器任务,在每次按键后重置,延迟0.5秒。这样,如果用户快速键入多个字符,它不会在每个笔划中查询索引,只有在用户暂停一秒钟时才会查询索引。可用性测试将让您知道暂停的时间

Timer findQuery = new Timer();
...
public void keyStrokeDetected(..) {
   findQuery.cancel();
   findQuery = new Timer();
   String text = widget.getEnteredText();
   final TimerTask task = new TimerTask() {
      public void run() {
         ...query Lucene Index for matches
      }
   };
   findQuery.schedule(task, 350); //350 ms delay
}

那里有一些psedoocode,但这就是想法。此外,如果设置了查询条件,则可以预先创建和优化Lucene索引。

如果无法将所有数据物理加载到RAM中,则必须处理一些磁盘上的数据

你用的是什么数据库

例如,Oracle有一个选项,您可以将整个表保留在内存中,并根据该选项执行查询

MySQL也声称有一些内存功能,但我对MySQL了解不多

然后,您可以删除基于java的缓存,也可以将缓存用于最流行/最新的搜索

显然,当RAM用完时,当您查询时,一些数据将出现在磁盘上,但取决于系统上的负载,这只会是第一次按键的问题,而不会是后续按键的问题,因为之后该行将在内存中


如果磁盘搜索使您的速度变慢,那么您可以研究使用SSD驱动器来加快读取速度。

我也有类似的要求

我使用关系数据库和一个索引良好的合成表(避免连接和视图以加快查找),并使用内存缓存(Ehcache)存储大多数使用的条目

通过使用MRU缓存,您将能够在大多数查找中获得即时响应时间,并且在访问存储在磁盘上的大表中的索引列时,可能没有什么比关系数据库更好的了

这是一个解决方案,适用于无法存储在客户机上的大型数据集,而且运行速度非常快(在我的例子中,非缓存查找总是在0.5秒内检索)。它还具有水平可扩展性—您可以随时添加其他服务器和数据库服务器

您还可以只缓存客户端上使用最多的结果,特别是如果您已经实现了它的话。在我的例子中,服务器端解决方案足够快,客户端加载时间也足够慢,所以不保证


另外,只有当用户暂停一定时间以避免重复查找时,才进行客户端查询是一个很好的解决方案。在我的客户机上,我只在输入前三个字符后才查询数据库,因为小于这三个字符会在所有情况下返回太多的结果。

也许我误解了你的问题,但你不能使用JQuery插件将信息Ajax到你的应用程序中吗

我以前用过这个:


我已经使用。DDJ代码不太难转换为Java,但它假定整个数据集都可以放入内存。有三元搜索树的磁盘实现(在python中是一种),但它们的性能当然会降低。由于三元搜索树擅长部分匹配,因此性能可能适合您的需要

有没有可能的解决办法 让我来放大

是的,甲骨文。这是建立数据库的目的。只需索引相关列。如果您在运行内存解决方案,那么与磁盘寻道时间或网络延迟的权衡可能是没有意义的。尤其是在中间插入缓存层时

此外,如果您选择
Directory directory = FSDirectory.getDirectory(indexDir);
IndexReader reader = IndexReader.open(directory);
FilterIndexReader filteredReader = new FilterIndexReader(reader) {
  @Override public TermEnum terms(Term t) throws IOException {
    final TermEnum origEnum = super.terms(t);

    return new TermEnum() {
      protected int count = 0;
      @Override public boolean next() throws IOException {
        if (count++ < (BooleanQuery.getMaxClauseCount() - 10))
          return origEnum.next();
        else return false;
      }

      @Override public Term term() {
        return origEnum.term();
      }

      @Override public int docFreq() {
        return origEnum.docFreq();
      }

      @Override public void close() throws IOException {
        origEnum.close();
      }
    };
  }
};

IndexSearcher searcher = new IndexSearcher(filteredReader);