我使用jQuery创建了一个DropDown自动完成菜单。 主要思想是,一旦将一个键插入文本框(最少3个字符),该函数就会使用GET方法向不同的页面发送请求(例如:search.php?q = iron man 3)和页面(搜索。 php)创建使用MySQL和PHP 5结果并显示它们。
好像在这里, 但由于我的网站中有很多用户(每天约10,000名用户),每次他们点击密钥都需要查询,这会在服务器上造成大量过载。 这会减慢服务器速度。
是否有更智能的方法来执行DropDown自动完成菜单?
谢谢。
答案 0 :(得分:3)
我假设您已经对您正在查询的表进行了规范化和简化。最好只查询一个具有名称字段的表,一些得分(例如流行度)用于排名结果(如果适用),可能是一个movie_id,用于链接到实际的电影数据或您查询的任何内容,也许是什么类型的数据(演员,电影等)的标识符。
您不必标准化所有内容,将此数据移动到单独的表中,并在每次要查看名称时运行联接。这可以只是数据的额外副本。您甚至可以使用通常不会使用的存储引擎,如内存或MyISAM(请参阅下面的文本搜索),以提高性能。
添加是在运行查询时添加一些条件。不要运行查询,直到他们停止输入一段时间(.5+秒左右,默认值为.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或Xapian仅举几例。
关于我看到的唯一真正的替代方法是将整个数据集推送给每个访问者,让客户端在javascript中自己处理查询和结果。对于HTML5应用程序,这可能有一些用途,但要确保数据被缓存。
答案 1 :(得分:2)
这取决于你所说的聪明。
您希望减少服务器负载,可以做一些事情:
设置较长的延迟。
这是您可以做的最简单的事情之一。 delay
参数可以减少查询次数:
$( ".selector" ).autocomplete({
delay: 500
});
默认值为300
毫秒,您可能需要设置更多。
设置minLength
另一件容易的事情是在查询开始之前设置需要插入的minimum length。将其设置为大于1的数字。根据您的口味调整它。
$( ".selector" ).autocomplete({
delay: 500, //from before
minLength: 3
});
优化您的查询。
你说这个是你应用程序中最耗费资源的部分。确保您的查询尽可能最佳。根据数据库,有最佳实践可供搜索。请务必仅查询最少量的所需数据。如果要为自动完成显示最多5个结果,则只查询5条记录。 (在MySQL中有LIMIT
,但我听说也可以是optimised。
<强> Indexing 强>
好的地方的索引可以大大提高查询的速度。例如,请查看this文章。
缓存表。
如果查询连接在一起的很多表,请考虑制作一个非常简单的表,其中只存储自动完成的可能值。这样您的查询就会简单得多。您需要事先填充它。
答案 2 :(得分:0)
我的原子防火墙将我的自动完成脚本标记为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);
}
};