Я поддерживаю пользовательское CMS-подобное приложение.
Всякий раз, когда документ отправляется, выполняется несколько задач, которые можно грубо сгруппировать по следующим категориям:
Категория 1 включает обновления различных таблиц MySQL, относящихся к содержимому документа.
Категория 2 включает в себя анализ содержимого HTML, хранящегося в полях MySQL LONGTEXT, для выполнения некоторых автоматических преобразований тегов привязки. Я подозреваю, что в этой задаче проводится большое количество вычислений.
Категория 3 включает обновления простого индекса поиска на основе MySQL, используя только несколько полей, соответствующих документу.
Все эти задачи должны быть завершены для того, чтобы представление документа считалось завершенным.
Аппарат, на котором размещено это приложение, имеет два четырехъядерных процессора Xeon (всего 8 ядер). Однако всякий раз, когда документ отправляется, весь выполняемый PHP-код ограничивается одним процессом, работающим на одном из ядер.
Мой вопрос:
Какие схемы, если таковые имеются, вы использовали для разделения нагрузки на обработку веб-приложений PHP / MySQL между несколькими ядрами ЦП? Мое идеальное решение в основном порождало несколько процессов, позволяло им выполнять параллельно на нескольких ядрах, а затем блокировать до тех пор, пока все процессы не будут выполнены.
Связанный вопрос:
Каков ваш любимый инструмент профилирования производительности PHP?
PHP не совсем ориентирован на многопоточность: как вы уже заметили, каждая страница обслуживается одним процессом PHP – это делает одно за раз, в том числе просто «ожидание», пока SQL-запрос выполняется на сервере базы данных.
К сожалению, вы не можете этого сделать, к сожалению: так работает PHP.
Тем не менее, вот пара из них:
Таким образом, на самом деле, ваше основное ядро сервера в конечном итоге будет использоваться 😉
И, если вы считаете, что ваши страницы слишком много времени для генерации, возможным решением является разделение ваших вычислений на две группы:
Для подобных ситуаций в моем втором пункте, поскольку вам не нужны эти вещи, сделанные немедленно … Ну, просто не делайте их сразу 😉
Решением, которое я часто использую, является некоторый механизм массового обслуживания:
И для некоторых других манипуляций вы просто хотите, чтобы они запускались каждые X минут – и, здесь тоже, cronjob – идеальный инструмент.
PHP имеет полную поддержку Multi-Threading, которую вы можете использовать в полной мере во многих отношениях. Могут продемонстрировать эту способность Multi-Threading в разных примерах:
Быстрый поиск даст дополнительные ресурсы.
MySQL полностью многопоточен и будет использовать несколько процессоров при условии, что операционная система их поддерживает, а также максимизирует системные ресурсы, если они правильно настроены для производительности.
Типичная настройка в 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. Вы должны удалить это из файлов конфигурации MySQL каждый раз, когда увидите это, если они не предназначены для Solaris 8 или ранее.
InnoDB ::
У вас нет таких ограничений, если вы используете Innodb, имеет механизм хранения, потому что он полностью поддерживает параллельность потоков потоков
innodb_thread_concurrency // Recommended 2 * CPUs + number of disks
Вы также можете посмотреть на innodb_read_io_threads
и innodb_write_io_threads
где по умолчанию 4
и его можно увеличить до 64
зависимости от аппаратного обеспечения
Другие:
Другие конфигурации, которые также можно посмотреть, включают key_buffer_size
, key_buffer_size
, table_open_cache
и т. key_buffer_size
, table_open_cache
приводят к лучшей производительности
PHP:
В pure 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
Вот полный рабочий пример SQLWorker
I suspect that a great deal of computation time is spent in this task.
Если вы уже знаете проблему, это облегчает решение с помощью циклов событий, очереди заданий или с использованием потоков.
Работа над одним документом по одному может быть very very slow painful process
. @ka однажды взломал свой путь, используя ajax для вызова нескольких запросов. Некоторые творческие умы просто разветвили процесс, используя pcntl_fork, но если вы используете windows
вы не сможете воспользоваться преимуществами pcntl
С pThreads
поддерживающим как окна, так и Unix-системы, у вас нет такого ограничения. Это так же просто, как .. Если вам нужно разобрать 100 document
? 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 8,714
файлов, 105,109
ссылок без потоков и посмотреть, сколько времени потребуется.
Лучшая архитектура
Нерест слишком много нитей, которые не являются умными в производстве. Лучше было бы использовать Pooling . Создайте пул Определите Рабочих, затем сверните с Task
Улучшение производительности
Прекрасно приведенный выше пример еще improved
. Если вы ожидаете, что система сканирует all files in a single thread
вы также можете use multiple threads scan my system
для файлов, а затем складывать данные в Workers for processing
Ответ на этот вопрос был в значительной степени удовлетворен первым ответом, но есть так много возможностей для улучшения производительности. Вы когда-нибудь рассматривали подход, основанный на событиях?
@rdlowrey Цитата 1:
Хорошо подумай об этом так. Представьте, что вам нужно обслуживать 10 000 одновременно подключенных клиентов в вашем веб-приложении. Традиционные серверы « нить за запрос» или « процесс за каждый запрос» не являются вариантом, так как независимо от того, насколько легки ваши потоки, вы по-прежнему не можете удерживать 10 000 из них одновременно.
@rdlowrey Цитата 2:
С другой стороны, если вы сохраняете все сокеты в одном процессе и слушаете, чтобы эти сокеты стали читабельными или записываемыми, вы можете поместить весь ваш сервер в один цикл событий и работать с каждым сокетом только тогда, когда есть что читать или писать.
Почему бы вам не поэкспериментировать с event-driven
, non-blocking I/O
подходом non-blocking I/O
к вашей проблеме. PHP имеет libevent для надстройки вашего приложения.
Я знаю, что этот вопрос является Multi-Threading
но если у вас есть время, вы можете посмотреть этот ядерный реактор, написанный на PHP by @igorw
Я думаю, вы должны использовать конденсатор, используя Cache
и задание для некоторых из ваших задач. Вы можете легко получить сообщение о том, что
Document uploaded for processing ..... 5% - Done
Затем выполняйте всю работу, тратя время в фоновом режиме. Посмотрите, как сделать большую работу по обработке меньше для аналогичного примера.
Профилирующий инструмент? Не существует единого профиля для веб-приложения от Xdebug до Yslow , все это очень полезно. Например. Xdebug не имеет полезности, когда дело доходит до потоков, потому что его не поддерживают
У меня нет любимой
Масштабирование веб-серверов не заставит MySQL сдвинуться на один дюйм, когда дело доходит до получения доступа к многоядерным процессорам. Зачем? Сначала рассмотрим два основных модуля хранения MySQL
Этот механизм хранения не имеет доступа к нескольким ядрам. Это никогда не было и никогда не будет. Он выполняет полную блокировку таблицы для каждого INSERT, UPDATE и DELETE. Отправка запросов с нескольких веб-серверов для чего-либо с помощью MyISAM просто становится узким местом.
До MySQL 5.1.38 этот механизм хранения имел доступ только к одному процессору. Вам приходилось делать странные вещи, например, запускать MySQL несколько раз на одной машине, чтобы принуждать ядра обрабатывать разные экземпляры MySQL . Затем настройте балансировку DB-соединений веб-серверов между несколькими экземплярами. Это старая школа (особенно если вы используете версии MySQL до MySQl 5.1.38).
Начиная с MySQL 5.1.38, вы устанавливаете новый плагин InnoDB. У него есть функции, которые вы должны настроить для получения InnoDB для доступа к нескольким процессорам. Я написал об этом в DBA StackExchange
Sep 20, 2011
: многоядерные процессоры и производительность MySQL Sep 12, 2011
: Возможно ли заставить MySQL использовать более одного ядра? May 26, 2011
О производительности однопоточных и многопоточных баз данных Эти новые функции полностью доступны в MySQL 5.5 / 5.6 и Percona Server.
Если ваша пользовательская CMS использует индексирование / поиск FULLTEXT, вы должны перейти на MySQL 5.6, потому что InnoDB теперь поддерживает индексирование / поиск FULLTEXT.
Установка в MySQL 5.6 не будет автоматически заставлять процессоры работать. Вам нужно будет настроить его, потому что, LEFT UNONFIGURED, возможно, что более старые версии MySQL будут превосходить и перенаправлять новые версии:
Nov 24, 2011
: Почему mysql 5.5 медленнее, чем 5.1 (linux, используя mysqlslap) Oct 05, 2011
: Query длится много времени в некоторых новых версиях MySQL Jun 19, 2011
: Как правильно выполнить выпечку MySQL? Это может быть не ответ на вопрос, который вы ищете, но решение, которое вы ищете, связано с потоковой обработкой. Threading необходим для многоядерного программирования, и потоки не реализованы в PHP.
Но, в некотором смысле, вы можете подделывать потоки в PHP, полагаясь на возможности многозадачности операционной системы. Я предлагаю дать краткий обзор стратегий многопоточности в PHP, чтобы разработать стратегию для достижения того, что вам нужно.
Мертвая ссылка: многопоточные стратегии в PHP