NodeJS намного медленнее, чем PHP?

Я запускаю крошечный веб-сервер масштаба под Apache + PHP + MySQL на данный момент и хотел бы изучить вариант использования NodeJS. Сервер буквально выполняет две вещи:

  • Подавать некоторые статические файлы (ресурсы HTML/CSS/изображения и т.д.)
  • Запросить базу данных (выбрать и вставить только, не обновлять и не удалять)

Однако я столкнулся с некоторой проблемой производительности, и я пытаюсь выяснить, где проблема. Чтобы изолировать проблему, я создал минимальное приложение NodeJS, которое запускает запрос к MySQL и возвращает 50 строк данных в виде JSON. Ниже мой код:

var express = require('express');
var compression = require('compression');
var mysql = require('mysql');

var db = mysql.createPool({
    host: <host>,
    user: <user>,
    password: <password>,
    database: <database>,
    debug: false
});

var app = express();
app.use(compression());

app.get('/data', function(req, res) {
    var sql = 'SELECT column_1, column_2 FROM table';
    db.query(sql, function (error, rows, fields) {
        if (error) throw error;
        res.json(rows);
    });
});

app.listen(3000, function () {
  console.log("Running on port 3000.");
});

Используя ApacheBench для запуска 1000 запросов на уровне concurrency 1 (чтобы не помешать однопоточному приложению Node), результат выглядит следующим образом:

Concurrency Level:      1
Time taken for tests:   10.377 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      3057000 bytes
HTML transferred:       2829000 bytes
Requests per second:    96.37 [#/sec] (mean)
Time per request:       10.377 [ms] (mean)
Time per request:       10.377 [ms] (mean, across all concurrent requests)
Transfer rate:          287.69 [Kbytes/sec] received

Для сравнения ниже мой код в PHP:

<?php

    $hostname = <host>;
    $username = <user>;
    $password = <password>;
    $database = <database>;

    try {
        $db_handler = new PDO('mysql:host=' . $hostname . ';dbname=' . $database, $username, $password);
    } catch (PDOException $e) {
        throw new Exception('[ERROR] Unable to connect to the database.');
    }

    $sql = 'SELECT column_1, column_2 FROM table';
    $statement = $db_handler->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    $statement->execute();
    $rows = array();
    while ($row = $statement->fetch(PDO::FETCH_ASSOC)){
        $rows[] = $row;
    }
    print json_encode($rows);

    $db_handler = null;

?>

И результат ApacheBench:

Concurrency Level:      1
Time taken for tests:   6.726 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      3023000 bytes
HTML transferred:       2829000 bytes
Requests per second:    148.68 [#/sec] (mean)
Time per request:       6.726 [ms] (mean)
Time per request:       6.726 [ms] (mean, across all concurrent requests)
Transfer rate:          438.92 [Kbytes/sec] received

Из приведенного выше результата видно, что PHP намного быстрее, чем NodeJS. Рассогласование еще шире, если запущен более сложный запрос (разница может быть в 20 раз, например, 20 мс против 400 мс), или если уровень concurrency увеличен.

Я попытался добавить до 4 рабочих (я запускаю сервер на Raspberry Pi 2, который имеет 4 ядра), в приложение Node и посмотреть, помогает ли он, к сожалению, он все еще не близок к результату в PHP. Не могли бы вы посоветовать, что я мог сделать неправильно? Или NodeJS просто не лучший выбор для того, чего я пытаюсь достичь?

[EDITED]

Большое спасибо за ваши комментарии. Похоже, что большинство людей подозревают, что проблема вызвана драйвером NodeJS MySQL. Я также сделал немного больше тестирования, чтобы убедиться, что это так, и я случайно нашел что-то действительно интересное.

Запустив такое же приложение Node на другом ПК (Core 2 Duo E7200), но подключившись к тому же MySQL на малине Pi, результат на самом деле вполне приличный:

Concurrency Level:      1
Time taken for tests:   2.705 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      3057000 bytes
HTML transferred:       2829000 bytes
Requests per second:    369.71 [#/sec] (mean)
Time per request:       2.705 [ms] (mean)
Time per request:       2.705 [ms] (mean, across all concurrent requests)
Transfer rate:          1103.72 [Kbytes/sec] received

И для сравнения, я также запустил сервер Apache на этом ПК, подключившись к тому же MySQL на малине Pi, а ниже - результат:

Concurrency Level:      1
Time taken for tests:   6.297 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      3034000 bytes
HTML transferred:       2829000 bytes
Requests per second:    158.80 [#/sec] (mean)
Time per request:       6.297 [ms] (mean)
Time per request:       6.297 [ms] (mean, across all concurrent requests)
Transfer rate:          470.50 [Kbytes/sec] received

Как итог, ниже приведен результат, который я получил до сих пор. Только часть веб-сервера отличается, в то время как база данных всегда MySQL на малине Pi:

Server      Time Taken
Node (Pi)   10.337s
PHP (Pi)    6.726s
Node (PC)   2.705s
PHP (PC)    6.297s

Результат для PHP кажется более или менее одинаковым на обоих серверах, в то время как для NodeJS много. Основываясь на приведенном выше результате, мне кажется, что NodeJS более чувствителен к производительности процессора или, другими словами, к интенсивному процессору? (Версия NodeJS, которую я использую, - v6.9.4, только FYI)

Ответ 1

Используя ApacheBench для запуска 1000 запросов на уровне concurrency 1 (чтобы не помешать однопоточному приложению Node)

Ограничивая concurrency на 1, вы фактически убираете Node самое большое преимущество, которое является асинхронным IO. Даже если node.js является однопоточным, он будет обрабатывать другие запросы, ожидая вызова db.query.

Поскольку Node не использует системные потоки для этого, но его собственный легкий планировщик, он может выполнять параллельные запросы намного дешевле Apache. Apache может быть настроен различными способами для обработки нескольких запросов (например, предварительное форматирование фиксированного количества процессов или форсирование, управляемое событиями), но как только у вас будет определенное количество одновременных запросов, все будет замедляться, так как запросы, возможно, придется ждать другие, чтобы завершить, даже если эти другие ничего не делают, кроме ожидания БД.

Итак, в заключение, для синхронного выполнения одного запроса да PHP может быть быстрее; но как только у вас будет достаточно запросов, которые превышают лимит, установленный конфигурацией Apache, и на который влияет также размер вашего компьютера, вы должны видеть, что node.js быстрее в целом.