3

测试更新为更具可读性;所有这些都在 100 倍的 foreach 循环中完成。

测试查询是SELECT * FROM school_courses;

任何人都可以提供“跳出框框思考”的反馈:

a) 为什么 PHP ActiveRecord ORM 需要 4 秒才能根据以下结果执行相同的查询?

b) 这是比较查询方法的实际基准还是更多的假设基准?

c)是否有其他方法(测试用例)我应该尝试(或修改这些方法)以获得更清晰的画面?

结果(使用 PDO 和 MySQLi)

Iterations: 100

PHP (config file)
Base Time: 5.793571472168E-5
Gross Time: 0.055607080459595
Net Time: 0.055549144744873

PHP ActiveRecord ORM
Base Time: 5.2213668823242E-5
Gross Time: 4.1013090610504
Net Time: 4.1012568473816

MySQL (standard)
Base Time: 5.1975250244141E-5
Gross Time: 0.32771301269531
Net Time: 0.32766103744507

CodeIgniter (Active Record)
Base Time: 5.1975250244141E-5
Gross Time: 0.28282189369202
Net Time: 0.28276991844177

MySQLi
Base Time: 5.1975250244141E-5
Gross Time: 0.20240592956543
Net Time: 0.20235395431519

PDO
Base Time: 5.2928924560547E-5
Gross Time: 0.17662906646729
Net Time: 0.17657613754272

测试

// Benchmark tests
$runs = 100;

// PHP (config file)
for ($i = 0; $i < $runs; $i++) {
    $this->view_data['courses'] = course_info();
}

// PHP ActiveRecord ORM
for ($i = 0; $i < $runs; $i++) {
    $this->view_data['courses'] = Course::all();
}

// mysql_* (MySQL standard; deprecated)
for ($i = 0; $i < $runs; $i++) {
    $sql = mysql_query('SELECT * FROM school_courses') or die(mysql_error());
    while ($row = mysql_fetch_object($sql)) {
        array_push($this->view_data['courses'], $row);
    }
}

// CodeIgniter (Active Record)
for ($i = 0; $i < $runs; $i++) {
    $this->view_data['courses'] = $this->db->get('school_courses');
}

// mysqli_* (MySQLi)
for ($i = 0; $i < $runs; $i++) {
    $res = $mysqli->query('SELECT * FROM school_courses');
    while ($row = $res->fetch_object()) {
        array_push($this->view_data['courses'], $row);
    }
}

// PDO
for ($i = 0; $i < $runs; $i++) {
    foreach($conn->query('SELECT * FROM school_courses') as $row) {
        array_push($this->view_data['courses'], $row);
    }
}
4

3 回答 3

2

因此,PHP ActiveRecord ORM 在对并发连接进行基准测试时引入如此多开销的原因是因为返回的每个结果都会实例化一个新的模型对象。这是使用这个 ORM 库不可或缺的一部分,我看不出有任何合理的方法可以在不彻底检查整个库的情况下进行更改。

这是我发现的:

在 Table 类的 find_by_sql() 方法中,您有:

    $sth = $this->conn->query($sql,$this->process_data($values));

    while (($row = $sth->fetch()))
    {
        $model = new $this->class->name($row,false,true,false);

        if ($readonly)
            $model->readonly();

        if ($collect_attrs_for_includes)
            $attrs[] = $model->attributes();

        $list[] = $model;
    }

具体来说,动态模型实例化new $this->class->name()负责开销,比方说,每个获取的结果的权重约为 0.004。

你把它乘以现在的记录数,(10 条记录 = 0.04)。现在将其乘以并发连接数,假设为 100,您就会遇到可预见的瓶颈问题。

100 个用户(假设)同时访问包含 10 条记录的表需要四 (4) 秒。

此时我是否应该担心由于该库为每条记录实例化模型类的方式,正在获取的记录数量可能会导致瓶颈问题?

同样,在这一点上,这一切都可能是假设的言论,假设正确使用 ORM,在现实世界中可能永远不会存在或提出问题。除非这些测试或结论不准确,否则我在这里试图模拟的是 100、1,000 和 10,000 名活跃现场访问者的流量负载。

换句话说,如果我不添加其他课程(限制 10),例如,浏览课程页面的 10,000 名访问者是否会导致其他人离开页面需要 400 秒(6.67 分钟)的等待时间?如果是这样的话,那么我会找到我自己的答案(因此这篇文章),并且会考虑寻找另一个 ORM 或根据具体情况进行重构。

这是基准测试和模拟流量负载的最合适方法吗?

其他资源

如何使用 ab 工具进行 Apache 压力测试 https://wiki.appnexus.com/display/documentation/How+to+Apache+Stress+Test+With+ab+Tool

于 2012-11-13T20:52:30.530 回答
1

重写建议:

我不想听起来很残酷,但如果你忘记了你所知道的关于 mysql_() 的一切,你可以在未来省去很多麻烦,并跟上当前的做法。以今天的标准,老实说,它是垃圾。查看 mysqli_ 或 PDO 作为您的数据库接口。

mysqli_:http ://us2.php.net/manual/en/book.mysqli.php

PDO:http ://us2.php.net/manual/en/book.pdo.php

然后报告基准...

于 2012-11-13T18:23:16.237 回答
0

您的简单查询并不是真正的公平测试。对于这样的简单查询,ORM 很好并且相当有竞争力。ORM 在其上创建低效查询是更复杂的查询(即 LEFT JOIN),您最终不得不绕过它们。ORM 总是比了解 SQL 的人编写的原始 SQL 慢。当然,了解 SQL 是关键。

如果你正在考虑 ORM,你真的应该试试 Doctrine。我(完全)不是 ORM 的粉丝,但那是最流行的 PHP ORM。

批量插入也是一些 ORM 和 DB 抽象层出错的另一个领域。他们没有认识到可以使用批量插入,而是在循环中进行单次插入。除了速度慢之外,这还会导致 MyISAM 上的表锁定问题。也许添加一个批量插入测试,如果可能的话让每个数据库层生成插入查询。

您的测试方法所揭示的是,经过多次迭代,每种数据库访问方法的开销都会增加。我建议完全消除查询开销,而只使用“SELECT VERSION()”。

于 2012-11-14T00:14:50.867 回答