将许多MySQL查询与逻辑组合到数​​据文件中

时间:2009-12-22 16:23:31

标签: php mysql optimization query-optimization

背景:

我正在使用控制台上的PHP脚本将330 meg xml文件解析为DB(netflix目录)。

我可以每3秒成功添加约1,500个标题直到我添加了添加演员,流派和格式的逻辑。这些是由关联表链接的单独表。

现在我必须按此顺序为每个标题运行许多查询(我首先截断所有表格,以消除旧标题,流派等)

  1. 为“titles”添加新标题并捕获插入ID
  2. 查看演员表以获取演员
  3. 如果存在,获取id,如果不存在,则插入 演员并获得插入ID
  4. 将标题ID和actor ID插入 关联表
  5. (对于类型也重复步骤2-4)

    这使我的速度降低到每3秒10左右。这将需要添加约250,00个标题。

    所以如何将4个查询合并为一个查询,而不添加重复的演员或类型

    我的目标是将所有查询都写入数据文件,并进行批量插入。

    我首先将所有关联查询写入数据文件,但它对性能没有太大作用。


    我首先插入etitle并保存ID

    function insertTitle($nfid, $title, $year){
        $query="INSERT INTO ".$this->titles_table." (nf_id, title, year ) VALUES ('$nfid','$title','$year')";
        mysql_query($query);
        $this->updatedTitleCount++;
        return mysql_insert_id();
    }
    

    然后与每个actor的名称一起使用以创建关联

    function linkActor($value, $title_id){
        //check if we already know value
        $query="SELECT * FROM ".$this->persons_table." WHERE person = '$value' LIMIT 0,1";
        //echo "<br>".$query."<br>";
        $result=mysql_query($query);
        if($result && mysql_num_rows($result) != 0){
            while ($row = mysql_fetch_assoc($result)) {
                $value_id=$row['id'];
            }
        }else{
            //no value known, add to persons table
            $query="INSERT INTO ".$this->persons_table." (person) VALUES ('$value')";
            mysql_query($query);
            $value_id=mysql_insert_id();
    
        }   
        //echo "linking title:".$title_id." with rel:".$value_id;
        $query = " INSERT INTO ".$this->title_persons_table." (title_id,person_id) VALUE ('$title_id','$value_id');";
        //mysql_query($query);
        //write query to data file to be read in bulk style
        fwrite($this->fh, $query);
    }
    

3 个答案:

答案 0 :(得分:1)

你的表现非常缓慢;事情是非常错误的。我假设以下

  • 您可以在尊重的硬件上运行专用的,空闲的数据库服务器
  • 您已在某种程度上对其进行了调整(即至少将其配置为正确使用一些ram) - 将需要特定于引擎的优化

你可能会因为使用自动提交进行大量微小的操作而受到刺激;这是一个错误,因为它会产生不合理数量的光盘IO操作。你应该在一个事务中做大量的工作(100,1000条记录等)然后提交它。

查询可能会减慢速度,因为执行查询的开销很简单(查询本身非常简单,因为您将拥有对actor名称的索引)。

我还质疑你假设没有两个演员有相同名字的方法 - 你的原始数据库肯定包含一个独特的演员ID,所以你不要混淆它们吗?

答案 1 :(得分:1)

这是使用prepared statements的绝佳机会 另请查看http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html上的提示,例如

要加快使用非事务表的多个语句执行的INSERT操作,请锁定表

您还可以减少查询次数。例如。您可以使用SELECT...FROM persons_tableLAST_INSERT_ID(expr)来消除INSERT...ON DUPLICATE KEY UPDATE以获取ID。

(对不起,没时间进行冗长的描述,但是在注意到时间之前我写了一个例子;-)如果这个答案没有过多投票,我可以在以后交出来。 )

class Foo {
  protected $persons_table='personsTemp';
  protected $pdo;
  protected $stmts = array();

  public function __construct($pdo) {
    $this->pdo = $pdo;
    $this->stmts['InsertPersons'] = $pdo->prepare('
      INSERT INTO
        '.$this->persons_table.'
        (person)
      VALUES
        (:person)
      ON DUPLICATE KEY UPDATE
        id=LAST_INSERT_ID(id)
    ');
  }

  public function getActorId($name) {
    $this->stmts['InsertPersons']->execute(array(':person'=>$name));
    return $this->pdo->lastInsertId('id');
  }
}

$pdo = new PDO("mysql:host=localhost;dbname=test", 'localonly', 'localonly'); 
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// create a temporary/test table
$pdo->exec('CREATE TEMPORARY TABLE personsTemp (id int auto_increment, person varchar(32), primary key(id), unique key idxPerson(person))');
// and fill in some data
foreach(range('A', 'D') as $p) {
  $pdo->exec("INSERT INTO personsTemp (person) VALUES ('Person $p')");
}

$foo = new Foo($pdo);
foreach( array('Person A', 'Person C', 'Person Z', 'Person B', 'Person Y', 'Person A', 'Person Z', 'Person A') as $name) {
  echo $name, ' -> ', $foo->getActorId($name), "\n";
}

打印

Person A -> 1
Person C -> 3
Person Z -> 5
Person B -> 2
Person Y -> 6
Person A -> 1
Person Z -> 5
Person A -> 1

(有人可能想开始讨论getXYZ()函数是否应该执行INSERT ......但不是我,现在不是....)

答案 2 :(得分:0)

你能使用PHP以外的语言吗?如果没有,您是将其作为PHP独立脚本运行还是通过Web服务器运行?网络服务器可能会增加你不需要的大量开销。

我在工作中使用Python做了类似的事情,并且可以在标准的3.4 GHz,3GB RAM机器上每秒插入几千行(带有关联表查找)。 MySQL数据库不是在本地托管,而是在局域网内托管。