jQuery 自动完成菜单阻塞了服务器

Posted

技术标签:

【中文标题】jQuery 自动完成菜单阻塞了服务器【英文标题】:jQuery autocomplete menu is jamming the server 【发布时间】:2013-04-30 10:13:57 【问题描述】:

我使用 jQuery 创建了一个 DropDown 自动完成菜单。 主要思想是,一旦将密钥插入文本框(最少 3 个字符),该函数就会使用 GET 方法向不同的页面(比如说:search.php?q=iron man 3)和页面(search.php?q=iron man 3)发送请求。 php) 使用 mysql 和 PHP 5 创建结果并显示它们。

到这里为止似乎还可以, 但是由于我的网站中有很多用户(每天大约 10,000 名用户),每次他们点击一个键都需要查询,这会在服务器上造成大量过载。 这会降低服务器速度。

有没有更聪明的方法来做下拉自动完成菜单?

谢谢。

【问题讨论】:

我要研究的第一件事是分析/优化您的搜索查询(也许是处理它的 PHP)。您能否简要介绍一下服务器端的工作原理并粘贴您正在使用的查询? 请求的表、查询和索引是什么样的? 【参考方案1】:

简化查询

我假设您已经对正在查询的表进行了规范化和精简。最好只查询一个表,该表有一个名称字段、一些分数(例如流行度)用于排名结果(如果适用)、可能是一个 movie_id 以将您链接到实际电影数据或您正在查询的任何内容,也许还有一个什么类型的数据(演员、电影等)的标识符。

您不必对所有内容进行规范化,将这些数据移动到单独的表中,并在每次想要查看名称时运行连接。这可能只是数据的额外副本。您甚至可以使用通常不会使用的存储引擎,例如内存或 MyISAM(请参阅下面的文本搜索)来提高性能。

减少查询流量

添加是在运行查询时添加一些条件。不要运行查询,直到他们在短时间内停止输入(0.5+ 秒左右与默认的 0.3 秒)。这将是您提高服务器稳定性的最简单、最快捷的方法,但会牺牲响应性,因为它们必须停止更长时间。您也可以尝试避免客户端同时运行多个查询,这对读者来说是一个练习,但是如果有适当的延迟,这可能是无用的。如果您是从 Google 来到这里的,您可以在 http://docs.jquery.com/UI/API/1.8/Autocomplete 查看相应的 jquery 自动完成文档。

$( ".selector" ).autocomplete(
    minLength: 3, // already set
    delay: 500
);

使用文本搜索引擎

另一个改进是使用文本搜索引擎。 MySQL 使用 MyISAM 和 InnoDB(5.6 中的新功能)引擎进行了一些全文搜索,但 MyISAM 有一些权衡,并且 InnoDB 全文可能无法提供生产就绪的结果 (as shown here)。幸运的是,在 MySQL 中切换表类型相当容易,并且拥有一个仅包含名称副本的小表应该可以缓解任何问题。有些数据库让您自己重建所有内容,以进行更复杂的 DBA 操作。

如果您的网站变得足够繁忙,那么一个专门的搜索引擎可能是个好主意,例如 Lucene/CLucene(一个 c++ 端口)、Solr(Lucene 子项目)、Sphinx 或 @987654327 @ 仅举几例。

替代方法

关于我看到的唯一真正的替代方法是将整个数据集推送给每个访问者,并让客户端在 javascript 中自行处理查询和结果。对于 html5 应用程序,这可能有一些用途,但请确保数据被缓存。

【讨论】:

【参考方案2】:

这取决于你所说的聪明。

你想减少你的服务器负载,可以做几件事:

设置更长的延迟时间。

这是您可以做的最简单的事情之一。通过delay参数可以减少查询次数:

$( ".selector" ).autocomplete(
    delay: 500 
);

默认为300 毫秒,您可能需要设置更多。

设置最小长度

另一件简单的事情是设置一个minimum length,它需要在查询开始之前插入。将其设置为大于一的数字。根据你的口味调整它。

