我维护一个自定义的类似CMS的应用程序。
每当提交文档时,都会执行几项任务,这些任务大致可分为以下几类:
类别1包括与文档内容相关的各种MySQL表的更新。
类别2包括解析存储在MySQL LONGTEXT字段中的HTML内容,以执行一些自动锚标记转换。我怀疑在这项任务中花费了大量的计算时间。
类别3包括使用与文档对应的少数字段对基于MySQL的简单搜索索引的更新。
所有这些任务都需要完成才能使文件提交完整。
托管此应用程序的计算机具有双四核Xeon处理器(总共8个核心)。但是,每当文档提交时,执行的所有PHP代码都限制在其中一个核心上运行的单个进程。
我的问题:
您曾经使用过什么方案将PHP / MySQL Web应用程序处理负载分散到多个CPU内核中?我理想的解决方案基本上会产生一些进程,让它们在几个内核上并行执行,然后阻塞直到所有进程完成。
相关问题:
您最喜欢的PHP性能分析工具是什么?
答案 0 :(得分:56)
PHP拥有完整的Multi-Threading支持,您可以在很多方面充分利用它。已经能够在不同的例子中证明这种多线程能力:
quick Search会提供额外资源。
MySQL is fully multi-threaded并且将使用多个CPU,前提是操作系统支持它们。如果正确配置性能,它还将最大化系统资源。
my.ini
中影响线程性能的典型设置是:
thread_cache_size = 8
如果您有很多新连接,可以增加thread_cache_size以提高性能。通常,如果您有一个良好的线程实现,这不会提供显着的性能改进。但是,如果您的服务器每秒看到数百个连接,您通常应该将thread_cache_size设置得足够高,以便大多数新连接使用缓存的线程
如果您使用Solaris,则可以使用
thread_concurrency = 8
thread_concurrency使应用程序能够为线程系统提供有关应该同时运行的所需线程数的提示。
此变量自MySQL 5.6.1起不推荐使用,并在MySQL 5.7中删除。除非它们适用于Solaris 8或更早版本,否则无论何时看到它都应从MySQL配置文件中删除它。
InnoDB::
如果您使用Innodb具有存储引擎,则不会有此类限制,因为它完全支持线程并发
innodb_thread_concurrency // Recommended 2 * CPUs + number of disks
您还可以查看默认为innodb_read_io_threads
的{{1}}和innodb_write_io_threads
,并根据硬件将其增加到4
其他人:
其他配置也包括64
,key_buffer_size
,table_open_cache
等等,这些都可以带来更好的效果
PHP:
在sort_buffer_size
中,您可以创建MySQL Worker,其中每个查询都在单独的PHP线程中执行
pure PHP
Here is a Full Working Example of SQLWorker
$sql = new SQLWorker($host, $user, $pass, $db);
$sql->start();
$sql->stack($q1 = new SQLQuery("One long Query"));
$sql->stack($q2 = new SQLQuery("Another long Query"));
$q1->wait();
$q2->wait();
// Do Something Useful
如果您已经知道问题,那么通过事件循环,作业队列或使用线程更容易解决。
一次处理一个文档可以是I suspect that a great deal of computation time is spent in this task.
。 @ka曾经使用ajax攻击多个请求,但是有些创意人员只会使用pcntl_fork来解决这个问题,但如果您使用的是very very slow painful process
,则无法利用Pooling 1}}
windows
支持Windows和Unix系统,你没有这样的限制。就像..如果你需要解析pcntl
一样容易吗? pThreads
...简单
HTML扫描
100 document
输出
Spawn 100 Threads
使用的课程
// Scan my System
$dir = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
$dir = new RecursiveIteratorIterator($dir);
// Allowed Extension
$ext = array(
"html",
"htm"
);
// Threads Array
$ts = array();
// Simple Storage
$s = new Sink();
// Start Timer
$time = microtime(true);
$count = 0;
// Parse All HTML
foreach($dir as $html) {
if ($html->isFile() && in_array($html->getExtension(), $ext)) {
$count ++;
$ts[] = new LinkParser("$html", $s);
}
}
// Wait for all Threads to finish
foreach($ts as $t) {
$t->join();
}
// Put The Output
printf("Total Files:\t\t%s \n", number_format($count, 0));
printf("Total Links:\t\t%s \n", number_format($t = count($s), 0));
printf("Finished:\t\t%0.4f sec \n", $tm = microtime(true) - $time);
printf("AvgSpeed:\t\t%0.4f sec per file\n", $tm / $t);
printf("File P/S:\t\t%d file per sec\n", $count / $tm);
printf("Link P/S:\t\t%d links per sec\n", $t / $tm);
Total Files: 8,714
Total Links: 105,109
Finished: 108.3460 sec
AvgSpeed: 0.0010 sec per file
File P/S: 80 file per sec
Link P/S: 907 links per sec
Sink
class Sink extends Stackable {
public function run() {
}
}
实验
尝试解析LinkParser
个没有线程的class LinkParser extends Thread {
public function __construct($file, $sink) {
$this->file = $file;
$this->sink = $sink;
$this->start();
}
public function run() {
$dom = new DOMDocument();
@$dom->loadHTML(file_get_contents($this->file));
foreach($dom->getElementsByTagName('a') as $links) {
$this->sink[] = $links->getAttribute('href');
}
}
}
个链接的文件,看看它需要多长时间。
更好的架构
产生太多线程,这在生产中并不是一件聪明的事情。更好的方法是使用Workers。拥有一个定义stack,然后@rdlowrey和8,714
绩效改进
上面的示例很好仍然是105,109
。如果等待系统扫描Task
,您也可以improved
查找文件,然后将数据堆叠到Workers进行处理
第一个答案几乎已经回答了这个问题,但是有很多方法可以改善性能。你有没有考虑过基于事件的方法?
@rdlowrey引用1:
好好想起来就好了。想象一下,您需要在Web应用程序中为10,000个同时连接的客户端提供服务。传统的线程每请求或每个请求的进程数服务器不是一个选项,因为无论您的线程多么轻量级,您仍然无法保持其中10,000个一次打开。
libevent引用2:
另一方面,如果您将所有套接字保留在一个进程中并监听这些套接字是否可读或可写,则可以将整个服务器放在单个事件循环中,并且仅在每个套接字上运行时才能进行操作。读/写的东西。
为什么不尝试使用all files in a single thread
,use multiple threads scan my system
来解决您的问题。 PHP有Nuclear Reactor written in PHP来增强您的应用程序。
我知道这个问题都是event-driven
,但如果你有时间,可以@igorw
我认为您应该使用non-blocking I/O
和Multi-Threading
来完成某些任务。您可以轻松地传达消息
Cache
然后一直在后台浪费任务。请查看Xdebug进行类似的案例研究。
分析工具??从Yslow到{{3}}的Web应用程序没有单一的配置文件工具都非常有用。例如。 Xdebug在线程方面没有用处,因为它不受支持
我没有收藏
答案 1 :(得分:35)
PHP并不是面向多线程:正如您已经注意到的那样,每个页面都由一个PHP进程提供服务 - 一次只执行一项操作,包括在数据库上执行SQL查询时只需“等待”服务器
不幸的是,你无能为力:这就是PHP的工作方式。
不过,这里有几个问题:
因此,实际上,您的服务器的8核将最终被使用; - )
而且,如果您认为您的页面生成时间太长,可能的解决方案是将您的计算分为两组:
对于我的第二点中的那种情况,因为你不需要立即完成那些事情......好吧,就是不要马上做它们;-) 我经常使用的解决方案是一些排队机制:
对于其他一些操作,你只想让它们每隔X分钟运行一次 - 而且,在这里,cronjob也是完美的工具。
答案 2 :(得分:4)
在访问多核CPU时,扩展Web服务器不会让MySQL预算减少一英寸。为什么?首先考虑MySQL的两个主要存储引擎
此存储引擎不访问多个核心。它从来没有,也永远不会。它为每个INSERT,UPDATE和DELETE执行全表锁定。从多个Web服务器发送查询以使用MyISAM执行任何操作都会遇到瓶颈。
在MySQL 5.1.38之前,此存储引擎只访问了一个CPU。你必须做一些奇怪的事情,比如run MySQL multiple times on one machine to coerce the cores to handle different instances of MySQL。然后,让Web服务器的数据库连接在多个实例之间进行负载平衡。那是旧学校(特别是如果你在MySQl 5.1.38之前使用的是MySQL版本)。
从MySQL 5.1.38开始,安装新的InnoDB插件。它具有您必须调整以使InnoDB访问多个CPU的功能。我在DBA StackExchange
中写过这个Sep 20, 2011
:Multi cores and MySQL Performance Sep 12, 2011
:Possible to make MySQL use more than one core? May 26, 2011
:About single threaded versus multithreaded databases performance 这些新功能在MySQL 5.5 / 5.6和Percona Server中也完全可用。
如果您的自定义CMS使用FULLTEXT索引/搜索,您应该升级到MySQL 5.6,因为InnoDB现在支持FULLTEXT索引/搜索。
安装到MySQL 5.6不会自动使CPU开始运行。您将不得不调整它,因为,LEFT UNCONFIGURED,旧版本的MySQL有可能超越并超越新版本:
Nov 24, 2011
:Why mysql 5.5 slower than 5.1 (linux,using mysqlslap) Oct 05, 2011
:Query runs a long time in some newer MySQL versions Jun 19, 2011
:How do I properly perform a MySQL bake-off? 答案 3 :(得分:2)
这可能不是您正在寻找的问题的答案,但您寻求的解决方案涉及线程。线程是多核编程所必需的,并且线程在PHP中实现 。
但是,从某种意义上说,你可以依靠操作系统的多任务处理能力伪造PHP中的线程。我建议快速概述Multi-threading strategies in PHP以制定实现所需目标的策略。
答案 4 :(得分:1)
只要让您知道,当您想到时:“可怜的PHP没有多线程处理”
好吧... Python doesn't have real multithreading either。 Nor does NodeJS have multi-threading support。 Java具有某种多线程,但即使在some code halts the whole machine afaik那里。
但是:除非您对一件事情进行繁重的编程,否则这是无关紧要的。许多请求打入您的页面,并且将继续使用所有内核,因为每个请求都使用自己的单个线程生成自己的进程。