在Django/Apache中的性能不佳

在Django/Apache中的性能不佳

问题描述:

我写过这个应该是快速和简单的视图,但实际上它花费了太多时间,基本上占用了我的服务器的CPU。在Django/Apache中的性能不佳

这是写在Django的REST的框架和主要执行以下操作:从查询

  • 查找DB对象参数
  • 更新的数据库对象的某些字段
  • 保存对象
  • 狼狈不堪对象的详细记录
  • 返回json编码的详细记录

该视图是作为APIView.post()处理程序的一部分编写的。

仿形函数本身提供下列:

  • 取指需要2 - 5毫秒
  • 更新需要1毫秒
  • 取细节需要3 - 7毫秒
  • 阿帕奇本身报告了一些30 -40毫秒更多比简介函数本身(如果函数报告总时间10ms,apache(access.log)将报告一些40ms。增加的时间似乎是所有查询的。

netdata(查看postgres页面)说所有提取都是从RAM完成的。 我有一个2 CPU的机器服务于整个应用程序。

2个CPU在Apache报告的整个时间内加载,这意味着平均功能服务时间为40毫秒,我以每秒50次查询获得100%的负载。显然,我的目标是每秒超过50个查询。由于核心功能只需要约10ms,我认为CPU应该加载20%,而不是100%。

top显示几乎所有的CPU时间都在wsgi进程中消耗。 Apache工作人员仅消耗2-3%的CPU,与postgres相同。

我试图验证它是否是Django的上下文处理器添加开销,但它们不是,至少不在我的开发机器上(manage.py runserver):来自同一台机器的本地查询不会产生任何开销,无论活动上下文处理器的数量(仅限于此处的默认值)。

我试着调整apache/mod_wsgi参数,但没有改变这种情况 - 对于小数据集,每秒只有50个请求,一切都在RAM中 - 它只需要很长时间。

我会添加配置文件,但我不确定它们是否相关,所以如果它们是,请告诉我要追加什么。

我错过了什么?

@Graham Dumpleton: mpm_event。CONF:

<IfModule mpm_event_module> 
     StartServers    2 
     MinSpareThreads   25 
     MaxSpareThreads   75 
     ThreadLimit    64 
     ThreadsPerChild   250 
     MaxRequestWorkers  1500 
     MaxConnectionsPerChild 0 
</IfModule> 

站点启用-:

<VirtualHost *:80> 
    .... 
    WSGIDaemonProcess mgmt-server display-name=wsgi-main processes=2 threads=50 python-path=/home/myuser/mgmt-server 
    WSGIProcessGroup mgmt-server 

    WSGIScriptAlias//home/myuser/mgmt-server/ServerCfg/wsgi.py 
    WSGIPassAuthorization On 

    <Directory "/home/myuser/mgmt-server/ServerCfg"> 
      <Files wsgi.py> 
        Require all granted 
      </Files> 
    </Directory> 
</VirtualHost> 

我不知道你在veryfying守护进程的应用程序是否正在运行的意思。 WSGIRestrictEmbedded未明确设置的站点配置

EDIT2: 进一步的测试表明,罪魁祸首肯定是在Django代码的地方:如果在分析wsgi.py,它清楚地表明,绝大多数的开销是WSGI之间。 py和目标API函数。 继续(再次)去除中间件以查看需要我的位置。

编辑3: 实施pgbouncer显示,尽管连接时间没有改善测试大大减少(下面的数字是每1000个连接秒):

pgbouncer 0.2007670805323869 
socket 4.6464033296797425 
ip 9.120775469928049 

这显然不是DB连接时间。继续进行详细的分析。

编辑4: 不知道它是否更合适张贴到答案或在这里,但我决定去这里:)

众多的优化和调整之后,我设法让整个每次通话时间大约为25毫秒。这仍然很大,但由于我需要索引,所以我不知如何补救。

通过函数分析功能,我发现有三个显著CPU刻录机:

  1. 27%,我的代码获取&更新记录
  2. 50%器transaction.commit(与transaction.atomic():) - 以上
  3. 23%Django & rest-framework中间件,最着名的是CommonMiddleware,resolver.resolve(URL匹配)和延迟加载session.user对象进行身份验证。总之一些6ms的

我完全困惑为什么从550个记录(而不是几百万,550没有背后有什么额外的零)表获取&更新将需要这么多时间,即使有问题表中有5个指标。这是所有几页中的数据库事务。同一个应用程序也有一张表,其中有数百万条记录,只有频繁的插入(一个日志表),该表的表现更加令人赞叹。

我试图立即运行它,如果它有帮助,我会尝试设置自动vaccuum以及。除此之外,我不知道该怎么做,除了介绍memcached和解决这个与数据库有关的愚蠢问题。

编辑5: 真空没有帮助。

编辑6: 与延迟WAL设置进行实验:

  • synchronous_commit =关
  • wal_sync_method = FSYNC(在pg_test_fsync最快)
  • wal_buffers = 16MB(是这样的值,即使之前为shared_buffers = 512MB)
  • wal_writer_delay = 500ms
  • COMMIT_DELAY = 1000
  • commit_siblings = 5

我真的不知道有关忘记过去三,调整无知识会显着降低性能) 这些变化增加了我的pgbench结果从72TPS到1500TPS。

这将transaction.commit()时间移回零。我的功能现在只需要其“原始时间”,而我在分析事务时间时并不在意。但是,整个请求仍然需要相同的扩展时间。

我的猜测:这是因为Django释放连接请求服务和触发强制写入,可能?继续玩CONN_MAX_AGE设置。可能重新安装pgbouncer。

Edit7:

最终报告:

重新安装pgbouncer现在导致大多数15ms内是服务器的请求。我想这是Django与某些数据库操作可以完成的理论极限。 6 - 8ms为Django处理程序,6 - 8ms为我自己的功能。这将我的服务容量增加到每秒约120个请求(双核CPU)。

继续使用某种类型的WebSockets解决方案,这将允许我池更新和减少Django处理开销。

+1

您使用的是什么Apache MPM?什么是Apache MPM设置?什么是“MaxRequestsPerChild”设置为?你使用的是什么mod_wsgi模式?如果使用mod_wsgi守护进程模式,用什么配置?如果需要,您是否验证过您的应用程序实际上是在mod_wsgi守护进程模式进程中运行的?你是否设置了'WSGIRestrictEmbedded On''来确保嵌入模式没有被启用? –

+0

@Graham Dumpleton:更新了相关配置细节 – velis

+0

WSGIRestrictEmbedded On现在位于wsgi_mod.load配置文件中。不用找了。 – velis

与此配置有关的主要事情是threads=50。如果CPU密集型,Python并不能很好地处理大量的并发线程。

作为背景,为什么,我建议你看我谈这个问题在:

我会一直在寻找其他的事情是守护进程的不断循环。你并没有使用任何会导致这种情况的选项。由于需要始终加载应用程序,因此不断重新启动会成为问题。

你还没有说任何建议你看到进程重新启动,但你可以在Apache配置中设置LogLevel info,然后mod_wsgi将记录有关重新启动的详细信息,以便您确认。

+0

感谢您的洞察力。请注意,“线程= 50”是一个临时值,而我正在试验看看罪魁祸首是什么(我现在把它移回到12)。我现在在Apache/mod_wsgi堆栈后的某处找到了罪魁祸首,但我还没有找到它。将不得不按功能手动功能,因为添加cProfile水域的罪魁祸首被遗忘。 – velis