如何在PHP / MySQL应用程序中充分利用多核CPU?

时间:2010-02-15 16:30:44

标签: php mysql multicore

我维护一个自定义的类似CMS的应用程序。

每当提交文档时,都会执行几项任务,这些任务大致可分为以下几类:

  1. MySQL查询。
  2. HTML内容解析。
  3. 搜索索引更新。
  4. 类别1包括与文档内容相关的各种MySQL表的更新。

    类别2包括解析存储在MySQL LONGTEXT字段中的HTML内容,以执行一些自动锚标记转换。我怀疑在这项任务中花费了大量的计算时间。

    类别3包括使用与文档对应的少数字段对基于MySQL的简单搜索索引的更新。

    所有这些任务都需要完成才能使文件提交完整。

    托管此应用程序的计算机具有双四核Xeon处理器(总共8个核心)。但是,每当文档提交时,执行的所有PHP代码都限制在其中一个核心上运行的单个进程。

    我的问题:

    您曾经使用过什么方案将PHP / MySQL Web应用程序处理负载分散到多个CPU内核中?我理想的解决方案基本上会产生一些进程,让它们在几个内核上并行执行,然后阻塞直到所有进程完成。

    相关问题:

    您最喜欢的PHP性能分析工具是什么?

5 个答案:

答案 0 :(得分:56)

简介

PHP拥有完整的Multi-Threading支持,您可以在很多方面充分利用它。已经能够在不同的例子中证明这种多线程能力:

quick Search会提供额外资源。

分类

1:MySQL查询

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

其他人:

其他配置也包括64key_buffer_sizetable_open_cache等等,这些都可以带来更好的效果

PHP:

sort_buffer_size中,您可以创建MySQL Worker,其中每个查询都在单独的PHP线程中执行

pure PHP

Here is a Full Working Example of SQLWorker

2:HTML内容解析

$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,然后@rdlowrey8,714

的池

绩效改进

上面的示例很好仍然是105,109。如果等待系统扫描Task,您也可以improved查找文件,然后将数据堆叠到Workers进行处理

3:搜索索引更新

第一个答案几乎已经回答了这个问题,但是有很多方法可以改善性能。你有没有考虑过基于事件的方法?

介绍活动

@rdlowrey引用1:

  

好好想起来就好了。想象一下,您需要在Web应用程序中为10,000个同时连接的客户端提供服务。传统的线程每请求每个请求的进程数服务器不是一个选项,因为无论您的线程多么轻量级,您仍然无法保持其中10,000个一次打开。

libevent引用2:

  

另一方面,如果您将所有套接字保留在一个进程中并监听这些套接字是否可读或可写,则可以将整个服务器放在单个事件循环中,并且仅在每个套接字上运行时才能进行操作。读/写的东西。

为什么不尝试使用all files in a single threaduse multiple threads scan my system来解决您的问题。 PHP有Nuclear Reactor written in PHP来增强您的应用程序。

我知道这个问题都是event-driven,但如果你有时间,可以@igorw

查看Making a large processing job smaller

最后

考虑

我认为您应该使用non-blocking I/OMulti-Threading来完成某些任务。您可以轻松地传达消息

Cache

然后一直在后台浪费任务。请查看Xdebug进行类似的案例研究。

Profilling

分析工具??从Yslow到{{3}}的Web应用程序没有单一的配置文件工具都非常有用。例如。 Xdebug在线程方面没有用处,因为它不受支持

我没有收藏

答案 1 :(得分:35)

PHP并不是面向多线程:正如您已经注意到的那样,每个页面都由一个PHP进程提供服务 - 一次只执行一项操作,包括在数据库上执行SQL查询时只需“等待”服务器

不幸的是,你无能为力:这就是PHP的工作方式。


不过,这里有几个问题:

  • 首先,您的服务器上一次可能有多个用户,这意味着您将在当时提供多个页面,这反过来意味着您将拥有多个PHP进程和SQL查询同时运行...这意味着将使用服务器的几个核心。
    • 每个PHP进程将在一个核心上运行,以响应一个用户的请求,但有几个Apache并行运行的子进程(每个请求一个,最多几个或几百个) ,取决于您的配置)
    • MySQL服务器是多线程的,这意味着它可以使用多个不同的核心来回答多个并发请求 - 即使每个请求都不能由多个核心提供服务。

因此,实际上,您的服务器的8核将最终被使用; - )


而且,如果您认为您的页面生成时间太长,可能的解决方案是将您的计算分为两组:

  • 一方面,生成页面必须要做的事情:对于那些,你可以做的事情不多
  • 另一方面,有时必须运行的东西,但不一定是立即运行的
    • 例如,我想一些统计计算:你希望它们是最新的,但是如果它们落后几分钟,那通常都很好。
    • 同样的电子邮件发送:无论如何,在用户收到/阅读邮件之前,几分钟就会过去,因此无需立即发送邮件。

对于我的第二点中的那种情况,因为你不需要立即完成那些事情......好吧,就是不要马上做它们;-) 我经常使用的解决方案是一些排队机制:

  • Web应用程序将内容存储在“待办事项列表”
  • “todo-list”由一些经常通过cronjob运行的批次排队。

对于其他一些操作,你只想让它们每隔X分钟运行一次 - 而且,在这里,cronjob也是完美的工具。

答案 2 :(得分:4)

在访问多核CPU时,扩展Web服务器不会让MySQL预算减少一英寸。为什么?首先考虑MySQL的两个主要存储引擎

MyISAM

此存储引擎不访问多个核心。它从来没有,也永远不会。它为每个INSERT,UPDATE和DELETE执行全表锁定。从多个Web服务器发送查询以使用MyISAM执行任何操作都会遇到瓶颈。

InnoDB

在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

中写过这个

这些新功能在MySQL 5.5 / 5.6和Percona Server中也完全可用。

CAVEAT

如果您的自定义CMS使用FULLTEXT索引/搜索,您应该升级到MySQL 5.6,因为InnoDB现在支持FULLTEXT索引/搜索。

安装到MySQL 5.6不会自动使CPU开始运行。您将不得不调整它,因为,LEFT UNCONFIGURED,旧版本的MySQL有可能超越并超越新版本:

答案 3 :(得分:2)

这可能不是您正在寻找的问题的答案,但您寻求的解决方案涉及线程。线程是多核编程所必需的,并且线程在PHP中实现

但是,从某种意义上说,你可以依靠操作系统的多任务处理能力伪造PHP中的线程。我建议快速概述Multi-threading strategies in PHP以制定实现所需目标的策略。

死链接: Multi-threading strategies in PHP

答案 4 :(得分:1)

只要让您知道,当您想到时:“可怜的PHP没有多线程处理”

好吧... Python doesn't have real multithreading eitherNor does NodeJS have multi-threading support。 Java具有某种多线程,但即使在some code halts the whole machine afaik那里。

但是:除非您对一件事情进行繁重的编程,否则这是无关紧要的。许多请求打入您的页面,并且将继续使用所有内核,因为每个请求都使用自己的单个线程生成自己的进程。