PHP CPU 和内存使用情况

3
我正在学习 PHP 和 Laravel。我工作的公司有一个使用纯 PHP 构建的 Web 应用程序。我注意到,当从一个大型 CSV 文件执行数据库更新时,这会占用大量 CPU 和内存资源,几乎无法进行其他 Web 应用程序。例如,更新某人的帐户,并不需要占用大量的 CPU 和内存资源。基本上,做前者会导致系统崩溃,直到完成为止。在 Linux 服务器上查看 TOP,显示 CPU 使用率达到了 99%,内存使用率相当高。
无论如何,这真的不是像企业那样的系统,只是一些用户用来管理另一个客户使用的 Python 框架编写的 Web 应用程序用户。但这让我想起了 PHP 基于企业的系统应该采取什么方法。
显然不能让单个用户执行密集任务并使系统崩溃,直到任务完成。我想实际世界中有很多例子,PHP 处理非常复杂的内存密集型任务,并且其他用户没有感受到。
编辑:当我说“系统崩溃”时,我的意思是服务器本身,因此管理员 PHP Web 应用程序和客户端 Python Web 应用程序都无法响应请求。

CSV文件有多大?是几十兆字节还是几个千兆字节?列的数据类型是什么?如果只是添加一大堆数据,应该很快...你是否让SQL执行任何逻辑或简单的INSERT INTO ...?我们需要看到代码才能知道为什么它需要这么长时间。 - ABuckau
我目前正在使用的表大约有17MB,但SQL正在检查表中行是否已存在,如果不存在则添加。我没有编写它,但我认为表应该被删除并创建一个新表。看起来他们几乎将所有列设置为 VARCHAR(32),大约有956列和5000行。 - cjones
希望它不需要逐行检查。 - MoeinPorkamel
是的,我应该真正查看一下这个查询。不过,是否有一种方法可以限制它使用的CPU和内存数量,而不停止它运行,从而希望允许其他用户继续他们的工作? - cjones
3个回答

3
在企业系统中,你首先要处理的就是冗余。一切都必须是冗余的。如果是基于PHP的应用程序,设置非常重要。
如果我们在谈论一个网站,以下是步骤:
- 更新DNS以便解析多个IP地址。这样无论哪个IP地址被返回给浏览器或者DNS池中的其中一台服务器先死掉,都没有影响。我们使用亚马逊的Route 53,非常棒。 - 接下来是Web服务器。当IP地址被“访问”时,请求将发送到Web服务器。我们使用nginx(因为nginx真的很棒)。由于DNS池中有多个IP地址,因此有多个Web服务器可用。同样,无论哪个Web服务器将处理客户端请求都是无关紧要的 - 冗余获胜。 - nginx提供静态内容或根据规则将请求传递给PHP。我们使用php-fpm。每个nginx服务器都将HTTP请求代理到一组php-fpm节点。通俗地说,它选择集群中的另一台计算机来提供PHP服务。无论哪个PHP节点将处理请求都是无关紧要的 - 冗余获胜。 - PHP现在会做一些工作,连接到数据库并与一组数据库进行交互。数据库是同步的,关于这个话题有整本书,所以我不会详细介绍。同样,哪个数据库获得查询也不重要 - 冗余获胜(实际上有很多处理方法)。 - 数据库服务器针对它们正在执行的工作进行了优化 - 这意味着它们针对读取或写入进行了优化。数据库或任何一种存储解决方案都不是你可以随便填充的无限黑洞。你必须仔细选择服务器将要执行的任务 - 它会写入吗?它将用于读取吗?它将进行多少次写入?它将进行多少次读取?当工作量增加时的计划是什么?基本上,你需要优化数据库服务器 - 你绝对不能使用默认的MySQL配置。如果你有大量的工作负载可能会超过数据库的负担,那么你需要使用一个队列机制,它能够汇总多个插入并在单个事务中刷新它们 - 这种方法利用了硬盘的带宽,并以I/O为代价进行交换(基本上这意味着它很好并且运行速度很快)。
所以,这就是如何在PHP中处理企业级别的东西的简短概述。每个部分都应该是冗余的,水平可扩展的,并针对其正在执行的工作进行优化。
你绝对不希望你的应用程序在有人搞砸了某些东西时变得挂起或无法使用。

3

高需求的进程不应该在网络请求上执行。

例如,您应该使用队列来处理。这样,您允许文件上传并创建一个队列进程来提供服务,然后请求完成,并且在一段时间后,进程开始在服务器上运行,但用户已经收到了请求。

然后,您可以考虑将队列配置为低CPU优先级,或者在数据处理之间添加休眠时间,以允许CPU为其他进程提供服务。

或者,您可以在上传文件后记录文件,并配置一个控制台作业每分钟检查是否有未处理的数据,然后处理一批并允许下一批在下一分钟检查时进行处理,同时在某个地方保留当前处理的行。

但是,请再次尝试避免在网络请求上执行这些长时间而繁重的进程,使用网络请求获取信息,然后单独触发进程。

Laravel框架对控制台作业或队列都有很好的支持。


2

我敢打赌你的长时间请求中有一个开放的会话在进行数据库插入,这会阻止后续请求打开会话。你需要做的是:

<?php
session_start();

/* pre-game stuff that depends on $_SESSION */

session_write_close();

/* long-running stuff that doesn't need to update $_SESSION */

或者更好的办法是,如果可能的话,跳过为此任务打开会话的步骤。
补充说明:
1. 开除那个设计了956列、仅使用varchar类型的数据库模式的人。 2. 如果插入操作这么慢,我敢打赌,有人添加了过多的索引。 3. 如果你在使用最便宜的共享CPU公共云实例,很可能已经用尽了所有的CPU份额。我曾经通过将大文件写入快速磁盘来将GCE微型实例的CPU压满。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接