PHP Red Bean ORM 性能问题

8

我正在用PHP制作一个WebService,我的网站将使用它来查询信息并进行Ajax调用。

起初,我只是用内置的php mysql库以标准方式编写所有查询,并在MySQL Workbench中手动创建整个数据模型等。这非常耗时,如果之后必须更改数据模型,则一切都变得非常复杂,因此我决定寻找一个PHP ORM,我发现了RedBean,它对我来说似乎是纯粹的魔法和快乐。

但我遇到了很多性能问题。我的网站是一个让用户创建自己电视剧列表的网站。我从外部源查询电视剧并将其插入到我的数据库中(如果尚不存在),否则当然我从自己的数据库中获取它们。

我从此外部资源获得的xml列出了电视剧、季节、剧集等信息,我把它们全部存储在这样的形式下。

function InsertSerie($serie) {
    $serieBean = $this->CreateSerieBean($serie->Series);
    $genreBeans = $this->CreateGenreBeans($serie->Series->Genre);
    $actorBeans = $this->CreateActorBeans($serie->Series->Actors);
    $episodeBeans = array();
    foreach ($serie->Episode as $episode) {
        $episodeBean = $this->CreateEpisodeBean($episode);
        $seasonBean = $this->CreateSeasonBean($episode);
        $writerBeans = $this->CreateWriterBeans($episode->Writer);
        $guestBeans = $this->CreateActorBeans($episode->GuestStars);
        $directorBeans = $this->CreateDirectorBeans($episode->Director);
        R::associate($episodeBean, $seasonBean);
        foreach ($writerBeans as $bean) {
            R::associate($episodeBean, $bean);
        }
        foreach ($guestBeans as $bean) {
            R::associate($episodeBean, $bean);
        }
        foreach ($directorBeans as $bean) {
            R::associate($episodeBean, $bean);
        }
        $episodeBeans[] = $episodeBean;
    }
    foreach ($genreBeans as $bean) {
        R::associate($serieBean, $bean);
    }
    foreach ($actorBeans as $bean) {
        R::associate($serieBean, $bean);
    }
    foreach ($episodeBeans as $bean) {
        R::associate($serieBean, $bean);
    }
}

function CreateGenreBeans($genres) {
    if(empty($genres)) { return; }
    $genre = explode("|", $genres);
$genreBeans = array();
foreach ($genre as $g) {
    if($g != '') {
            $genreBeans[] = $this->CreateGenreBean($g);
        }
    }
    return $genreBeans;
}

function CreateGenreBean($genre) {
    $bean = R::dispense('genre');
    $bean->name = (string)$genre;
    return $bean;
}

function CreateDirectorBeans($directors) {
    if(empty($directors)) { return; }
    $director = explode("|", $directors);
$directorBeans = array();
foreach ($director as $d) {
    if($d != '') {
            $directorBeans[] = $this->CreateDirectorBean($d);
        }
    }
    return $directorBeans;
}

function CreateDirectorBean($director) {
    $bean = R::dispense('director');
    $bean->name = (string)$director;
    return $bean;
}

function CreateActorBeans($actors) {
    if(empty($actors)) { return; }
    $actor = explode("|", $actors);
$actorBeans = array();
foreach ($actor as $a) {
    if($a != '') {
            $actorBeans[] = $this->CreateActorBean($a);
        }
    }
    return $actorBeans;
}

function CreateActorBean($actor) {
    $bean = R::dispense('actor');
    $bean->name = (string)$actor;
    return $bean;
}

function CreateWriterBeans($writers) {
    if(empty($writers)) { return; }
    $writer = explode("|", $writers);
$writerBeans = array();
foreach ($writer as $w) {
    if($w != '') {
            $writerBeans[] = $this->CreateWriterBean($w);
        }
    }
    return $writerBeans;
}

function CreateWriterBean($writer) {
    $bean = R::dispense('writer');
    $bean->name = (string)$writer;
    return $bean;
}

function CreateSerieBean($serie) {  
    $bean = R::dispense('serie');
    $bean->serie_id = (string)$serie->id;
    $bean->airs_day_of_week = (string)$serie->Airs_DayOfWeek;
    $bean->airs_time = (string)$serie->Airs_Time;
    $bean->content_rating = (string)$serie->ContentRating;
    $bean->first_aired = (string)$serie->FirstAired;
    $bean->imdb_id = (string)$serie->IMDB_ID;
    $bean->language = (string)$serie->Language;
    $bean->network = (string)$serie->Network;
    $bean->overview = (string)$serie->Overview;
    $bean->rating = (string)$serie->Rating;
    $bean->rating_count = (string)$serie->RatingCount;
    $bean->run_time = (string)$serie->Runtime;
    $bean->serie_name = (string)$serie->SeriesName;
    $bean->status = (string)$serie->Status;
    $bean->last_updated = (string)$serie->lastupdated;
    $bean->thumbnail = (string)$serie->thumbnail;
    return $bean;
}

