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

SQL基准:PHP ActiveRecord ORM vs.MySQL vs.CodeIgniter Active Record vs.标准PHP,php,mysql,sql,performance,benchmarking,Php,Mysql,Sql,Performance,Benchmarking,更新测试以提高可读性;所有这些都是在一个100倍的foreach循环中完成的 测试查询为SELECT*FROM school\u courses 是否有人能提供“开箱思考”的反馈: a) 为什么PHP ActiveRecord ORM需要4秒钟才能根据以下结果执行相同的查询 b) 这是一个实际的基准,还是一个比较查询方法的假设基准 c) 是否有其他方法(测试用例)我应该尝试(或修改这些方法)以获得更清晰的图像 结果(使用PDO和MySQLi) 测试 // Benchmark tests $run

更新测试以提高可读性;所有这些都是在一个100倍的foreach循环中完成的

测试查询为SELECT*FROM school\u courses

是否有人能提供“开箱思考”的反馈:

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

b) 这是一个实际的基准,还是一个比较查询方法的假设基准

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

结果(使用PDO和MySQLi)

测试

// 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);
    }
}
//基准测试
$runs=100;
//PHP(配置文件)
对于($i=0;$i<$runs;$i++){
$this->查看课程数据['courses']=课程信息();
}
//PHP ActiveRecord ORM
对于($i=0;$i<$runs;$i++){
$this->view_data['courses']=Course::all();
}
//mysql(mysql标准;已弃用)
对于($i=0;$i<$runs;$i++){
$sql=mysql\u query('SELECT*fromschool\u courses')或die(mysql\u error());
while($row=mysql\u fetch\u object($sql)){
数组推送($this->view_data['courses',$row);
}
}
//代码点火器(活动记录)
对于($i=0;$i<$runs;$i++){
$this->view_data['courses']=$this->db->get('school_courses');
}
//mysqli_u*(mysqli)
对于($i=0;$i<$runs;$i++){
$res=$mysqli->query('SELECT*fromschool_courses');
而($row=$res->fetch_object()){
数组推送($this->view_data['courses',$row);
}
}
//PDO
对于($i=0;$i<$runs;$i++){
foreach($conn->query('SELECT*FROM school_courses')作为$row){
数组推送($this->view_data['courses',$row);
}
}

重写建议:

我不想听起来很残忍,但是如果你忘记了你所知道的关于mysql的一切,你可以在将来省去很多头疼的事情,也可以跟上当前的做法。按照今天的标准,这是垃圾。将mysqli_uu或PDO作为数据库接口

mysqli_2;

PDO:


然后报告基准测试…

因此,当基准测试并发连接时,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,您就有了一个可预见的瓶颈问题

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

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

同样,这一切都可能是假设性的演讲,在假设正确使用ORM的情况下,在现实世界中可能永远不会存在或出现问题。除非这些测试或结论不准确,否则我在这里试图模拟的是100、1000和10000名活跃的现场访客的流量

换句话说,如果我不添加其他课程(限制为10),例如,10000名浏览课程页面的访问者是否会导致400秒(6.67分钟)等待其他人离开页面?如果是这样的话,那么我会找到我自己的答案(因此这篇文章),并会寻找另一个ORM或在个案基础上进行重构

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

其他资源

// 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);
    }
}
如何使用ab工具进行Apache压力测试
您的简单查询并不是一个真正公平的测试。ORM很好,对于类似这样的简单查询非常有竞争力。更复杂的是,ORM会在其上创建低效的查询(即左连接),最终不得不绕过它们。ORMs总是比熟悉SQL的人编写的原始SQL慢。当然,了解SQL是关键

如果你正在考虑使用ORMs,你真的应该尝试使用ORMs。我对ORM不感兴趣,但这是最流行的PHP ORM

批量插入也是一些ORMs和DB抽象层出现问题的另一个领域。他们没有意识到可以使用批量插入,而是在循环中执行单个插入。这将导致MyISAM上的表锁定问题,而且速度很慢。也许可以添加一个批量插入测试,如果可能的话,让每个DB层生成插入查询


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

我添加了这两个接口,结果位于顶部,实际测试本身位于原始测试系列的下方。这就是你要找的吗?此外,我还知道PHP ActiveRecord ORM使用PDO库。感谢您添加PDO,有趣的是,它是迄今为止最快的库,ActiveRecords正在吸引您的测试。附带问题:为什么要按array\u push向对象添加属性?“array_push($this->view_data['courses'],$row);”我并不认为这会对您的问题产生影响,只是想知道为什么不$this->view_data['courses'][$row['courseu name']=$row;我还没有时间深入研究库代码以找出原因。出于各种原因,我对我现在使用的ORM感到非常担忧