Если два пользователя выполняют один и тот же php-файл, будут ли они выполняться параллельно или последовательно? Пример:
Если у меня есть данные базы данных, у которых есть только один идентификатор столбца, возможно ли, что следующий код создает для двух разных пользователей одинаковый результат?
1. $db=startConnection(); 2. $query="SELECT id FROM data"; 3. $result=$db->query($query)or die($db->error); 4. $zeile=mysqli_fetch_row($result); 5. $number=$zeile['id']; 6. $newnumber=$number+1; 7. echo $number; 8. $update = "UPDATE data Set id = '$newnumber' WHERE id = '$number'"; 9. $db->query($query)or die($db->error); 10. mysqli_close($db);
Если это не выполняется параллельно, значит ли это, когда 100 человек загружают файл php, время загрузки которого составляет 1 секунду, то одному из них приходится ждать 99 секунд?
Изменить: в комментариях сказано, что я мог бы испортить мою базу данных, я думаю, вот как это может испортиться:
Пользователь A выполняет файл с 1.-7. в этот момент пользователь B выполняет файл с 1.-7. то A загружает 8-10. и В нагрузки 8.-10. В этом случае оба пользователя будут иметь одинаковый номер на экране.
Теперь давайте рассмотрим следующий пример:
1. $db=startConnection(); 2. $query=" INSERT INTO data VALUES ()"; 3. $result=$db->query($query)or die($db->error); 4. echo $db->insert_id; 5. mysqli_close($db);
Допустим, что A выполняет файл с 1.-3. в этот момент пользователь B выполняет файл с 1.-5., после этого пользователь A загружает файл с 4.-5. Думаю, в этом сценарии оба будут иметь одинаковое число на экране справа? Предотвращает ли транзакция оба сценария?
Вы можете сказать, что файлы php выполняются параллельно (в большинстве случаев это так, но это зависит от веб-сервера).
Да, возможно, что следующий код создает для двух разных пользователей одинаковый результат.
1) Если вы используете MySQL, вы можете использовать транзакции и «SELECT … UPDATE FOR», чтобы избежать этой возможности. Просто использование транзакции не помогло бы!
2) Убедитесь, что вы используете InnoDB или любой другой механизм базы данных, поддерживающий транзакции. Например, MyISAM не поддерживает транзакции. Также у вас могут быть проблемы, если в базе данных разрешена какая-либо форма снимка для обработки чтения заблокированных записей.
3) Пример использования «SELECT … UPDATE FOR»:
$db = startConnection(); // Start transaction $db->query("START TRANSACTION") or die($db->error); // Your SELECT request but with "FOR UPDATE" lock $query = "SELECT id FROM data FOR UPDATE"; $result = $db->query($query); // Rollback changes if there is error if (!$result) { mysql_query("ROLLBACK"); die($db->error); } $zeile = mysqli_fetch_row($result); $number = $zeile['id']; $newnumber = $number + 1; echo $number; $update = "UPDATE data Set id = '$newnumber' WHERE id = '$number'"; $result = $db->query($query); // Rollback changes if there is error if (!$result) { mysql_query("ROLLBACK"); die($db->error); } // Commit changes in database after requests sucessfully executed mysql_query("COMMIT"); mysqli_close($db);
Только транзакция будет заблокирована только для записи. Вы можете проверить примеры ниже, запустив два клиента консоли mysql в двух отдельных терминальных окнах. Я сделал это, и так все работает.
У нас есть клиент №1 и клиент №2, которые выполняются параллельно.
client#1: BEGIN client#2: BEGIN client#1: SELECT id FROM data // fetched id = 3 client#2: SELECT id FROM data // fetched id = 3 client#1: UPDATE data Set id = 4 WHERE id = 3 client#2: UPDATE data Set id = 4 WHERE id = 3 client#1: COMMIT client#2: COMMIT
Оба клиента выбрали один и тот же идентификатор (3).
client#1: BEGIN client#2: BEGIN client#1: SELECT id FROM data FOR UPDATE // fetched id = 3 client#2: SELECT id FROM data FOR UPDATE // here! client#2 will wait for end of transaction started by client#1 client#1: UPDATE data Set id = 4 WHERE id = 3 client#1: COMMIT client#2: client#1 ended transaction and client#2 fetched id = 4 client#1: UPDATE data Set id = 5 WHERE id = 4 client#2: COMMIT
«SELECT … FOR UPDATE» делает блокировку чтения только для клиентов, которые используют «SELECT … FOR UPDATE». Это хорошо, потому что это означает, что такая блокировка чтения не повлияет на стандартные запросы «SELECT» без «FOR UPDATE».
Документация MySQL: «SELECT … FOR UPDATE» и другие блокировки чтения
Часть вашего вопроса заключалась в том, что PHP работает либо параллельно, либо последовательно. Поскольку я прочитал все и свою противоположность по этой теме, я решил проверить ее сам.
Полевые испытания:
В стеке LAMP, работающем под управлением PHP 5.5 w / Apache 2, я сделал сценарий с очень дорогим циклом:
function fibo($n) { return ($n > 1) ? fibo($n - 1) + fibo($n - 2) : 1; } $start = microtime(true); print "result: ".fibo(38); $end = microtime(true); print " - took ".round(($end - $start), 3).' s';
Результат с использованием 1 скрипта:
результата: 63245986 – выполнено 19.871 s
Результат с двумя сценариями, запущенными одновременно в двух разных окнах браузера:
результата: 63245986 – занял 20.753 с
результата: 63245986 – занял 20.847 с
Результат с тремя сценариями, запущенными одновременно в трех разных окнах браузера:
результата: 63245986 – выполнено 26.172 s
результата: 63245986 – занял 28.302 с
результата: 63245986 – занял 28.422 с
Использование ЦП при запуске 2 экземпляров сценария:
Использование ЦП при запуске 3 экземпляров сценария:
Althoug внутри скрипта PHP, вы не можете легко использовать многопоточность (хотя это возможно), Apache использует преимущества ваших серверов, имеющих несколько ядер для отправки нагрузки.
Поэтому, если ваш 1-секундный скрипт запускается 100 пользователями одновременно, ну, если у вас есть 100 процессорных ядер, 100-й пользователь почти ничего не заметит. Если у вас 8 ядер процессора (что более часто встречается), то 100-й пользователь теоретически должен ожидать чего-то вроде 100/8 100 / 8 = 12.5
секунд, чтобы его экземпляр скрипта начал. На практике, как показывает «контрольный показатель», производительность каждого потока уменьшается, когда другие потоки работают одновременно на других ядрах. Так что это может быть намного больше. Но не 100 секунд.