使用 Django 通过远程数据提高 Twitter 的 typeahead.js 性能
Posted
技术标签:
【中文标题】使用 Django 通过远程数据提高 Twitter 的 typeahead.js 性能【英文标题】:Improving Twitter's typeahead.js performance with remote data using Django 【发布时间】:2013-10-26 17:32:30 【问题描述】:我有一个包含大约 120 万个名字的数据库。当您键入某人的姓名时,我正在使用 Twitter 的 typeahead.js 远程获取自动完成建议。在我的本地环境中,在您停止输入后大约需要 1-2 秒才能显示结果(在您输入时自动完成不会出现),并且在 Heroku 上部署的应用程序上需要 2-5 秒以上(仅使用 1 dyno )。
我想知道它仅在您停止输入(并延迟几秒钟)后才显示建议的原因是否是因为我的代码没有优化?
页面上的脚本:
<script type="text/javascript">
$(document).ready(function()
$("#navPersonSearch").typeahead(
name: 'people',
remote: 'name_autocomplete/?q=%QUERY'
)
.keydown(function(e)
if (e.keyCode === 13)
$("form").trigger('submit');
);
);
</script>
keydown sn-p 是因为没有它我的表单在按下回车时由于某种原因无法提交。
我的 Django 视图:
def name_autocomplete(request):
query = request.GET.get('q','')
if(len(query) > 0):
results = Person.objects.filter(short__istartswith=query)
result_list = []
for item in results:
result_list.append(item.short)
else:
result_list = []
response_text = json.dumps(result_list, separators=(',',':'))
return HttpResponse(response_text, content_type="application/json")
我的 Person 模型中的短字段也被索引。有没有办法提高我的预输入性能?
【问题讨论】:
您能否使用浏览器的分析工具(Chrome 开发者工具或 Firebug)来查看 a) 您的请求何时发出以及 b) 它们花费了多长时间? 是的......就像我说的我的预输入请求的本地版本大约需要 1-2 秒。在 Heroku 上,它至少需要 2 秒,最长可达 8-9 秒。与较短的请求相比,较长的请求具有非常长的“等待”时间。 【参考方案1】:我认为这与 Django 没有直接关系,但我可能错了。对于这种情况,我可以提供一些通用的建议:
(我的钱在下面的 #4 或 #5)。
1) 从您的机器到 Heroku 的平均“ping”是多少?如果它很远,那就有点额外的开销。不过不多。与您所指的 8-9 秒相比,当然不多。请注意,https
的惩罚会更大。
2) 检查remote
数据集中waitLimitFn
和rateLimitWait
的值。它们是默认的吗?
3) 问题很可能与数据库/数据集有关。首先要检查的是您建立与数据库的连接需要多长时间(您是否使用连接池?)。
4) 第二件事:运行查询需要多长时间。我的赌注是在这一点或下一个。添加调试打印,或使用 NewRelic(即使是免费计划也可以)。查看生成的查询并确保它已编入索引。让您的数据库“解释”此类查询的执行计划并使其使用索引。
5) 第三件事:结果大吗?例如,如果您指定“J”作为查询,我想会有很多答案。只是获取它们并将它们流式传输到客户端将需要时间。在这种情况下:
5.1) 为您的数据集指定 minLength
。至少 3 个,如果不是 4 个。
5.2) 限制您的数据库查询返回的结果集。让它返回不超过 10,比如说。
6) 我不是 Django 专家,但请确保您在 Django 中使用模型的方式不会使其首先将整个表加载到内存中。只是说说而已。
HTH。
【讨论】:
对于您的点 1-2,ping 和值是正常的。 3. 我正在使用 heroku 来处理与数据库的连接。 4. 我不熟悉测试这些东西,所以我会调查一下。 5. 是的,我最近限制了输入及其返回的大小,它仍然很慢。谢谢你的意见,我会继续努力的。【参考方案2】: results = Person.objects.filter(short__istartswith=query)
result_list = []
for item in results:
result_list.append(item.short)
可能不是您运行缓慢的唯一原因,但从性能的角度来看这是可怕的:永远不要循环遍历 django 查询集。要从 django 查询集中组装一个列表,您应该始终使用 values_list。在这种特定情况下:
results = Person.objects.filter(short__istartswith=query)
result_list = results.values_list('short', flat=True)
通过这种方式,您可以直接从数据库中获取所需的单个字段,而不是:获取所有表行,从中创建一个 Person 实例,最后从中读取单个属性。
【讨论】:
【参考方案3】:Nitzan 涵盖了许多可以提高性能的要点,但与他不同的是,我认为这可能与 Django 直接相关(至少,服务器端)。
测试这一点的一种快速方法是更新您的name_autocomplete
方法,以简单地返回10 个随机生成的字符串,这些字符串采用Typeahead 期望的格式。 (我们希望它们随机的原因是 Typeahead 的缓存不会扭曲任何结果)。
我怀疑您会看到 Typeahead 现在运行得非常快,您应该会在输入 minLength
字符串后立即看到结果。
如果是这种情况,那么我们将需要研究可能会减慢查询速度的原因,我的 Python 技能不存在,所以我无法为您提供帮助,抱歉!
如果不是这样,那么我可能会考虑记录 $('#navPersonSearch')
何时调用 typeahead:initialized
和 typeahead:opened
以查看它们是否会带来任何奇怪的东西。
【讨论】:
【参考方案4】:您可以使用django haystack,您的服务器端代码大致如下:
def autocomplete(request):
sqs = SearchQuerySet().filter(content_auto=request.GET.get('q', ''))[:5] # or how many names you need
suggestions = [result.first_name for result in sqs]
# you have to configure typeahead how to process returned data, this is a simple example
data = json.dumps('q': suggestions)
return HttpResponse(data, content_type='application/json')
【讨论】:
调试一个类似的问题,我发现了一个bug。在第 3 行,应该是:suggests = [result.object.first_name for result in sqs]以上是关于使用 Django 通过远程数据提高 Twitter 的 typeahead.js 性能的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法通过 ajax 提高正常的 Django 表单验证?
django cloudinary 通过 url 上传远程图像 - 文件名无效