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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在PHP / MySQL应用程序中充分利用多核CPU? MyISAM InnoDB相关的知识,希望对你有一定的参考价值。

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

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

  1. mysql查询。
  2. html内容解析。
  3. 搜索索引更新。

类别1包括与文档内容相关的各种MySQL表的更新。

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

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

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

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

我的问题:

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

相关问题:

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

答案

PHP并不是面向多线程的:正如您已经注意到的,每个页面都由一个PHP进程提供服务 - 它一次完成一件事,包括在数据库服务器上执行SQL查询时“等待”。

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

不过,这里有几个想法:

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

所以,事实上,你的服务器的8核将最终被使用;-)

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

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

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

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

对于其他一些操作,你只需要每X分钟运行一次 - 而且,这里也是一个完美的工具。

另一答案

介绍

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

quick Search会提供额外的资源。

分类

1: MySQL queries

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_threadsinnodb_write_io_threads,默认值为4,根据硬件的不同,它可以增加到64一样高

其他:

其他配置也包括key_buffer_sizetable_open_cachesort_buffer_size等,这些都可以带来更好的性能

PHP:

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

$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

Here is a Full Working Example of SQLWorker

2: HTML content parsing

我怀疑在这项任务中花费了大量的计算时间。

如果您已经知道问题,那么通过事件循环,作业队列或使用线程更容易解​​决。

一次处理一个文档可能是一个非常非常缓慢,痛苦的过程。 @ka曾经使用ajax来调用多个请求,一些创造性思维只会使用pcntl_fork来解决这个问题,但是如果你使用的是windows那么你就不能利用pcntl

使用pThreads支持windows和Unix系统,你没有这样的限制。就如此简单..如果你需要解析100个文件? Spawn 100 Threads ......简单

HTML扫描

// 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');
        
    

实验

尝试解析没有线程的8,714链接的105,109文件,看看它需要多长时间。

更好的架构

产生太多的线程,这在生产中不是一件聪明的事情。更好的方法是使用Pooling。有一个定义Workers池然后stackTask

性能改进

很好,上面的例子仍然可以改进。您可以使用多个线程扫描系统中的文件,然后将数据堆叠到Workers进行处理,而不是等待系统扫描单个线程中的所有文件

3: Search index updating

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

Introducing Event

@rdlowrey报价1:

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

@rdlowrey报价2:

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

你为什么不试验event-drivennon-blocking I/O解决你的问题。 PHP有libevent来增强您的应用程序。

我知道这个问题都是Multi-Threading,但如果你有一些时间你可以看看这个以上是关于如何在PHP / MySQL应用程序中充分利用多核CPU? MyISAM InnoDB的主要内容,如果未能解决你的问题,请参考以下文章

如何为超线程/多核选择最佳线程数?

Node玩转多进程

Java7 Fork-Join 框架:任务切分,并行处理

mysql在大数据量下性能调优相关参数

Nginx 开启多核cpu配置

node处理http比php慢