$( ".selector" ).autocomplete(
    delay: 500, //from before
    minLength: 3
);

优化您的查询。

你说这个是你的应用程序中最消耗资源的部分。确保您的查询尽可能优化。根据数据库,可以搜索最佳实践。确保只查询最少量的所需数据。如果要为自动完成显示最多 5 个结果,只查询 5 条记录。 (在 MySQL 中有 LIMIT,但我听说也可以是 optimised)。

Indexing.

在好地方的索引可以大大提高您的查询速度。例如查看this 文章。

缓存表。

如果您查询许多连接在一起的表,请考虑创建一个非常简单的表,其中您只存储自动完成的可能值。这样你的查询会简单得多。不过,您需要事先填充它。

【讨论】:

答案不如@jbo5112 的完整,但在资源有限的情况下更容易实施 是的,他大大改进了他的初始答案,难怪它被接受了【参考方案3】:

我的原子防火墙将我的自动完成脚本标记为 DDOS 攻击 我进入了 jquery 自动完成脚本并对其进行了更改 1) 只查找 3 个字母然后创建一个列表 2) 仅在未发送查询时查询 这可能不是最好的方法,但它阻止了所有防火墙问题,并降低了我的服务器负载,不会发出高负载警告,可能会减少 80%

function request(term, success, failure) 
    if (!options.matchCase)
        term = term.toLowerCase();
    var data = cache.load(term);
    // recieve the cached data
    if (data && data.length) 
        success(term, data);
    // if an AJAX url has been supplied, try loading the data now
     else if( (typeof options.url == "string") && (options.url.length > 0) )

        var extraParams = 
            timestamp: +new Date()
        ;
        $.each(options.extraParams, function(key, param) 
            extraParams[key] = typeof param == "function" ? param() : param;
        );
        // added by gary - the ajax call is made at 3 letters and only 1 call is made to avoid swamping the server with ajax requests


        if(term.length>'3')
        stopLoading();
        
        if(term.length=='3')  // added by gary
        var calls=$("#AUTOCOMPLETECALLS").val();
        if(calls==0) //by pass if a call already made
        $.ajax(
            // try to leverage ajaxQueue plugin to abort previous requests
            mode: "abort",
            // limit abortion to this input
            port: "autocomplete" + input.name,
            dataType: options.dataType,
            url: options.url,
            data: $.extend(
                q: lastWord(term),
                limit: options.max
            , extraParams),
            success: function(data) 

                var parsed = options.parse && options.parse(data) || parse(data);
                cache.add(term, parsed);
                success(term, parsed);
                var numCalls=$("#AUTOCOMPLETECALLS").val();
                numCalls++;
                $("#AUTOCOMPLETECALLS").val(numCalls);
                var idData = $(input).attr('id');
                if(idData=='txtText')
                var SplitData=data.split("|");
                if(SplitData[1]==undefined)
                    if(document.getElementById('donor_id') != null)
                    
                        $("#donor_id").val('0');
                        $("#NewRecepientWarning").show();
                       
                 

             
        );
         // eof calls
         else                          //eof term.length
            // no match is selected the user just keeps typing
            hideResultsNow();
            var idData = $(input).attr('id');
            if(idData=='txtText')
            if(document.getElementById('donor_id') != null)
            
                $("#donor_id").val('0');
                $("#NewRecepientWarning").show();
             

        
     else 
        // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
        select.emptyList();
        failure(term);

    
;    

【讨论】:

以上是关于jQuery 自动完成菜单阻塞了服务器的主要内容,如果未能解决你的问题,请参考以下文章

自动完成下拉菜单未正确显示

如何使用 KnockoutJS 和 JQuery UI 创建自动完成组合框

每个单词下方的jQuery-ui自动完成下拉菜单

jQuery UI 自动完成 - 没有结果消息

jQuery 自动完成:事件选择

Rails 3.2.1 Jquery 自动完成