django查询优化——啥时候用sql,啥时候用python

Posted

技术标签:

【中文标题】django查询优化——啥时候用sql,啥时候用python【英文标题】:django query optimization - when to use sql and when to use pythondjango查询优化——什么时候用sql,什么时候用python 【发布时间】:2014-10-29 15:32:15 【问题描述】:

我正在寻找一个通用的经验法则,什么时候重新查询数据库更快,什么时候使用 python 从缓存中提取数据更快。

假设我需要同时从数据库中提取两件事:所有比萨饼和 pk=5 的特定比萨饼。

什么更优化:

pizzas = Pizza.objects.all()   
specific_pizza = Piazza.objects.get(pk=5)

pizzas = Pizza.objects.all()
for pizza in pizzas:
    if pizza.pk == 5
        specific_pizza = pizza
        break 

当然,这取决于数据库。比如pizza是1000万行,很明显重新查询sql比较好,如果pizza是10行,即使字段被索引,python也可能更快。

任何人都可以帮助在中间范围内进行更优化吗?比如比萨饼是几百行?数千行?

【问题讨论】:

【参考方案1】:

这个问题没有明确的答案 - 正如您所说,它取决于数据库(可能还取决于它的位置、表的数量和大小......)。您必须在您的特定环境中进行测试。

除了原始速度之外,使用第一个版本还有一些重要的优势:

更短更清晰 ORM 确切地知道您想要什么,因此可以在该级别进行任何进一步的优化,而不是将它们推送到您的应用程序 它避免了在您的网络服务器中进行(潜在的)密集计算

另外,值得深思的是:如果您的表足够小,python 比 DB 快,那么速度是否重要?

您可能想了解过早优化

【讨论】:

(1) 只要我知道何时使用 python 和 sql 的经验法则,提前编写首选选项并不需要我付出任何代价。 (2) 我没有太多的 sql 经验,但根据经验,每页超过 20 个查询可能会很多。 (3) 全部放在一起——数据规范化,没有python的情况下我所有的查询都使用sql,可以引导我进行20多个查询。 @ErezO 除非您确定行数保持在非常低的限制内,否则您不可能“提前写”。如果您确实知道会有固定的低行数,那么 SQL 可能不是最适合您的应用程序的。另请注意,在执行Pizza.objects.get(pk=5) 时不会有 20 个查询 - 只有一个。即使您需要更复杂的操作(例如连接多个表),您也应该能够只用一个查询来完成大多数事情——这就是关系数据库的全部意义所在,将此类操作推送到服务器 谢谢@goncalopp ch3ka。我认为不可能将其减少为一个查询,这就是我问这个问题的原因。在我需要根据同一页面上的 几个 过滤器(例如 all_pizzas、pk=5、topping=mushrooms 等)显示比萨饼的情况下,我不知道有什么方法可以可以在一个查询中完成(如果您这样做,请告诉我)。因此,问题是什么时候最好使用 python。你和 ch3ka 基本上都说经验法则应该是始终使用 SQL,而永远不要使用 python。 @ErezO 如果你有时间,你应该彻底阅读 Django 的文档Making Queries。我想querysets are lazy 部分回答了你的问题。请注意,您可以使用 print myqueryset.query 打印查询集表示的 SQL @goncalopp 相信我,我已经从头到尾读过很多遍了。我的意思是需要同时将多个过滤器呈现到一个 html 页面中(例如“all_pizzas”和“pizzas_with_mushrooms”和“pizza_no_5”)。在这种情况下,我只看到两种解决方案——对每个查询使用 sql,或者只使用一次 sql,其余的使用 python。这就是为什么我问何时使用 python 的原始问题。顺便说一句,我没有接受答案,因为我认为它不正确。如果是这样,Django 就不会费心改进 prefetch_related 了。【参考方案2】:

例如,如果披萨有 1000 万行,很明显 重新查询 sql 更好,如果披萨是 10 行,即使 字段被索引,python可能更快。

嗯...第一个陈述:是的。第二个说法:不确定,但也不重要。 因为当只有几个比萨饼时,下一条命令会花费相当长的时间。

任何人都可以帮助在中间范围内更优化吗?

我猜,不像你预期的那样,但是是的:因为我们同意当有很多比萨饼时使用.get() 会更快,并且因为我们看到只有当有很多比萨饼时性能才是一个问题,考虑到这个事实未来披萨的数量可能会增加,我认为我们可以同意使用.get() 是正确的做法。

抛开性能不谈 - 它显然也更具可读性,所以你真的应该走这条路。

另外,请注意,您可以使用 QuerySet 上的方法(.all() 返回 QuerySet!)来过滤您想要的内容。它的工作原理是“幕后的魔力”——因此假设是优化的,直到找到反对该假设的证据。所以你应该使用这些方法,直到你真正需要有针对性的优化。而且如果您曾经达到过这一点,您可以进行基准测试并获得可靠的答案。

【讨论】:

thx @ch3ka,我评论了 goncalopp 的回答,但它是为你们俩准备的。【参考方案3】:

我很欣赏@ch3ka 和@goncalopp 的回复,但我认为他们没有直接回答这个问题,所以这是我对自己的一些描述:

底线,我发现 python 查找的点甚至与 sql 的点一样,大约是 1000 个条目:

假设我已经查询了数据库并收到了 1000 个披萨:

pizzas = Pizza.objects.all()

我做了两个测试:

测试1: 通过查看 pk,在 1000 个比萨饼中找到一个特定的比萨饼:

for pizza in pizzas:
    if pizza.pk == 500
        specific_pizza = pizza
        break 

耗时 0.2 毫秒

测试2: 根据比萨的成员过滤,并创建一个新列表:

mushroom_pizzas=[pizza for pizza in pizzas if pizza.topping==Pizza.MUSHROOM]

其中 MUSHROOM 是可能的浇头的枚举。我选择了枚举,因为我认为它与索引数据库字段的比较是正确的

耗时 0.3 毫秒

使用 Django 调试工具栏,一个简单的索引 sql 查询所需的时间约为 0.3 毫秒。

我确实像@goncalopp 和@ch3ka 一样认为,由于简单的索引查询已经是 0.3 毫秒,因此使用 python 进行优化真的没有意义。所以即使我事先知道条目的数量会少于1000,甚至远少于1000,我仍然会一直使用sql。

如果我计算错误或得出错误的结论,我将不胜感激。

【讨论】:

以上是关于django查询优化——啥时候用sql,啥时候用python的主要内容,如果未能解决你的问题,请参考以下文章

sql语句中嵌套时候用in 和=有啥区别

sql语句中啥时候用单引号啥时候用双引号?如图中的红为啥用双引号?

mysql啥时候使用子查询,啥时候使用表连接查询,关系多张表的时候该怎么查询

asp中sql语句中啥时候加单引号,啥时候加双引号

c#sqlserver在啥情况下使用到groupby进行分组查询

mysql+php程序中sql语句中的引号使用方法,啥时候用双引号啥时候用单引号