Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJS much slower than PHP?

I run a tiny scale web server under Apache + PHP + MySQL at the moment and would like to explore the option of using NodeJS instead. The server literally does two things:

  1. Serve some static files (HTML/CSS/image resources etc)
  2. Query the database (select and insert only, no update or delete)

However, I'm running into some performance issue and I'm trying to figure out where the problem is. To isolate the problem, I've created a minimal NodeJS app, which runs a query against MySQL and return 50 rows of data as JSON. Below is my code:

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.");
});

By using ApacheBench to fire 1000 requests at a concurrency level of 1 (in order not to disadvantage the single-threaded Node app), the result is as follows:

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

As a comparison, below is my code in 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;

?>

And the result from 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

From the above result, it is observed that PHP is much faster than NodeJS. The discrepancy is even wider if a more complex query is fired (the difference could be 20 times, like 20ms vs 400ms), or if the concurrency level is increased.

I've tried to add up to 4 workers (I'm running the server on Raspberry Pi 2 which has 4 cores) to the Node app and see if it helps, unfortunately it is still not close to the result in PHP. Could you please advise what I might have done wrong? Or NodeJS is just not a good pick for what I'm trying to achieve?

[EDITED]

Thanks a lot for all your comments. Seems like most people suspect that the problem is caused by the NodeJS MySQL driver. I've also done a bit more testing to make sure if that's the case, and I accidentally found something really interesting.

By running the same Node app in another PC (Core 2 Duo E7200), but connecting to the same MySQL on Raspberry Pi, the result is in fact quite decent:

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

And as a comparison, I've also run an Apache server on that PC, connecting to the same MySQL on Raspberry Pi, and below is the result:

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

As a summary, below is the result I've gotten so far. Only the web server part is different, while the database is always MySQL on Raspberry Pi:

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

The result for PHP appears to be more or less the same on both server, while that of NodeJS varies a lot. Based on the result above, it appears to me that NodeJS is more sensitive to CPU performance, or in another words CPU intensive? (The NodeJS version I'm using is v6.9.4, just FYI)

like image 276
CLDev Avatar asked Jan 30 '17 04:01

CLDev


1 Answers

By using ApacheBench to fire 1000 requests at a concurrency level of 1 (in order not to disadvantage the single-threaded Node app)

By limiting concurrency to 1, you're actually taking away node's biggest advantage, which is asynchronous IO. Even though node.js is single-threaded, it will handle other requests while waiting on the db.query call.

Because node is not using system threads for this, but its own lightweight scheduler, it can execute concurrent requests much cheaper than Apache. Apache can be configured in different ways to handle multiple requests (e.g. pre-forking a fixed number of processes or event-driven forking), but once you have a certain number of concurrent requests, things will slow down as requests might have to wait for others to complete, even if those others are doing nothing but waiting for the DB.

So, to conclude, for synchronously executing a single request, yes PHP might be faster; but once you have enough requests which exceed the limit set by Apache's configuration and which is affected also by the size of your machine, you should see node.js being faster overall.

like image 81
Bastian Blankenburg Avatar answered Sep 21 '22 21:09

Bastian Blankenburg