我怎样才能更快地完成这项工作?

时间:2010-11-01 18:35:00

标签: php mysql performance

我有一个导入CSV文件的脚本。最终在我的数据库中的是客户列表和地址列表。我有一个名为customer的表和另一个名为address的表,其中address有一个customer_id

对我来说重要的一件事就是不要有任何重复的行。因此,每次导入地址时,我都会这样做:

$address = new Address();
$address->setLine_1($line_1);
$address->setZip($zip);
$address->setCountry($usa);
$address->setCity($city);
$address->setState($state);
$address = Doctrine::getTable('Address')->findOrCreate($address);
$address->save();

正如您可能猜到的那样,findOrCreate()找到匹配的地址记录(如果存在),否则只返回一个新的Address对象。这是代码:

  public function findOrCreate($address)
  {
    $q = Doctrine_Query::create()
      ->select('a.*')
      ->from('Address a')
      ->where('a.line_1 = ?', $address->getLine_1())
      ->andWhere('a.line_2 = ?', $address->getLine_2())
      ->andWhere('a.country_id = ?', $address->getCountryId())
      ->andWhere('a.city = ?', $address->getCity())
      ->andWhere('a.state_id = ?', $address->getStateId())
      ->andWhere('a.zip = ?', $address->getZip());

    $existing_address = $q->fetchOne();

    if ($existing_address)
    {
      return $existing_address;
    }
    else
    {
      return $address;
    }
  }

这样做的问题是它很慢。要保存CSV文件中的每一行(在不同的表中转换为多个INSERT语句),大约需要四分之一秒。我希望尽可能接近“瞬时”,因为我的CSV文件中有时会有超过50,000行。我发现,如果我注释掉导入中保存地址的部分,那就快得多了。有没有更快的方法可以做到这一点?我简单地考虑过对其进行索引,但似乎是因为所有字段都需要匹配,所以索引无济于事。

4 个答案:

答案 0 :(得分:1)

我建议您调查使用LOAD DATA INFILE

将CSV文件加载到MySQL中

http://dev.mysql.com/doc/refman/5.1/en/load-data.html

为了更新现有行,您有几个选项。 LOAD DATA INFILE没有upsert功能(在重复键更新时插入...),但它有一个REPLACE选项,您可以使用它来更新现有行,但是您需要确保拥有一个合适的唯一索引,而REPLACE实际上只是DELETEINSERT,它比UPDATE慢。

另一种选择是将CSV中的数据加载到临时表中,然后使用INSERT...ON DUPLICATE KEY UPDAT E将该表与实时表合并。同样,请确保您有一个合适的唯一索引,但在这种情况下,您正在进行更新而不是删除,因此它应该更快。

答案 1 :(得分:1)

这肯定不会减少花费在数万次迭代上的所有时间,但为什么不在每次迭代数据库查询之外管理你的地址呢?总体思路:

  1. 获取所有当前地址的列表(将其存储在数组中)
  2. 在迭代时,检查数组成员资格(checksums [sic]);如果它不存在,请将新地址存储在数组中并将地址保存到数据库中。
  3. 除非我误解了这个场景,否则你只需要进行INSERT查询,除了第一个查询之外你不需要执行任何SELECT查询。

答案 2 :(得分:0)

看起来你的重复检查会减慢你的速度。要找出原因,请找出Doctrine正在创建的查询,并在其上运行EXPLAIN

我的猜测是你需要创建一些索引。搜索整个表可能会非常慢,但是为zip添加索引将允许查询仅通过具有该邮政编码的地址进行完整搜索。 EXPLAIN将能够引导您进行其他优化。

答案 3 :(得分:0)

我最终做的是,大大提高了效果,就是使用ON DUPLICATE KEY UPDATE而不是findOrCreate()