我目前在 Apache + PHP + MySQL 下运行一个小型 Web 服务器,并且想探索使用 NodeJS 的选项。服务器实际上做了两件事:
但是,我遇到了一些性能问题,我正在尝试找出问题所在。为了隔离问题,我创建了一个最小的 NodeJS 应用程序,它对 MySQL 运行查询并以 JSON 形式返回 50 行数据。下面是我的代码:
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 在并发级别 1 下触发 1000 个请求(为了不损害单线程 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 毫秒),或者如果并发级别增加,则差异会更大。
我尝试向 Node 应用程序添加最多 4 个工作线程(我在有 4 个核心的 Raspberry Pi 2 上运行服务器),看看它是否有帮助,不幸的是它仍然与 PHP 中的结果不接近。你能告诉我我可能做错了什么吗?或者 NodeJS 对于我想要实现的目标来说并不是一个好的选择?
[已编辑]
非常感谢您的所有评论。似乎大多数人怀疑问题是由 NodeJS MySQL 驱动程序引起的。我还做了更多测试来确定情况是否如此,并且我意外地发现了一些非常有趣的东西。
通过在另一台 PC(Core 2 Duo E7200)上运行相同的 Node 应用程序,但在 Raspberry Pi 上连接到相同的 MySQL,结果实际上相当不错:
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
作为比较,我还在该 PC 上运行了 Apache 服务器,连接到 Raspberry Pi 上的相同 MySQL,结果如下:
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
作为总结,以下是我迄今为止得到的结果。只有 Web 服务器部分不同,而数据库始终是 Raspberry Pi 上的 MySQL:
Server Time Taken
Node (Pi) 10.337s
PHP (Pi) 6.726s
Node (PC) 2.705s
PHP (PC) 6.297s
PHP 的结果在两台服务器上似乎或多或少相同,而 NodeJS 的结果差异很大。根据上面的结果,在我看来 NodeJS 对 CPU 性能更敏感,或者换句话说 CPU 密集型? (我使用的NodeJS版本是v6.9.4,仅供参考)
使用 ApacheBench 在并发级别 1 下触发 1000 个请求(为了不损害单线程 Node 应用程序)
通过将并发限制为 1,您实际上剥夺了节点最大的优势,即异步 IO。即使node.js是单线程的,它也会在等待db.query调用时处理其他请求。
因为 Node 不使用系统线程,而是使用自己的轻量级调度程序,所以它可以比 Apache 更便宜地执行并发请求。 Apache 可以通过不同的方式配置来处理多个请求(例如,预分叉固定数量的进程或事件驱动的分叉),但是一旦并发请求达到一定数量,事情就会变慢,因为请求可能需要等待其他人要完成,即使其他人除了等待数据库什么也没做。
因此,总而言之,对于同步执行单个请求,PHP 可能更快;但是,一旦您有足够的请求,超出了 Apache 配置设置的限制,并且还受到计算机大小的影响,您应该会看到 Node.js 总体上更快。
我尝试做一些类似的测试。
ab -n 10000 -c 10 http://localhost:8080/
或
ab -n 10000 -c 100 http://localhost:8080/
节点可能会因错误而崩溃
"Too many connections"
如果修改代码在查询后添加con.end()
节点可能会因错误而崩溃 "PROTOCOL_SEQUENCE_TIMEOUT"
可能需要修改代码并进行更多测试。
P/S:php 没有错误。
ab -n 10000 -c 10000 http://localhost/Php03.php