【问题标题】:PHP Red Bean ORM Performance issuePHP Red Bean ORM 性能问题
【发布时间】:2012-05-27 08:41:06
【问题描述】:

我正在 php 中创建一个 WebService,我的网站将使用它来通过 Ajax 调用查询信息。

起初我只是用内置的 php mysql lib 以标准方式完成,然后手动编写所有查询并在 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;
}
    ....

【问题讨论】:

  • 解决性能问题从分析开始。这是因为在您确定问题所在之前尝试解决问题是没有意义的。您可以使用 microtime() 或 xdebug 之类的工具。
  • 我不知道如何进一步缩小问题范围:/ 我想我在这里使用 redbean 的方式是错误的,而不是大批量或类似的交易?跨度>
  • 分析意味着测量代码的某些部分需要多长时间才能执行。目的是识别最慢的部分,因为通常会有一件事特别突出。然后,您可以将精力集中在获得最大回报的地方。
  • 我个人从未使用过redbean ORM,但在我看来,每个associate()-call 都会执行一个sql-query。有没有办法一次添加所有关系?不过,分析和检查正在运行的查询是一个很好的开始方式。
  • @Zombaya 似乎是这样,我尝试了共享列表,例如仅使用一个大 R::store 建议的共享列表,但结果相同。 ://

标签: php performance orm redbean


【解决方案1】:

您是否尝试过使用共享列表而不是循环访问关系?

$serieBean->sharedActor[] = $actorBeans;
$serieBean->sharedEpisode[] = $episodeBeans;

http://www.redbeanphp.com/manual/shared_lists

【讨论】:

  • 只创建了意甲表,只插入意甲数据,没有其他表...
  • 实际上我犯了一个错误,我很快并没有读到 shared 是一个关键字,并且列表的其余部分必须与 bean 类型匹配。现在它就像以前一样工作,插入重复项,插入一个系列大约需要 5 分钟。服务器上的 cpu 和 ram 使用率似乎很低,但我可以听到磁盘工作得像地狱一样,但不要认为这是这里的问题
  • 我读过一些关于工作单元的东西,它是你所有工作的容器,它在一个事务中完成所有的 sql。 redbeanphp.com/community/wiki/index.php/Unit_of_Work 但是我找不到任何代码示例来说明如何获取我可以 addWork(); 的 $uow 对象;太……
【解决方案2】:

我终于找到了如何将执行时间从 5 分钟降低到大约 11 秒,仍然需要分配时间,但考虑到数据量和它必须做的工作,我认为它对硬件有好处。

我添加了这些代码行

R::Begin();
R::associate($bean1, $bean2);
...
R::commit();

现在它收集所有工作并在一个大事务中执行它,就像工作单元模式一样。另外为了防止插入重复我切换到使用

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

然后,如果 bean 已经存在,我就返回它,否则我创建一个新的并返回它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-11
    • 2017-07-14
    • 2016-09-05
    • 1970-01-01
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多