SQL基准测试:PHP ActiveRecord ORM vs. MySQL vs. CodeIgniter Active Record vs.标准PHP。

3

测试结果更新,使其更易读;全部在一个100x的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);
    }
}

你是如何在PHP配置文件中放置SQL命令的? - billmalarky
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

1

感谢您添加PDO,有趣的是它是最快的库,而ActiveRecords在您的测试中表现不佳。附带问题:为什么要使用array_push向对象添加属性?“array_push($this->view_data['courses'], $row);”我并不认为这会对您的问题产生影响,只是想知道为什么不使用$this->view_data['courses'][$row['course_name']] = $row; - David J Eddy
我还没有时间深入研究库代码,找出其中的原因。由于各种原因,我现在非常担心使用哪些ORM,并且正在考虑编写自己的定制数据库库,以便更好地调整这些查询和操作。我知道一些公司像谷歌一样为其特定目的提供定制解决方案。这取决于查询类型和每个查询的用途,需要判断优化和重构的程度。 - Matt Borja
分配键可能会更快,也可能不会更快,例如:$this->view_data['courses'][$row->id] = $row;与array_push($this->view_data['courses'], $row);实际上我使用前一种方法,但后一种方法只是在组合此示例时首先想到的。 - Matt Borja
我正在进行一些库的分析,希望很快能得出结果,找出是什么导致了PHP ActiveRecord ORM的问题。到目前为止,我已经将其缩小到一个单独的static::table()->find($options)静态函数,它独自占用了4秒钟的时间。 - Matt Borja
刚刚意识到一件事。ActiveRecord 是为框架而设计的,在这些用例中,实例化的对象和属性都是一次性填充的吗?因此只需要一个查询时间段。+在第一轮之后,CI 是否进行了缓存处理?请原谅我对 CI 逻辑的无知,我以前没有使用过它(更喜欢 CakePHP)。 - David J Eddy
显示剩余3条评论

0

你的简单查询并不是一个公平的测试。ORM对于像这样的简单查询是很好的,而且相当有竞争力。但是在更复杂的查询(例如LEFT JOIN)上,ORM会创建低效的查询,你最终不得不绕过它们。ORM将始终比由了解SQL的人编写的原始SQL慢。当然,了解SQL是关键。

如果你正在考虑使用ORM,你真的应该尝试一下Doctrine。我不是ORM的粉丝(根本不是),但那是目前最流行的PHP ORM。

批量插入也是另一个领域,一些ORM和DB抽象层会出现问题。他们会在循环中执行单个插入,而不是识别可以使用批量插入。这将导致MyISAM上的表锁定问题,除了速度慢之外。也许可以添加一个批量插入测试,让每个DB层在可能的情况下生成插入查询。

你的测试方法所揭示的是,在许多迭代中,每种DB访问方法的开销都会累积。我建议完全消除查询开销,只使用“SELECT VERSION()”。


1
我使用ORM,并且一直喜欢独立的PHP ActiveRecord ORM库,不像DataMapper曾经那样与任何框架绑定(有被破坏的风险)。我不知道其他ORM是否以同样的方式展示了这个问题,但是我对我在其中一个控制器中使用此ORM的简单查询感到好奇,经过一些快速测试,根据我的自我回答,发现该库获取记录的方式存在问题。我不介意毫秒级别的差异,但是这比所有其他测试长了整整4秒,所以显然有问题:模型实例化。 - Matt Borja

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接