function CreateSeasonBean($episode) {
    $bean = R::dispense('season');
    $bean->season_id = (string)$episode->seasonid;
    $bean->season_number = (string)$episode->SeasonNumber;
    return $bean;
}

function CreateEpisodeBean($episode) {
    $bean = R::dispense('episode');
    $bean->episode_id = (string)$episode->id;
    $bean->episode_name = (string)$episode->EpisodeName;
    $bean->episode_number = (string)$episode->EpisodeNumber;
    $bean->first_aired = (string)$episode->FirstAired;
    $bean->imdb_id = (string)$episode->IMDB_ID;
    $bean->language = (string)$episode->Language;
    $bean->overview = (string)$episode->Overview;
    $bean->rating = (string)$episode->Rating;
    $bean->rating_count = (string)$episode->RatingCount;
    $bean->last_updated = (string)$episode->lastupdated;
    return $bean;
}

问题在于插入一个系列需要约5分钟时间,而且还会插入重复数据,使用代码 R::freeze(); 也不能提高性能。

问:我该如何解决这个问题?有什么方法可以使redbean表现更好?我该怎样修改自己的代码以使其工作得更好,或者是否应该使用不同的解决方案/框架等?

尝试了像建议的共享列表一样的方法,但结果相同。
function InsertSerie($serie) {
    $serieBean = $this->CreateSerieBean($serie->Series);
    ...
    foreach ($serie->Episode as $episode) {
        $episodeBean = $this->CreateEpisodeBean($serieBean ,$episode);
        ...
        $this->CreateDirectorBeans($serieBean, $episode->Director);
        $serieBean->sharedEpisode[] = $episodeBean;
    }
    R::store($serieBean);
}

function CreateDirectorBeans($bean, $directors) {
    if(empty($directors)) { return; }
    $director = explode("|", $directors);
    foreach ($director as $d) {
        if($d != '') {
            $bean->sharedDirector[] = $this->CreateDirectorBean($d);
        }
    }
}

function CreateDirectorBean($director) {
    $bean = R::dispense('director');
    $bean->name = (string)$director;
    return $bean;
}
    ....

3
解决性能问题的第一步是进行代码剖析。这是因为在你确定问题所在之前,试图去解决它就毫无意义。你可以使用microtime()函数或像xdebug这样的工具来进行剖析。 - goat
我不知道如何进一步缩小问题:/ 我猜我在这里使用redbean的方式不对,而是做了很多交易,而不是一个大批处理或类似的东西? - furier
1
性能分析意味着测量代码执行的某些部分需要多长时间。其目的是识别最慢的部分,通常会有一个特别突出的问题。然后,您可以将精力集中在为您节省最多时间的地方。 - goat
我个人从未使用过Redbean ORM,但我认为每个associate()调用都会执行一个SQL查询。难道没有一种方法可以一次性添加所有关系吗?不过,分析和检查运行的查询是一个好的开始。 - Zombaya
@Zombaya 看起来是这样,我尝试了像建议的共享列表,只使用一个大的 R::store,但结果仍然相同。 :/ - furier
2个回答

7
我终于找到了将执行时间从5分钟降低到约11秒的方法,虽然仍然需要很多时间,但考虑到要处理的数据量和工作量,我认为对于该硬件来说还算不错。
我添加了这些代码行。
R::Begin();
R::associate($bean1, $bean2);
...
R::commit();

现在它会将所有工作都汇总在一个大事务中执行,就像工作单元模式一样。此外,为了防止插入重复数据,我已经切换到使用

$bean = R::findOrDispense($type, $sql, $values);

如果bean已经存在,我只需返回它;如果不存在,我会创建一个新的并返回它。


0

只创建了系列表,并插入了系列数据,没有其他表格... - furier
实际上我犯了一个错误,我太匆忙了,没有读到shared是一个关键字,而且列表的其余部分必须与bean类型匹配。现在它的工作方式就像以前一样,插入重复项,并且需要大约5分钟来插入一个系列。服务器上的CPU和RAM使用率似乎非常低,但我可以听到磁盘像地狱一样运转,但不认为这是问题所在。 - furier
我读到了关于工作单元的一些内容,它是一个容器,可以容纳所有的工作,并在一个事务中执行所有的SQL操作。 http://redbeanphp.com/community/wiki/index.php/Unit_of_Work 但是我找不到任何代码示例,如何获取一个$uow对象,以便我可以添加addWork();。 - furier

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