3

所以我已经构建 django 应用程序有一段时间了,并且喝了很酷的东西:只使用 ORM,从不编写自定义 SQL。

一旦您拥有大量用户特定内容(即照片、朋友、其他数据等),网站的主页(用户将花费 80% - 90% 时间的主界面)会变慢

所以我弹出了 sql 记录器(预装了 pinax,我只是在设置中启用了它)并想象当它报告超过500 个数据库查询时我的惊讶!使用手工编码的 sql,我几乎不会在最复杂的页面上运行超过 50 个。

事后看来,这并不令人惊讶,但这似乎不是一件好事。

...即使只有十几个查询需要 1ms+

所以我想知道,往返 mysql 有多少开销?django 和 mysql 在同一台服务器上运行,因此不应该有任何与网络相关的开销。

4

4 回答 4

4

仅仅因为您正在使用 ORM 并不意味着您不应该进行性能调整。

我有 - 像你一样 - 我的一个性能低下的应用程序的主页。我看到我正在执行数百个查询来显示该页面。我查看了我的代码,并意识到仔细使用select_related()我的查询会带来更多我需要的数据——我的查询从数百个增加到了数十个。

您还可以运行 SQL 分析器并查看是否没有索引可以帮助您最常见的查询 - 您知道,标准数据库的东西。

缓存也是你的朋友,我想。如果很多页面没有变化,是否需要每次都查询数据库?

如果一切都失败了,请记住:ORM 很棒,是的 - 你应该尝试使用它,因为它是 Django 哲学;但你没有嫁给它

如果您确实有一个用例,其中研究和调整 ORM 导航没有帮助,如果您确定使用标准查询可以做得更好:在这种情况下使用原始 sql。

于 2009-11-06T18:34:27.210 回答
3

每个查询的开销只是图片的一部分。您的 Django 和 Mysql 服务器之间的实际往返时间可能非常短,因为您的大多数查询都在不到一毫秒的时间内返回。更大的问题是向您的数据库发出的查询数量会很快使其不堪重负。一个页面的 500 个查询已经很多了,甚至 50 个对我来说似乎也很多。如果有 10 个用户查看复杂的页面,您现在最多可以有 5000 个查询。

当调用者从广域网访问数据库时,到数据库服务器的往返时间更多是一个因素,其中往返很容易在 20 毫秒到 100 毫秒之间。

我肯定会考虑使用某种缓存。

于 2009-11-06T17:37:31.203 回答
2

有一些方法可以减少查询量。

  1. .filter()and.all()得到一堆东西;{%if%}在视图功能(或模板通过)中挑选和选择。Python 可以比 MySQL 更快地处理一批行。

    “但我可以向模板发送太多内容”。没错,但您将执行更少的 SQL 请求。测量看看哪个更好。

    这是你写 SQL 时经常做的事情。这没有错——它不会破坏 ORM——但它优化了底层数据库工作并将处理放入视图函数和模板中。

  2. 避免在模板中进行查询导航。当您执行 {{foo.bar.baz.quux}} 时,SQL 用于获取与bar关联foo,然后与baz关联bar,然后与quux关联baz。您可以通过一些仔细.filter()的 Python 处理来减少此查询业务,以在视图函数中组装一个有用的元组。

    同样,这是您在手工编写 SQL 时经常做的事情。在这种情况下,您在视图函数中收集更大批量的 ORM 管理的对象,并在 Python 中进行过滤,而不是通过大量单独的 ORM 请求。

    这不会破坏 ORM。它将使用配置文件从许多小查询更改为几个更大的查询。

于 2009-11-06T18:09:45.293 回答
1

数据库调用总是存在开销,在您的情况下,开销并没有那么糟糕,因为应用程序和数据库位于同一台机器上,因此没有网络延迟,但仍然有很大的成本。

当您向数据库发出请求时,它必须通过执行许多操作来准备为该请求提供服务,包括:

  • 将资源(内存缓冲区、临时表等)分配给将处理请求的数据库服务器连接/线程,
  • 反序列化 sql 和参数(即使在一台机器上也是必要的,因为这是一个进程间请求,除非您使用的是嵌入式数据库)
  • 如果不优化,则检查查询是否存在于查询缓存中并将其放入缓存中。
    • 另请注意,如果您的查询没有参数化(即值没有与 SQL 分离),这可能会导致语句的缓存未命中,这意味着每个请求都会导致每次都分析和优化查询。
  • 处理查询。
  • 准备并将结果返回给客户端。

这只是对大多数数据库管理系统为处理 SQL 请求所做的各种事情的概述。即使查询本身运行得相对较快,您也会产生 500 次开销。甚至与本地数据库的底线数据库交互也没有您预期的那么便宜。

于 2009-11-06T17:37:04.517 回答